From: Alexander Moisseev Date: Sun, 5 Jul 2026 12:12:52 +0000 (+0300) Subject: [Rework] WebUI: remove jQuery from leaf modules X-Git-Tag: 4.1.3~52^2~12 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1e8f7d1b566929855e5d72cd2d59ef21de79a604;p=thirdparty%2Frspamd.git [Rework] WebUI: remove jQuery from leaf modules Migrate graph.js, selectors.js, history.js, and symbols.js off jQuery: $.extend/$.each/$.trim and $().val/prop/attr/hasClass/is/ addClass/removeClass/change/click/on to native DOM and the shared common.el / common.delegate helpers added in the common.js stage. Delegated handlers on Tabulator-rendered DOM (.scorebar) move to common.delegate; the repeated button disable/enable patterns collapse into small per-module helpers. jQuery remains loaded for the remaining modules (config, libft, stats, upload, rspamd) — removed in later stages. --- diff --git a/interface/js/app/graph.js b/interface/js/app/graph.js index e996548156..c84fa9171a 100644 --- a/interface/js/app/graph.js +++ b/interface/js/app/graph.js @@ -3,9 +3,8 @@ * Copyright (C) 2017 Alexander Moisseev */ -define(["jquery", "app/common", "app/tab-utils", "d3evolution", "d3pie", "d3", "tabulator"], - // eslint-disable-next-line max-params -- AMD factory: each dep needs a param - ($, common, tabUtils, D3Evolution, D3Pie, d3, Tabulator) => { +define(["app/common", "app/tab-utils", "d3evolution", "d3pie", "d3", "tabulator"], + (common, tabUtils, D3Evolution, D3Pie, d3, Tabulator) => { "use strict"; const rrd_pie_config = { @@ -59,23 +58,24 @@ define(["jquery", "app/common", "app/tab-utils", "d3evolution", "d3pie", "d3", " }; function initGraph() { - const graph = new D3Evolution("graph", $.extend({}, graph_options, { + const graph = new D3Evolution("graph", { + ...graph_options, yScale: common.getSelector("selYScale"), type: common.getSelector("selType"), interpolate: common.getSelector("selInterpolate"), convert: common.getSelector("selConvert"), - })); - $("#selYScale").change(function () { - graph.yScale(this.value); }); - $("#selConvert").change(function () { - graph.convert(this.value); + document.getElementById("selYScale").addEventListener("change", (e) => { + graph.yScale(e.currentTarget.value); }); - $("#selType").change(function () { - graph.type(this.value); + document.getElementById("selConvert").addEventListener("change", (e) => { + graph.convert(e.currentTarget.value); }); - $("#selInterpolate").change(function () { - graph.interpolate(this.value); + document.getElementById("selType").addEventListener("change", (e) => { + graph.type(e.currentTarget.value); + }); + document.getElementById("selInterpolate").addEventListener("change", (e) => { + graph.interpolate(e.currentTarget.value); }); return graph; @@ -177,7 +177,9 @@ define(["jquery", "app/common", "app/tab-utils", "d3evolution", "d3pie", "d3", " graphs.graph.data(data); if (unit !== prevUnit) { graphs.graph.yAxisLabel("Message rate, " + unit); - $(".unit").text(unit); + document.querySelectorAll(".unit").forEach((el) => { + el.textContent = unit; + }); prevUnit = unit; } drawRrdTable(rrd_summary.rows, unit); @@ -225,7 +227,11 @@ define(["jquery", "app/common", "app/tab-utils", "d3evolution", "d3pie", "d3", " } updateWidgets(data); }, - complete: function () { $("#refresh").removeAttr("disabled").removeClass("disabled"); }, + complete: function () { + const refreshBtn = document.getElementById("refresh"); + refreshBtn.disabled = false; + refreshBtn.classList.remove("disabled"); + }, errorMessage: "Cannot receive throughput data", errorOnceId: "alerted_graph_", data: {type: type} @@ -234,13 +240,17 @@ define(["jquery", "app/common", "app/tab-utils", "d3evolution", "d3pie", "d3", " // Handling mouse events on overlapping elements - $("#rrd-pie").mouseover(() => { - $("#rrd-pie,#rrd-pie-tooltip").css("z-index", "200"); - $("#rrd-table_toggle").css("z-index", "300"); + function setZIndex(selector, value) { + document.querySelectorAll(selector).forEach((el) => { + el.style.zIndex = value; + }); + } + document.getElementById("rrd-pie").addEventListener("mouseover", () => { + setZIndex("#rrd-pie, #rrd-pie-tooltip", "200"); + setZIndex("#rrd-table_toggle", "300"); }); - $("#rrd-table_toggle").mouseover(() => { - $("#rrd-pie,#rrd-pie-tooltip").css("z-index", "0"); - $("#rrd-table_toggle").css("z-index", "0"); + document.getElementById("rrd-table_toggle").addEventListener("mouseover", () => { + setZIndex("#rrd-pie, #rrd-pie-tooltip, #rrd-table_toggle", "0"); }); return ui; diff --git a/interface/js/app/history.js b/interface/js/app/history.js index aa886cc4d8..c5b90519dd 100644 --- a/interface/js/app/history.js +++ b/interface/js/app/history.js @@ -2,8 +2,8 @@ * Copyright (C) 2017 Vsevolod Stakhov */ -define(["jquery", "app/common", "app/libft", "app/tab-utils", "tabulator"], - ($, common, libft, tabUtils, Tabulator) => { +define(["app/common", "app/libft", "app/tab-utils", "tabulator"], + (common, libft, tabUtils, Tabulator) => { "use strict"; const ui = {}; let prevVersion = null; @@ -21,7 +21,7 @@ define(["jquery", "app/common", "app/libft", "app/tab-utils", "tabulator"], common.hide("#selSymOrder_history, label[for='selSymOrder_history']"); - $.each(data, (i, item) => { + data.forEach((item) => { libft.preprocess_item(item); item.symbols = Object.keys(item.symbols) .map((key) => item.symbols[key]) @@ -115,6 +115,16 @@ define(["jquery", "app/common", "app/libft", "app/tab-utils", "tabulator"], legacy: columns_legacy() }; + // Toggle the Refresh and Update-history buttons together: the `disabled` + // property controls behaviour, the `disabled` class keeps Bootstrap + // `.btn` styling in sync (redundant for the attribute alone on buttons). + function setControlsDisabled(disabled) { + document.querySelectorAll("#refresh, #updateHistory").forEach((el) => { + el.disabled = disabled; + el.classList.toggle("disabled", disabled); + }); + } + function process_history_data(data) { const process_functions = { 2: libft.process_history_v2, @@ -146,7 +156,7 @@ define(["jquery", "app/common", "app/libft", "app/tab-utils", "tabulator"], } ui.getHistory = function () { - $("#refresh, #updateHistory").attr("disabled", true); + setControlsDisabled(true); const histTo = histFrom - 1 + histCount; common.query(`history?from=${histFrom}&to=${histTo}`, { success: function (req_data) { @@ -191,7 +201,10 @@ define(["jquery", "app/common", "app/libft", "app/tab-utils", "tabulator"], libft.destroyTable("history"); libft.initHistoryTable(data, items, "history", get_history_columns(data), false, () => { - $("#history .tab-columns-dropdown .btn-dropdown-apply").removeAttr("disabled"); + document.querySelectorAll("#history .tab-columns-dropdown .btn-dropdown-apply") + .forEach((el) => { + el.disabled = false; + }); ui.updateHistoryControlsState(); if (version) libft.bindFuzzyHashButtons("history"); }); @@ -288,7 +301,7 @@ define(["jquery", "app/common", "app/libft", "app/tab-utils", "tabulator"], .filter((d) => d.status) // filter out unavailable neighbours .map((d) => d.data); const rows = [].concat.apply([], neighbours_data); - $.each(rows, (i, item) => { + rows.forEach((item) => { for (const prop in item) { if (!{}.hasOwnProperty.call(item, prop)) continue; if (typeof item[prop] === "string") item[prop] = common.escapeHTML(item[prop]); @@ -301,24 +314,13 @@ define(["jquery", "app/common", "app/libft", "app/tab-utils", "tabulator"], } } }); - - $("#updateErrors").off("click"); - $("#updateErrors").on("click", (e) => { - e.preventDefault(); - ui.getErrors(); - }); }; ui.updateHistoryControlsState = function () { - const from = parseInt($("#history-from").val(), 10); - const count = parseInt($("#history-count").val(), 10); + const from = parseInt(document.getElementById("history-from").value, 10); + const count = parseInt(document.getElementById("history-count").value, 10); const valid = !(isNaN(from) || from < 0 || isNaN(count) || count < 1); - - if (valid) { - $("#refresh, #updateHistory").removeAttr("disabled").removeClass("disabled"); - } else { - $("#refresh, #updateHistory").attr("disabled", true).addClass("disabled"); - } + setControlsDisabled(!valid); }; function validateAndClampInput(el) { @@ -326,42 +328,42 @@ define(["jquery", "app/common", "app/libft", "app/tab-utils", "tabulator"], let v = parseInt(el.value, 10); if (isNaN(v) || v < min) { v = min; - $(el).addClass("is-invalid"); + el.classList.add("is-invalid"); } else { - $(el).removeClass("is-invalid"); + el.classList.remove("is-invalid"); } return v; } - $("#history-from").val(histFrom); - $("#history-count").val(histCount); - $("#history-from, #history-count").on("input", (e) => { - validateAndClampInput(e.currentTarget); - ui.updateHistoryControlsState(); - }); - $("#history-from, #history-count").on("blur", (e) => { - const el = e.currentTarget; - const v = validateAndClampInput(el); - $(el).val(v).removeClass("is-invalid"); - ui.updateHistoryControlsState(); - }); - $("#history-from,#history-count").on("change", () => { - histFrom = parseInt($("#history-from").val(), 10) || histFromDef; - histCount = parseInt($("#history-count").val(), 10) || historyCountDef; + document.getElementById("history-from").value = histFrom; + document.getElementById("history-count").value = histCount; + document.querySelectorAll("#history-from, #history-count").forEach((el) => { + el.addEventListener("input", () => { + validateAndClampInput(el); + ui.updateHistoryControlsState(); + }); + el.addEventListener("blur", () => { + const v = validateAndClampInput(el); + el.value = v; + el.classList.remove("is-invalid"); + ui.updateHistoryControlsState(); + }); + el.addEventListener("change", () => { + histFrom = parseInt(document.getElementById("history-from").value, 10) || histFromDef; + histCount = parseInt(document.getElementById("history-count").value, 10) || historyCountDef; + }); }); - libft.set_page_size("history", $("#history_page_size").val()); + libft.set_page_size("history", document.getElementById("history_page_size").value); libft.bindHistoryTableEventHandlers("history", 9); - $("#updateHistory").off("click"); - $("#updateHistory").on("click", (e) => { + document.getElementById("updateHistory").addEventListener("click", (e) => { e.preventDefault(); ui.getHistory(); }); // @reset history log - $("#resetHistory").off("click"); - $("#resetHistory").on("click", (e) => { + document.getElementById("resetHistory").addEventListener("click", (e) => { e.preventDefault(); if (!confirm("Are you sure you want to reset history log?")) { // eslint-disable-line no-alert return; @@ -378,5 +380,10 @@ define(["jquery", "app/common", "app/libft", "app/tab-utils", "tabulator"], }); }); + document.getElementById("updateErrors").addEventListener("click", (e) => { + e.preventDefault(); + ui.getErrors(); + }); + return ui; }); diff --git a/interface/js/app/selectors.js b/interface/js/app/selectors.js index 637a595253..d16c75eee3 100644 --- a/interface/js/app/selectors.js +++ b/interface/js/app/selectors.js @@ -1,18 +1,18 @@ -define(["jquery", "app/common"], - ($, common) => { +define(["app/common"], + (common) => { "use strict"; const ui = {}; const fileSet = {files: null, index: null}; function enable_disable_check_btn() { - $("#selectorsChkMsgBtn").prop("disabled", ( - $.trim($("#selectorsMsgArea").val()).length === 0 || - !$("#selectorsSelArea").hasClass("is-valid") - )); + const msgArea = document.getElementById("selectorsMsgArea"); + const selArea = document.getElementById("selectorsSelArea"); + document.getElementById("selectorsChkMsgBtn").disabled = + msgArea.value.trim().length === 0 || !selArea.classList.contains("is-valid"); } function checkMsg(data) { - const selector = $("#selectorsSelArea").val(); + const selector = document.getElementById("selectorsSelArea").value; common.query("plugins/selectors/check_message?selector=" + encodeURIComponent(selector), { data: data, method: "POST", @@ -20,8 +20,8 @@ define(["jquery", "app/common"], const json = neighbours_status[0].data; if (json.success) { common.alertMessage("alert-success", "Message successfully processed"); - $("#selectorsResArea") - .val(Object.prototype.hasOwnProperty.call(json, "data") ? json.data.toString() : ""); + document.getElementById("selectorsResArea").value = + Object.prototype.hasOwnProperty.call(json, "data") ? json.data.toString() : ""; } else { common.alertMessage("alert-danger", "Unexpected error processing message"); } @@ -31,11 +31,13 @@ define(["jquery", "app/common"], } function checkSelectors() { + const selArea = document.getElementById("selectorsSelArea"); function toggle_form_group_class(remove, add) { - $("#selectorsSelArea").removeClass("is-" + remove).addClass("is-" + add); + selArea.classList.remove("is-" + remove); + selArea.classList.add("is-" + add); enable_disable_check_btn(); } - const selector = $("#selectorsSelArea").val(); + const selector = selArea.value; if (selector.length && !common.read_only) { common.query("plugins/selectors/check_selector?selector=" + encodeURIComponent(selector), { method: "GET", @@ -49,7 +51,7 @@ define(["jquery", "app/common"], server: common.getServer() }); } else { - $("#selectorsSelArea").removeClass("is-valid is-invalid"); + selArea.classList.remove("is-valid", "is-invalid"); enable_disable_check_btn(); } } @@ -57,11 +59,11 @@ define(["jquery", "app/common"], function buildLists() { function build_table_from_json(json, table_id) { Object.keys(json).forEach((key) => { - const td = $(""); - const tr = $("") - .append(td.clone().html("" + key + "")) - .append(td.clone().html(json[key].description)); - $(table_id + " tbody").append(tr); + const tr = common.el("tr", null, + common.el("td", null, common.el("code", {text: key})), + common.el("td", {text: json[key].description}) + ); + document.querySelector(table_id + " tbody").append(tr); }); } @@ -82,53 +84,51 @@ define(["jquery", "app/common"], ui.displayUI = function () { if (!common.read_only && - !$("#selectorsTable-extractors>tbody>tr").length && - !$("#selectorsTable-transforms>tbody>tr").length) buildLists(); - if (!$("#selectorsSelArea").is(".is-valid, .is-invalid")) checkSelectors(); + !document.querySelector("#selectorsTable-extractors>tbody>tr") && + !document.querySelector("#selectorsTable-transforms>tbody>tr")) buildLists(); + if (!document.getElementById("selectorsSelArea").matches(".is-valid, .is-invalid")) checkSelectors(); }; function toggleSidebar(side) { - $("#sidebar-" + side).toggleClass("collapsed"); - const openSidebarsCount = $("#sidebar-left").hasClass("collapsed") + - $("#sidebar-right").hasClass("collapsed"); + document.getElementById("sidebar-" + side).classList.toggle("collapsed"); + const openSidebarsCount = document.getElementById("sidebar-left").classList.contains("collapsed") + + document.getElementById("sidebar-right").classList.contains("collapsed"); const layoutMap = {1: "col-lg-9", 2: "col-lg-12"}; const contentClass = layoutMap[openSidebarsCount] || "col-lg-6"; - $("#content").removeClass("col-lg-12 col-lg-9 col-lg-6") - .addClass(contentClass); + const content = document.getElementById("content"); + content.classList.remove("col-lg-12", "col-lg-9", "col-lg-6"); + content.classList.add(contentClass); } - $("#sidebar-tab-left>a").click(() => { + document.querySelector("#sidebar-tab-left > a").addEventListener("click", (e) => { + e.preventDefault(); toggleSidebar("left"); - return false; }); - $("#sidebar-tab-right>a").click(() => { + document.querySelector("#sidebar-tab-right > a").addEventListener("click", (e) => { + e.preventDefault(); toggleSidebar("right"); - return false; }); - $("#selectorsMsgClean").on("click", () => { - $("#selectorsChkMsgBtn").attr("disabled", true); - $("#selectorsMsgArea").val(""); - return false; + document.getElementById("selectorsMsgClean").addEventListener("click", (e) => { + e.preventDefault(); + document.getElementById("selectorsChkMsgBtn").disabled = true; + document.getElementById("selectorsMsgArea").value = ""; + document.getElementById("selectorsFile").value = ""; }); - $("#selectorsClean").on("click", () => { - $("#selectorsSelArea").val(""); + document.getElementById("selectorsClean").addEventListener("click", (e) => { + e.preventDefault(); + document.getElementById("selectorsSelArea").value = ""; checkSelectors(); - return false; }); - $("#selectorsChkMsgBtn").on("click", () => { - $("#selectorsResArea").val(""); - checkMsg($("#selectorsMsgArea").val()); - return false; + document.getElementById("selectorsChkMsgBtn").addEventListener("click", (e) => { + e.preventDefault(); + document.getElementById("selectorsResArea").value = ""; + checkMsg(document.getElementById("selectorsMsgArea").value); }); - $("#selectorsSelArea").on("input", () => { + document.getElementById("selectorsSelArea").addEventListener("input", () => { checkSelectors(); }); - $("#selectorsMsgClean").on("click", () => { - $("#selectorsMsgArea").val(""); - $("#selectorsFile").val(""); - }); common.fileUtils.setupFileHandling("#selectorsMsgArea", "#selectorsFile", fileSet, enable_disable_check_btn); diff --git a/interface/js/app/symbols.js b/interface/js/app/symbols.js index c2d18c2ef2..7050800238 100644 --- a/interface/js/app/symbols.js +++ b/interface/js/app/symbols.js @@ -2,20 +2,31 @@ * Copyright (C) 2017 Vsevolod Stakhov */ -define(["jquery", "app/common", "app/tab-utils", "tabulator"], - ($, common, tabUtils, Tabulator) => { +define(["app/common", "app/tab-utils", "tabulator"], + (common, tabUtils, Tabulator) => { "use strict"; const ui = {}; let altered = {}; let groupSelectEl = null; + function setSymbolsControlsDisabled(disabled) { + document.querySelectorAll("#refresh, #updateSymbols").forEach((el) => { + el.disabled = disabled; + }); + } + function setSaveButtonsDisabled(disabled) { + document.querySelectorAll("#save-alert button").forEach((el) => { + el.disabled = disabled; + }); + } + function clear_altered() { - $("#save-alert").addClass("d-none"); + document.getElementById("save-alert").classList.add("d-none"); altered = {}; } function saveSymbols(server) { - $("#save-alert button").attr("disabled", true); + setSaveButtonsDisabled(true); const values = []; Object.entries(altered).forEach(([key, value]) => values.push({name: key, value: value})); @@ -25,7 +36,7 @@ define(["jquery", "app/common", "app/tab-utils", "tabulator"], clear_altered(); common.alertMessage("alert-modal alert-success", "Symbols successfully saved"); }, - complete: () => $("#save-alert button").removeAttr("disabled"), + complete: () => setSaveButtonsDisabled(false), errorMessage: "Save symbols error", method: "POST", params: { @@ -114,7 +125,7 @@ define(["jquery", "app/common", "app/tab-utils", "tabulator"], // @get symbols into modal form ui.getSymbols = function () { - $("#refresh, #updateSymbols").attr("disabled", true); + setSymbolsControlsDisabled(true); clear_altered(); common.query("symbols", { success: function (json) { @@ -214,23 +225,25 @@ define(["jquery", "app/common", "app/tab-utils", "tabulator"], common.tables.symbols.on("tableBuilt", () => { if (common.read_only) { - $(".mb-disabled").attr("disabled", true); + document.querySelectorAll(".mb-disabled").forEach((el) => { + el.disabled = true; + }); } - $("#refresh, #updateSymbols").removeAttr("disabled"); + setSymbolsControlsDisabled(false); }); common.tables.symbols.on("renderComplete", () => { - $("#refresh, #updateSymbols").removeAttr("disabled"); + setSymbolsControlsDisabled(false); }); }, - error: () => $("#refresh, #updateSymbols").removeAttr("disabled"), + error: () => setSymbolsControlsDisabled(false), server: common.getServer() }); }; - $("#updateSymbols").on("click", (e) => { + document.getElementById("updateSymbols").addEventListener("click", (e) => { e.preventDefault(); - $("#refresh, #updateSymbols").attr("disabled", true); + setSymbolsControlsDisabled(true); clear_altered(); common.query("symbols", { success: function (data) { @@ -241,28 +254,27 @@ define(["jquery", "app/common", "app/tab-utils", "tabulator"], common.tables.symbols.updateColumnDefinition("frequency", {formatterParams: freqParams}); common.tables.symbols.updateColumnDefinition("frequency_stddev", {formatterParams: freqParams}); }, - error: () => $("#refresh, #updateSymbols").removeAttr("disabled"), + error: () => setSymbolsControlsDisabled(false), server: common.getServer() }); }); - $("#symbolsTable") - .on("input", ".scorebar", ({target}) => { - const t = $(target); - t.removeClass("scorebar-ham scorebar-spam"); - if (target.value < 0) { - t.addClass("scorebar-ham"); - } else if (target.value > 0) { - t.addClass("scorebar-spam"); - } - }) - .on("change", ".scorebar", ({target}) => { - altered[$(target).attr("id").substring(5)] = parseFloat(target.value); - $("#save-alert").removeClass("d-none"); - }); + common.delegate("#symbolsTable", "input", ".scorebar", (event, target) => { + target.classList.remove("scorebar-ham", "scorebar-spam"); + if (target.value < 0) { + target.classList.add("scorebar-ham"); + } else if (target.value > 0) { + target.classList.add("scorebar-spam"); + } + }); + common.delegate("#symbolsTable", "change", ".scorebar", (event, target) => { + altered[target.id.substring(5)] = parseFloat(target.value); + document.getElementById("save-alert").classList.remove("d-none"); + }); - $("#save-alert button") - .on("click", ({target}) => saveSymbols($(target).data("save"))); + document.querySelectorAll("#save-alert button").forEach((el) => { + el.addEventListener("click", () => saveSymbols(el.dataset.save)); + }); return ui; });