This patch is an effort to modernize and unify the WUI JavaScript. It will be possible to share e.g. translation strings and common functions between the different modules.
This uses native ES6 modules that are supported by all major browsers. Therefore, no change to the toolchain is necessary.
Signed-off-by: Leo-Andres Hofmann hofmann@leo-andres.de ---
Hi all,
this patchset brings new modern Javascript modules to the webinterface.
Still on my to-do/wish list: - run make.sh lang - convert pakfire.js to a module - convert/rewrite refreshInetInfo.js, maybe add load displays to all interfaces in index.cgi
I hope this is in a presentable state to see where it is headed. Let me know what you think and as always thanks for reading/testing/reviewing :)
Best, Leo
config/etc/mime.types | 1 + html/html/include/wui.js | 47 ++++++ html/html/include/wui_core.mjs | 154 +++++++++++++++++++ html/html/include/wui_rrdimage.mjs | 30 ++++ html/html/themes/ipfire/include/functions.pl | 3 +- 5 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 html/html/include/wui.js create mode 100644 html/html/include/wui_core.mjs create mode 100644 html/html/include/wui_rrdimage.mjs
diff --git a/config/etc/mime.types b/config/etc/mime.types index af82d4886..bb3816db9 100644 --- a/config/etc/mime.types +++ b/config/etc/mime.types @@ -252,6 +252,7 @@ multipart/voice-message text/css css text/directory text/enriched +text/javascript mjs text/plain asc txt text/prs.lines.tag text/rfc822-headers diff --git a/html/html/include/wui.js b/html/html/include/wui.js new file mode 100644 index 000000000..e65924e29 --- /dev/null +++ b/html/html/include/wui.js @@ -0,0 +1,47 @@ +/*############################################################################# +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Team info@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see http://www.gnu.org/licenses/. # +# # +#############################################################################*/ + +// IPFire Web User Interface +// Collection of JavaScript functions and modules (requires jQuery) + +import {WUIcore_i18n as WUI_i18n} from "./wui_core.mjs"; + +import {WUImodule_rrdimage as WUI_rrdimage} from "./wui_rrdimage.mjs"; + +//--- WUI main class --- +class WUImain { + constructor() { + //- Public properties - + // Translation strings + this.i18n = new WUI_i18n(); + + //- Modules - + // RRDtool graph images + this.rrdimage = new WUI_rrdimage(this.i18n); + + //- Defaults - + // These modules are available on every page: + this.rrdimage.enabled = true; + } +} + +//### Initialize WUI ### +const wui = new WUImain(); +export default wui; diff --git a/html/html/include/wui_core.mjs b/html/html/include/wui_core.mjs new file mode 100644 index 000000000..b7b729396 --- /dev/null +++ b/html/html/include/wui_core.mjs @@ -0,0 +1,154 @@ +/*############################################################################# +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Team info@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see <http://www. gnu.org/licenses/>. # +# # +#############################################################################*/ + +// IPFire Web User Interface - JavaScript module +// Core functions & module helpers + +//--- Module template --- +// Make sure that overridden functions are still executed with super()! +export class WUIcore_moduleBase { + //- Private properties - + #enabled; // Activation state, disabled by default + #readyState; // Loading state similar to Document.readyState + #namespace; // Namespace derived from the class name (without "WUImod_" prefix) + + //- Class constructor - + constructor(translations) { + this.i18n = translations; + + this.#enabled = false; + + this.#readyState = "loading"; + this.#namespace = this.constructor.name.slice(10); + } + + // Module activation state + // Note: Because a module likely changes the DOM, it is not intended that it can + // be cleanly disabled again. Disabling it anyway will trigger "_handleModuleDestroy". + set enabled(state) { + if(this.#enabled !== state) { + this.#enabled = state; + + if(state) { + this._handleModuleEnable(); + } else { + this._handleModuleDestroy(); + } + } + } + get enabled() { + return this.#enabled; + } + + // Module loading state + // loading: Instance created + // interactive: Module enabled, document not ready + // complete: DOMContentLoaded/jQuery.ready event fired + // destroyed: Module disabled, event handlers removed + get readyState() { + return this.#readyState; + } + + // Module namespace + get namespace() { + return this.#namespace; + } + + //### Protected properties ### + + // Module activation state event handlers + _handleModuleEnable() { + if(this.#readyState === "loading") { + this.#readyState = "interactive"; + + // Attach jQuery.ready event + $(this._handleDOMReady.bind(this)); + } + } + _handleModuleDestroy() { + this.#readyState = "destroyed"; + + // Attempt to remove all event handlers of this module + $("body").off(`.${this.namespace}`); + } + + // DOMContentLoaded/jQuery.ready event handler + // The module must be enabled for this event to be triggered once + _handleDOMReady() { + this.#readyState = "complete"; + } + + // Translations quick access in module namespace + _i18n(key) { + return this.i18n.get(key, this.namespace); + } +} + +//--- Simple translation strings helper --- +export class WUIcore_i18n { + //- Private properties - + #strings; + + //- Class constructor - + constructor() { + this.#strings = Object.create(null); //Object without prototypes + } + + // Default module prefix for general translations + get #mainModule() { + return "wui"; + } + + // Get translation + get(key, module = this.#mainModule) { + const index = `${module}%${key}`; + + if(Object.hasOwn(this.#strings, index)) { + return this.#strings[index]; + } + return `(missing string '${key}' in '${module}')`; + } + + // Add translation + add(key, value, module = this.#mainModule) { + if(typeof value === "string" || typeof value === "number") { + this.#strings[`${module}%${key}`] = value; + } + } + + // Load key/translation JS object + // Format: {"key": "translation"} + load(translations, module = this.#mainModule) { + if(translations instanceof Object) { + Object.entries(translations).forEach(([key, value]) => { + this.add(key, value, module); + }); + } + } +} + +//--- Static utility functions --- +export class WUIcore_utilities { + // Reload document (clears POST/GET data from history) + static reloadDocument() { + const url = window.location.origin + window.location.pathname; + window.location.replace(url); + } +} diff --git a/html/html/include/wui_rrdimage.mjs b/html/html/include/wui_rrdimage.mjs new file mode 100644 index 000000000..5254b1e98 --- /dev/null +++ b/html/html/include/wui_rrdimage.mjs @@ -0,0 +1,30 @@ +/*############################################################################# +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Team info@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see http://www.gnu.org/licenses/. # +# # +#############################################################################*/ + +// IPFire Web User Interface - JavaScript module + +import {WUIcore_moduleBase as WUI_module} from "./wui_core.mjs"; + +//--- RRDtool graph images --- +export class WUImodule_rrdimage extends WUI_module { + constructor(translations) { + super(translations); + } +} diff --git a/html/html/themes/ipfire/include/functions.pl b/html/html/themes/ipfire/include/functions.pl index cbd05d109..445597e51 100644 --- a/html/html/themes/ipfire/include/functions.pl +++ b/html/html/themes/ipfire/include/functions.pl @@ -113,7 +113,8 @@ print <<END; <title>$headline - $title</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="shortcut icon" href="/favicon.ico" /> - <script type="text/javascript" src="/include/jquery.js"></script> + <script src="/include/jquery.js"></script> + <script src="/include/wui.js" type="module"></script> <script src="/include/rrdimage.js"></script>
$extrahead
This migrates the rrdimage.js functions to the new WUI framework and improves some functions.
Signed-off-by: Leo-Andres Hofmann hofmann@leo-andres.de --- config/cfgroot/graphs.pl | 2 +- html/html/include/rrdimage.js | 122 ------------------- html/html/include/wui_rrdimage.mjs | 103 +++++++++++++++- html/html/themes/ipfire/include/functions.pl | 1 - 4 files changed, 102 insertions(+), 126 deletions(-) delete mode 100644 html/html/include/rrdimage.js
diff --git a/config/cfgroot/graphs.pl b/config/cfgroot/graphs.pl index 9803dd124..a637e1a6a 100644 --- a/config/cfgroot/graphs.pl +++ b/config/cfgroot/graphs.pl @@ -118,7 +118,7 @@ END # Print range select buttons foreach my $range (@time_ranges) { print <<END; - <li><button data-range="$range" onclick="rrdimage_selectRange(this)">$Lang::tr{$range}</button></li> + <li><button type="button" data-range="$range">$Lang::tr{$range}</button></li> END }
diff --git a/html/html/include/rrdimage.js b/html/html/include/rrdimage.js deleted file mode 100644 index c6f5930c9..000000000 --- a/html/html/include/rrdimage.js +++ /dev/null @@ -1,122 +0,0 @@ -/*############################################################################# -# # -# IPFire.org - A linux based firewall # -# Copyright (C) 2007-2021 IPFire Team info@ipfire.org # -# # -# This program is free software: you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation, either version 3 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program. If not, see http://www.gnu.org/licenses/. # -# # -#############################################################################*/ - -// "onclick" event handler for graph time range select button -// buttonObj: reference to the button -function rrdimage_selectRange(buttonObj) { - if(! (buttonObj && ('range' in buttonObj.dataset))) { - return; //required parameters are missing - } - - // Get selected time range from button - const range = buttonObj.dataset.range; - - // Get surrounding div box and select new range - let graphBox = $(buttonObj).closest('div'); - _rrdimg_setRange(graphBox, range); -} - -// Document loaded: Process all graphs, start reload timers -$(function() { - $('div.rrdimage').each(function() { - let graphBox = $(this); - _rrdimg_setRange(graphBox, graphBox.data('defaultRange'), true); - }); -}); - -//--- Internal functions --- - -// Set or update graph time range, start automatic reloading -// graphBox: jQuery object, reference to graph div box -// range: time range (day, hour, ...) -// initMode: don't immediately reload graph, but force timers and attributes update -function _rrdimg_setRange(graphBox, range, initMode = false) { - if(! ((graphBox instanceof jQuery) && (graphBox.length === 1))) { - return; //graphBox element missing - } - - // Check range parameter, default to "day" on error - if(! ["hour", "day", "week", "month", "year"].includes(range)) { - range = "day"; - } - - // Check if the time range is changed - if((graphBox.data('range') !== range) || initMode) { - graphBox.data('range', range); //Store new range - - // Update button highlighting - graphBox.find('button').removeClass('selected'); - graphBox.find(`button[data-range="${range}"]`).addClass('selected'); - } - - // Clear pending reload timer to prevent multiple image reloads - let timerId = graphBox.data('reloadTimer'); - if(timerId !== undefined) { - window.clearInterval(timerId); - graphBox.removeData('reloadTimer'); - } - - // Determine auto reload interval (in seconds), - // interval = 0 disables auto reloading by default - let interval = 0; - switch(range) { - case 'hour': - interval = 60; - break; - - case 'day': - case 'week': - interval = 300; - break; - } - - // Start reload timer and store reference - if(interval > 0) { - timerId = window.setInterval(function(graphRef) { - _rrdimg_reload(graphRef); - }, interval * 1000, graphBox); - graphBox.data('reloadTimer', timerId); - } - - // Always reload image unless disabled by init mode - if(! initMode) { - _rrdimg_reload(graphBox); - } -} - -// Reload graph image, add timestamp to prevent caching -// graphBox: jQuery object (graph element must be valid) -function _rrdimg_reload(graphBox) { - const origin = graphBox.data('origin'); - const graph = graphBox.data('graph'); - const timestamp = Date.now(); - - // Get user selected range or fall back to default - let range = graphBox.data('range'); - if(! range) { - range = graphBox.data('defaultRange'); - } - - // Generate new image URL with timestamp - const imageUrl = `/cgi-bin/getrrdimage.cgi?origin=${origin}&graph=${graph}&range=${range}×tamp=${timestamp}`; - - // Get graph image and set new URL - graphBox.children('img').first().attr('src', imageUrl); -} diff --git a/html/html/include/wui_rrdimage.mjs b/html/html/include/wui_rrdimage.mjs index 5254b1e98..64b34dfac 100644 --- a/html/html/include/wui_rrdimage.mjs +++ b/html/html/include/wui_rrdimage.mjs @@ -24,7 +24,106 @@ import {WUIcore_moduleBase as WUI_module} from "./wui_core.mjs";
//--- RRDtool graph images --- export class WUImodule_rrdimage extends WUI_module { - constructor(translations) { - super(translations); + + // DOMContentLoaded/jQuery.ready event handler + _handleDOMReady() { + super._handleDOMReady(); + + // Process all graphs, start reload timers + $("div.rrdimage").each((i, divElem) => { + const $graphBox = $(divElem); + this.#updateProperties($graphBox, $graphBox.data("defaultRange"), true); + + // Attach event listeners to buttons + $graphBox.find("button[data-range]").each((j, buttonElem) => { + const $rangeBtn = $(buttonElem); + $rangeBtn.on(`click.${this.namespace}`, {"graphBox": $graphBox, "range": $rangeBtn.data("range")}, this.#handleRangeClick.bind(this)); + }); + }); + } + + // Graph range select button "click" event handler + #handleRangeClick(event) { + event.preventDefault(); + + this.#updateProperties(event.data["graphBox"], event.data["range"]); + } + + // Update graph range + // $graphBox: jQuery object, reference to a single graph div box + // range: time range (day, hour, ...) + setRange($graphBox, range) { + if(($graphBox instanceof jQuery) && ($graphBox.length === 1)) { + this.#updateProperties($graphBox, range); + } + } + + // Update graph range properties, configure automatic reloading + // $graphBox: jQuery object, reference to graph div box + // range: time range (day, hour, ...) + // initialize: don't immediately reload graph, but force timers and attributes update + #updateProperties($graphBox, range, initialize = false) { + // Check range parameter, default to "day" on error + if(! ["hour", "day", "week", "month", "year"].includes(range)) { + range = "day"; + } + + // Check if the time range is changed + if(($graphBox.data("range") !== range) || initialize) { + $graphBox.data("range", range); //Store new range + + // Update button highlighting + $graphBox.find("button[data-range]").removeClass("selected"); + $graphBox.find(`button[data-range="${range}"]`).addClass("selected"); + } + + // Clear pending reload timer to prevent multiple image reloads + let timerId = $graphBox.data("reloadTimer"); + if(typeof timerId !== "undefined") { + window.clearInterval(timerId); + $graphBox.removeData("reloadTimer"); + } + + // Determine auto reload interval (in seconds), + // interval = 0 disables auto reloading by default + let interval = 0; + switch(range) { + case "hour": + interval = 60; + break; + + case "day": + case "week": + interval = 300; + break; + } + + // Start reload timer and store reference + if(interval > 0) { + timerId = window.setInterval(this.#reloadImage.bind(this), interval * 1000, $graphBox); + $graphBox.data("reloadTimer", timerId); + } + + // Always reload image unless disabled by init mode + if(! initialize) { + this.#reloadImage($graphBox); + } + } + + // Reload graph image, add timestamp to prevent caching + // $graphBox: jQuery object (graph element must be valid/previously processed by updateRange) + #reloadImage($graphBox) { + const origin = $graphBox.data("origin"); + const graph = $graphBox.data("graph"); + + // Get user selected range or fall back to default + const range = $graphBox.data("range") ?? $graphBox.data("defaultRange"); + + // Generate new image URL with timestamp + const timestamp = Date.now(); + const imageUrl = `/cgi-bin/getrrdimage.cgi?origin=${origin}&graph=${graph}&range=${range}×tamp=${timestamp}`; + + // Get graph image and set new URL + $graphBox.children("img").first().attr("src", imageUrl); } } diff --git a/html/html/themes/ipfire/include/functions.pl b/html/html/themes/ipfire/include/functions.pl index 445597e51..bc66d7fdb 100644 --- a/html/html/themes/ipfire/include/functions.pl +++ b/html/html/themes/ipfire/include/functions.pl @@ -115,7 +115,6 @@ print <<END; <link rel="shortcut icon" href="/favicon.ico" /> <script src="/include/jquery.js"></script> <script src="/include/wui.js" type="module"></script> - <script src="/include/rrdimage.js"></script>
$extrahead <script type="text/javascript">
This patch adds a modal confirmation dialog for HTML forms.
The dialog can be invoked by setting the "data-confirm-message" attribute on the form. Default texts can be overridden with the "data-confirm-title" and "data-confirm-subheading" attributes.
Signed-off-by: Leo-Andres Hofmann hofmann@leo-andres.de --- html/html/include/wui.js | 6 ++ html/html/include/wui_core.mjs | 77 ++++++++++++++- html/html/include/wui_dialogs.mjs | 97 +++++++++++++++++++ html/html/themes/ipfire/include/css/style.css | 75 ++++++++++++++ html/html/themes/ipfire/include/functions.pl | 28 ++++++ langs/en/cgi-bin/en.pl | 4 + 6 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 html/html/include/wui_dialogs.mjs
diff --git a/html/html/include/wui.js b/html/html/include/wui.js index e65924e29..e219f5ca4 100644 --- a/html/html/include/wui.js +++ b/html/html/include/wui.js @@ -23,6 +23,7 @@
import {WUIcore_i18n as WUI_i18n} from "./wui_core.mjs";
+import {WUIdialog_confirm as WUI_confirm} from "./wui_dialogs.mjs"; import {WUImodule_rrdimage as WUI_rrdimage} from "./wui_rrdimage.mjs";
//--- WUI main class --- @@ -33,11 +34,16 @@ class WUImain { this.i18n = new WUI_i18n();
//- Modules - + // Dialogs + this.dialogs = {}; + this.dialogs.confirm = new WUI_confirm(this.i18n); + // RRDtool graph images this.rrdimage = new WUI_rrdimage(this.i18n);
//- Defaults - // These modules are available on every page: + this.dialogs.confirm.enabled = true; this.rrdimage.enabled = true; } } diff --git a/html/html/include/wui_core.mjs b/html/html/include/wui_core.mjs index b7b729396..ab5338f12 100644 --- a/html/html/include/wui_core.mjs +++ b/html/html/include/wui_core.mjs @@ -27,7 +27,7 @@ export class WUIcore_moduleBase { //- Private properties - #enabled; // Activation state, disabled by default #readyState; // Loading state similar to Document.readyState - #namespace; // Namespace derived from the class name (without "WUImod_" prefix) + #namespace; // Namespace derived from the class name (without "WUImodule_"/"WUIdialog_" prefix)
//- Class constructor - constructor(translations) { @@ -101,6 +101,81 @@ export class WUIcore_moduleBase { } }
+//--- Modal dialog template --- +// Make sure that overridden functions are still executed with super()! +// Text fields and buttons are identified by their data-... attributes: +// data-textbox="foo", data-action="bar". See setText for reference. +// Events should only be managed by the modules, there is no public interface by design. +export class WUIcore_dialogBase extends WUIcore_moduleBase { + //- Private properties - + #modalId; // Element ID of the overlay div box with dialog window + + //- Class constructor - + constructor(translations) { + super(translations); + + //- Protected properties - + // jQuery object, reference to dialog div box + this._$modal = $(); + } + + // DOMContentLoaded/jQuery.ready event handler + _handleDOMReady() { + super._handleDOMReady(); + + // modalId was set before, but the document was not ready yet + if(this.modalId && (! this.hasModal)) { + this._$modal = $(`#${this.modalId}`).first(); + } + } + + // Element ID of dialog overlay div box + // This element must be hidden by default and render a modal dialog when activated. + set modalId(id) { + this.#modalId = id; + + // Delay attaching element until DOM is ready + if(this.readyState === "complete") { + this._$modal = $(`#${id}`).first(); + } + } + get modalId() { + return this.#modalId; + } + + // Check if modal dialog element has been attached + get hasModal() { + return (this._$modal.length === 1); + } + + // Show/hide modal overlay by setting CSS "display" property + // The top modal element should always be a simple overlay, so that display=block does not disturb the layout. + showModal() { + this._$modal.css("display", "block"); + } + hideModal() { + this._$modal.css("display", "none"); + } + get isVisible() { + return (this._$modal.css("display") === "block"); + } + + // Set text field content. Fields are identified by their data-textbox attribute: + // <span data-textbox="message"></span> -> field "message" + setText(field, value) { + this._$modal.find(`[data-textbox="${field}"]`).text(value); + } + + //### Protected properties ### + + // Get a dialog window button as a jQuery object. Buttons are identified by their data-action attribute: + // <button type="button" data-action="submit">OK</button> -> action "submit" + // Button actions must be unique within the modal window. Events are managed internally only, do not add custom handlers. + _$getButton(action) { + return this._$modal.find(`button[data-action="${action}"]`).first(); + } +} + //--- Simple translation strings helper --- export class WUIcore_i18n { //- Private properties - diff --git a/html/html/include/wui_dialogs.mjs b/html/html/include/wui_dialogs.mjs new file mode 100644 index 000000000..a2b3bdbb4 --- /dev/null +++ b/html/html/include/wui_dialogs.mjs @@ -0,0 +1,97 @@ +/*############################################################################# +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Team info@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see http://www.gnu.org/licenses/. # +# # +#############################################################################*/ + +// IPFire Web User Interface - JavaScript module + +import {WUIcore_dialogBase as WUI_dialog} from "./wui_core.mjs"; + +//--- Form submit confirm dialog --- +// Text fields: data-textbox "title" "subheading" "message" +// Buttons: data-action "submit" "cancel" "close" +export class WUIdialog_confirm extends WUI_dialog { + + // DOMContentLoaded/jQuery.ready event handler + _handleDOMReady() { + super._handleDOMReady(); + + // Process all forms with confirmation request, attach submit events + $("form[data-confirm-message]").each((i, formElem) => { + const $form = $(formElem); + $form.on(`submit.${this.namespace}`, {"form": $form, "message": $form.data("confirmMessage")}, this.#handleFormSubmit.bind(this)); + }); + } + + // Form with confirmation "submit" event handler + async #handleFormSubmit(event) { + event.preventDefault(); + + const $form = event.data["form"]; + this.prepareModal(event.data["message"], $form.data("confirmTitle"), $form.data("confirmSubheading")); + + // Show the dialog and wait for user interaction + try { + const response = await this.requestAsync(); + if(response === true) { + // Trigger native HTML submit() method, since it does not raise another submit event that would cause a loop + event.currentTarget.submit(); + } + } catch(error) { + // User closed the window, do nothing + } + } + + // Show modal confirmation request dialog + // Returns a promise that resolves true/false upon user interaction and rejects when the window is closed. + requestAsync() { + // Attach promise to submit/cancel button "click" events + const whenConfirm = new Promise((resolve, reject) => { + this._$getButton("submit").on(`click.${this.namespace}`, () => { resolve(true); }); + this._$getButton("cancel").on(`click.${this.namespace}`, () => { resolve(false); }); + + this._$getButton("close").on(`click.${this.namespace}`, () => { reject(new Error("dialog window closed")); }); + }).finally(() => { + // Always hide the window and detach all button events after any click, + // so that the dialog can be used multiple times without side effects. + this.hideModal(); + this.#clearButtonEvents(); + }); + + // Show dialog, default action is "close" + this.showModal(); + this._$getButton("close").focus(); + + return whenConfirm; + } + + // Prepare dialog window: set texts, but leave hidden and without events + prepareModal(message, title = undefined, subheading = undefined) { + this.hideModal(); + this.#clearButtonEvents(); + + this.setText("message", message); + this.setText("title", title ?? this._i18n("title")); + this.setText("subheading", subheading ?? this._i18n("subheading")); + } + + // Remove all active events from dialog window buttons + #clearButtonEvents() { + this._$modal.find(`button[data-action]`).off(`.${this.namespace}`); + } +} diff --git a/html/html/themes/ipfire/include/css/style.css b/html/html/themes/ipfire/include/css/style.css index 96d0519f5..f4ef1769c 100644 --- a/html/html/themes/ipfire/include/css/style.css +++ b/html/html/themes/ipfire/include/css/style.css @@ -377,3 +377,78 @@ div.rrdimage > img { max-width: 100%; min-height: 290px; } + +/* Modal dialog box */ + +div.dialog-overlay { + display: none; + z-index: 99; + + left: 0; + top: 0; + width: 100%; + height: 100%; + position: fixed; + overflow: auto; + + background-color: rgba(0, 0, 0, 0.5); + animation: fadeIn 0.5s; +} +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +div.dialog-window { + top: 15%; + width: 550px; + margin: auto; + padding: 0; + position: relative; + + border: 1px solid black; + background: #fff url('../../images/n2.gif') 0px 0px repeat-x; + border-radius: 3px; + box-shadow: 3px 3px 15px rgba(0, 0, 0, 0.7); +} + +.dialog-window .section { + box-sizing: border-box; + padding: 1em 1.5em; + text-align: left; +} +.dialog-window .section:not(:last-child) { + border-bottom: 1px solid silver; +} + +.dialog-window .header > h3 { + display: inline-block; + color: #66000F; + font-size: 1.6em; +} +.dialog-window .header > button[data-action="close"] { + border: none; + background: none; + font-size: 1.2em; + font-weight: bold; + cursor: pointer; +} + +.dialog-window .content { + line-height: 150%; +} + +.dialog-window .header, +.dialog-window .controls { + display:flex; + flex-direction: row; + align-items: center; + justify-content: space-between; +} +.dialog-window .controls > button { + padding: 0.3em; + min-width: 35%; +} +.dialog-window .controls > button[data-action="submit"] { + font-weight: bold; +} diff --git a/html/html/themes/ipfire/include/functions.pl b/html/html/themes/ipfire/include/functions.pl index bc66d7fdb..784b2f398 100644 --- a/html/html/themes/ipfire/include/functions.pl +++ b/html/html/themes/ipfire/include/functions.pl @@ -217,6 +217,34 @@ print <<END;
<strong>$system_release</strong> </div> + + <!-- Modal confirm dialog --> + <div class="dialog-overlay" id="modal-dialog_confirm"> + <div class="dialog-window"> + <div class="section header"> + <h3 data-textbox="title"></h3> + <button type="button" data-action="close">×</button> + </div> + <div class="section content"> + <span data-textbox="subheading"></span><br> + <strong data-textbox="message"></strong> + </div> + <div class="section controls"> + <button type="button" data-action="cancel">$Lang::tr{'dialog confirm cancel'}</button> + <button type="button" data-action="submit">$Lang::tr{'dialog confirm submit'}</button> + </div> + </div> + </div> + <script type="module"> + import wui from "/include/wui.js"; + + wui.i18n.load({ + "title": "$Lang::tr{'dialog confirm title'}", + "subheading": "$Lang::tr{'dialog confirm subheading'}" + }, "confirm"); + + wui.dialogs.confirm.modalId = "modal-dialog_confirm"; + </script> </body> </html> END diff --git a/langs/en/cgi-bin/en.pl b/langs/en/cgi-bin/en.pl index 729516538..df9deda53 100644 --- a/langs/en/cgi-bin/en.pl +++ b/langs/en/cgi-bin/en.pl @@ -84,6 +84,10 @@ 'ConnSched up' => 'Up', 'ConnSched weekdays' => 'Days of the week:', 'Daily' => 'Daily', +'dialog confirm title' => 'Permanently save changes?', +'dialog confirm subheading' => 'The following action cannot be undone:', +'dialog confirm submit' => 'Yes, continue', +'dialog confirm cancel' => 'No, cancel', 'Disabled' => 'Disabled', 'Edit an existing route' => 'Edit an existing route', 'Enter TOS' => 'Activate or deactivate TOS-bits <br /> and then press <i>Save</i>.',
This adds a confirmation dialog to the delete buttons, thus preventing users from accidentally removing certificates that are still in use.
Fixes: #13058
Signed-off-by: Leo-Andres Hofmann hofmann@leo-andres.de --- html/cgi-bin/ovpnmain.cgi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/html/cgi-bin/ovpnmain.cgi b/html/cgi-bin/ovpnmain.cgi index 87bda4f1e..b0f00fa57 100644 --- a/html/cgi-bin/ovpnmain.cgi +++ b/html/cgi-bin/ovpnmain.cgi @@ -5539,7 +5539,7 @@ END <input type='image' name='$Lang::tr{'edit'}' src='/images/edit.gif' alt='$Lang::tr{'edit'}' title='$Lang::tr{'edit'}' width='20' height='20' border='0'/> <input type='hidden' name='KEY' value='$key' /> </td></form> - <form method='post' name='frm${key}f'><td align='center' $col> + <form method='post' name='frm${key}f' data-confirm-message='$Lang::tr{'remove'} $Lang::tr{$confighash{$key}[3]} ($Lang::tr{$confighash{$key}[4]}): "$confighash{$key}[1]"'><td align='center' $col> <input type='hidden' name='ACTION' value='$Lang::tr{'remove'}' /> <input type='image' name='$Lang::tr{'remove'}' src='/images/delete.gif' alt='$Lang::tr{'remove'}' title='$Lang::tr{'remove'}' width='20' height='20' border='0' /> <input type='hidden' name='KEY' value='$key' />
Hello,
Thanks for the patchset.
It is a lot of code to look through indeed.
On 1 Apr 2023, at 15:43, Leo-Andres Hofmann hofmann@leo-andres.de wrote:
This patch is an effort to modernize and unify the WUI JavaScript. It will be possible to share e.g. translation strings and common functions between the different modules.
This uses native ES6 modules that are supported by all major browsers. Therefore, no change to the toolchain is necessary.
Signed-off-by: Leo-Andres Hofmann hofmann@leo-andres.de
Hi all,
this patchset brings new modern Javascript modules to the webinterface.
I suppose there is no way around this.
Is this all written from scratch? Did you base this on anything or use some existing code as inspiration?
Are they any existing libraries that could be useful for example pulling in the translation?
-Michael
Still on my to-do/wish list:
- run make.sh lang
- convert pakfire.js to a module
- convert/rewrite refreshInetInfo.js, maybe add load displays to all interfaces in index.cgi
I hope this is in a presentable state to see where it is headed. Let me know what you think and as always thanks for reading/testing/reviewing :)
Best, Leo
config/etc/mime.types | 1 + html/html/include/wui.js | 47 ++++++ html/html/include/wui_core.mjs | 154 +++++++++++++++++++ html/html/include/wui_rrdimage.mjs | 30 ++++ html/html/themes/ipfire/include/functions.pl | 3 +- 5 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 html/html/include/wui.js create mode 100644 html/html/include/wui_core.mjs create mode 100644 html/html/include/wui_rrdimage.mjs
diff --git a/config/etc/mime.types b/config/etc/mime.types index af82d4886..bb3816db9 100644 --- a/config/etc/mime.types +++ b/config/etc/mime.types @@ -252,6 +252,7 @@ multipart/voice-message text/css css text/directory text/enriched +text/javascript mjs text/plain asc txt text/prs.lines.tag text/rfc822-headers diff --git a/html/html/include/wui.js b/html/html/include/wui.js new file mode 100644 index 000000000..e65924e29 --- /dev/null +++ b/html/html/include/wui.js @@ -0,0 +1,47 @@ +/*############################################################################# +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Team info@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see http://www.gnu.org/licenses/. # +# # +#############################################################################*/
+// IPFire Web User Interface +// Collection of JavaScript functions and modules (requires jQuery)
+import {WUIcore_i18n as WUI_i18n} from "./wui_core.mjs";
+import {WUImodule_rrdimage as WUI_rrdimage} from "./wui_rrdimage.mjs";
+//--- WUI main class --- +class WUImain {
- constructor() {
- //- Public properties -
- // Translation strings
- this.i18n = new WUI_i18n();
- //- Modules -
- // RRDtool graph images
- this.rrdimage = new WUI_rrdimage(this.i18n);
- //- Defaults -
- // These modules are available on every page:
- this.rrdimage.enabled = true;
- }
+}
+//### Initialize WUI ### +const wui = new WUImain(); +export default wui; diff --git a/html/html/include/wui_core.mjs b/html/html/include/wui_core.mjs new file mode 100644 index 000000000..b7b729396 --- /dev/null +++ b/html/html/include/wui_core.mjs @@ -0,0 +1,154 @@ +/*############################################################################# +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Team info@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see <http://www. gnu.org/licenses/>. # +# # +#############################################################################*/
+// IPFire Web User Interface - JavaScript module +// Core functions & module helpers
+//--- Module template --- +// Make sure that overridden functions are still executed with super()! +export class WUIcore_moduleBase {
- //- Private properties -
- #enabled; // Activation state, disabled by default
- #readyState; // Loading state similar to Document.readyState
- #namespace; // Namespace derived from the class name (without "WUImod_" prefix)
- //- Class constructor -
- constructor(translations) {
- this.i18n = translations;
- this.#enabled = false;
- this.#readyState = "loading";
- this.#namespace = this.constructor.name.slice(10);
- }
- // Module activation state
- // Note: Because a module likely changes the DOM, it is not intended that it can
- // be cleanly disabled again. Disabling it anyway will trigger "_handleModuleDestroy".
- set enabled(state) {
- if(this.#enabled !== state) {
- this.#enabled = state;
- if(state) {
- this._handleModuleEnable();
- } else {
- this._handleModuleDestroy();
- }
- }
- }
- get enabled() {
- return this.#enabled;
- }
- // Module loading state
- // loading: Instance created
- // interactive: Module enabled, document not ready
- // complete: DOMContentLoaded/jQuery.ready event fired
- // destroyed: Module disabled, event handlers removed
- get readyState() {
- return this.#readyState;
- }
- // Module namespace
- get namespace() {
- return this.#namespace;
- }
- //### Protected properties ###
- // Module activation state event handlers
- _handleModuleEnable() {
- if(this.#readyState === "loading") {
- this.#readyState = "interactive";
- // Attach jQuery.ready event
- $(this._handleDOMReady.bind(this));
- }
- }
- _handleModuleDestroy() {
- this.#readyState = "destroyed";
- // Attempt to remove all event handlers of this module
- $("body").off(`.${this.namespace}`);
- }
- // DOMContentLoaded/jQuery.ready event handler
- // The module must be enabled for this event to be triggered once
- _handleDOMReady() {
- this.#readyState = "complete";
- }
- // Translations quick access in module namespace
- _i18n(key) {
- return this.i18n.get(key, this.namespace);
- }
+}
+//--- Simple translation strings helper --- +export class WUIcore_i18n {
- //- Private properties -
- #strings;
- //- Class constructor -
- constructor() {
- this.#strings = Object.create(null); //Object without prototypes
- }
- // Default module prefix for general translations
- get #mainModule() {
- return "wui";
- }
- // Get translation
- get(key, module = this.#mainModule) {
- const index = `${module}%${key}`;
- if(Object.hasOwn(this.#strings, index)) {
- return this.#strings[index];
- }
- return `(missing string '${key}' in '${module}')`;
- }
- // Add translation
- add(key, value, module = this.#mainModule) {
- if(typeof value === "string" || typeof value === "number") {
- this.#strings[`${module}%${key}`] = value;
- }
- }
- // Load key/translation JS object
- // Format: {"key": "translation"}
- load(translations, module = this.#mainModule) {
- if(translations instanceof Object) {
- Object.entries(translations).forEach(([key, value]) => {
- this.add(key, value, module);
- });
- }
- }
+}
+//--- Static utility functions --- +export class WUIcore_utilities {
- // Reload document (clears POST/GET data from history)
- static reloadDocument() {
- const url = window.location.origin + window.location.pathname;
- window.location.replace(url);
- }
+} diff --git a/html/html/include/wui_rrdimage.mjs b/html/html/include/wui_rrdimage.mjs new file mode 100644 index 000000000..5254b1e98 --- /dev/null +++ b/html/html/include/wui_rrdimage.mjs @@ -0,0 +1,30 @@ +/*############################################################################# +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Team info@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see http://www.gnu.org/licenses/. # +# # +#############################################################################*/
+// IPFire Web User Interface - JavaScript module
+import {WUIcore_moduleBase as WUI_module} from "./wui_core.mjs";
+//--- RRDtool graph images --- +export class WUImodule_rrdimage extends WUI_module {
- constructor(translations) {
- super(translations);
- }
+} diff --git a/html/html/themes/ipfire/include/functions.pl b/html/html/themes/ipfire/include/functions.pl index cbd05d109..445597e51 100644 --- a/html/html/themes/ipfire/include/functions.pl +++ b/html/html/themes/ipfire/include/functions.pl @@ -113,7 +113,8 @@ print <<END;
<title>$headline - $title</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="shortcut icon" href="/favicon.ico" /> - <script type="text/javascript" src="/include/jquery.js"></script> + <script src="/include/jquery.js"></script> + <script src="/include/wui.js" type="module"></script> <script src="/include/rrdimage.js"></script>
$extrahead
2.37.1.windows.1
Hi Michael,
Am 11.04.2023 um 14:58 schrieb Michael Tremer:
Hello,
Thanks for the patchset.
It is a lot of code to look through indeed.
Yes, I'm aware this is a bit unannounced and a lot of code. Thank you for looking at it!
On 1 Apr 2023, at 15:43, Leo-Andres Hofmann hofmann@leo-andres.de wrote:
This patch is an effort to modernize and unify the WUI JavaScript. It will be possible to share e.g. translation strings and common functions between the different modules.
This uses native ES6 modules that are supported by all major browsers. Therefore, no change to the toolchain is necessary.
Signed-off-by: Leo-Andres Hofmann hofmann@leo-andres.de
Hi all,
this patchset brings new modern Javascript modules to the webinterface.
I suppose there is no way around this.
My thinking was: At the moment, the web interface is quite old-fashined and uses relatively little javascript, scattered in multiple files. But new features are still added on a fairly regular basis. So I think it makes sense to start a good structure now rather than later.
Is this all written from scratch? Did you base this on anything or use some existing code as inspiration?
This is written from scratch, in the sense that it is not just a modified copy of some existing library. However, I have read the documentation of other libraries before, and there are only so many reasonable ways to implement this. So I haven't tried to reinvent the wheel, and you'll certainly be able to find similarities to existing libraries. But the actual source code in this patchset is 100% my own work.
- The module.enable/readyState logic is inspired by jQuery's .ready and the HTML DOM readyState property - The i18n class is based on the work I did for pakfire.js and extended to work with the module concept - To learn how to create the CSS overlay window, I followed the w3schools tutorial "Overlay" - The async/await interface for the dialog box is inspired by jQuery's deferred.then() interface for Ajax calls, but implemented with native JS
Are they any existing libraries that could be useful for example pulling in the translation?
I looked at a few popular libraries, but in my opinion they are too extensive and do not fit well with the existing web interface. (jquery.i18n, Polyglot, ...) For example, some libraries I found prefer to load the texts from a JSON file. This would result in additional language files parallel to the existing system. Besides, dynamically changing translations based on user agent settings may break the form field mapping: if ($cgiparams{'ACTION'} eq $Lang::tr{'save'} )... If we didn't have to pay attention to many specifics of the old CGIs, I would also prefer an existing library.
Best, Leo
-Michael
Still on my to-do/wish list:
- run make.sh lang
- convert pakfire.js to a module
- convert/rewrite refreshInetInfo.js, maybe add load displays to all interfaces in index.cgi
I hope this is in a presentable state to see where it is headed. Let me know what you think and as always thanks for reading/testing/reviewing :)
Best, Leo
config/etc/mime.types | 1 + html/html/include/wui.js | 47 ++++++ html/html/include/wui_core.mjs | 154 +++++++++++++++++++ html/html/include/wui_rrdimage.mjs | 30 ++++ html/html/themes/ipfire/include/functions.pl | 3 +- 5 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 html/html/include/wui.js create mode 100644 html/html/include/wui_core.mjs create mode 100644 html/html/include/wui_rrdimage.mjs
diff --git a/config/etc/mime.types b/config/etc/mime.types index af82d4886..bb3816db9 100644 --- a/config/etc/mime.types +++ b/config/etc/mime.types @@ -252,6 +252,7 @@ multipart/voice-message text/css css text/directory text/enriched +text/javascript mjs text/plain asc txt text/prs.lines.tag text/rfc822-headers diff --git a/html/html/include/wui.js b/html/html/include/wui.js new file mode 100644 index 000000000..e65924e29 --- /dev/null +++ b/html/html/include/wui.js @@ -0,0 +1,47 @@ +/*############################################################################# +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Team info@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see http://www.gnu.org/licenses/. # +# # +#############################################################################*/
+// IPFire Web User Interface +// Collection of JavaScript functions and modules (requires jQuery)
+import {WUIcore_i18n as WUI_i18n} from "./wui_core.mjs";
+import {WUImodule_rrdimage as WUI_rrdimage} from "./wui_rrdimage.mjs";
+//--- WUI main class --- +class WUImain {
- constructor() {
- //- Public properties -
- // Translation strings
- this.i18n = new WUI_i18n();
- //- Modules -
- // RRDtool graph images
- this.rrdimage = new WUI_rrdimage(this.i18n);
- //- Defaults -
- // These modules are available on every page:
- this.rrdimage.enabled = true;
- }
+}
+//### Initialize WUI ### +const wui = new WUImain(); +export default wui; diff --git a/html/html/include/wui_core.mjs b/html/html/include/wui_core.mjs new file mode 100644 index 000000000..b7b729396 --- /dev/null +++ b/html/html/include/wui_core.mjs @@ -0,0 +1,154 @@ +/*############################################################################# +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Team info@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see <http://www. gnu.org/licenses/>[http://www.gnu.org/licenses/]. # +# # +#############################################################################*/
+// IPFire Web User Interface - JavaScript module +// Core functions & module helpers
+//--- Module template --- +// Make sure that overridden functions are still executed with super()! +export class WUIcore_moduleBase {
- //- Private properties -
- #enabled; // Activation state, disabled by default
- #readyState; // Loading state similar to Document.readyState
- #namespace; // Namespace derived from the class name (without "WUImod_" prefix)
- //- Class constructor -
- constructor(translations) {
- this.i18n = translations;
- this.#enabled = false;
- this.#readyState = "loading";
- this.#namespace = this.constructor.name.slice(10);
- }
- // Module activation state
- // Note: Because a module likely changes the DOM, it is not intended that it can
- // be cleanly disabled again. Disabling it anyway will trigger "_handleModuleDestroy".
- set enabled(state) {
- if(this.#enabled !== state) {
- this.#enabled = state;
- if(state) {
- this._handleModuleEnable();
- } else {
- this._handleModuleDestroy();
- }
- }
- }
- get enabled() {
- return this.#enabled;
- }
- // Module loading state
- // loading: Instance created
- // interactive: Module enabled, document not ready
- // complete: DOMContentLoaded/jQuery.ready event fired
- // destroyed: Module disabled, event handlers removed
- get readyState() {
- return this.#readyState;
- }
- // Module namespace
- get namespace() {
- return this.#namespace;
- }
- //### Protected properties ###
- // Module activation state event handlers
- _handleModuleEnable() {
- if(this.#readyState === "loading") {
- this.#readyState = "interactive";
- // Attach jQuery.ready event
- $(this._handleDOMReady.bind(this));
- }
- }
- _handleModuleDestroy() {
- this.#readyState = "destroyed";
- // Attempt to remove all event handlers of this module
- $("body").off(`.${this.namespace}`);
- }
- // DOMContentLoaded/jQuery.ready event handler
- // The module must be enabled for this event to be triggered once
- _handleDOMReady() {
- this.#readyState = "complete";
- }
- // Translations quick access in module namespace
- _i18n(key) {
- return this.i18n.get(key, this.namespace);
- }
+}
+//--- Simple translation strings helper --- +export class WUIcore_i18n {
- //- Private properties -
- #strings;
- //- Class constructor -
- constructor() {
- this.#strings = Object.create(null); //Object without prototypes
- }
- // Default module prefix for general translations
- get #mainModule() {
- return "wui";
- }
- // Get translation
- get(key, module = this.#mainModule) {
- const index = `${module}%${key}`;
- if(Object.hasOwn(this.#strings, index)) {
- return this.#strings[index];
- }
- return `(missing string '${key}' in '${module}')`;
- }
- // Add translation
- add(key, value, module = this.#mainModule) {
- if(typeof value === "string" || typeof value === "number") {
- this.#strings[`${module}%${key}`] = value;
- }
- }
- // Load key/translation JS object
- // Format: {"key": "translation"}
- load(translations, module = this.#mainModule) {
- if(translations instanceof Object) {
- Object.entries(translations).forEach(([key, value]) => {
- this.add(key, value, module);
- });
- }
- }
+}
+//--- Static utility functions --- +export class WUIcore_utilities {
- // Reload document (clears POST/GET data from history)
- static reloadDocument() {
- const url = window.location.origin + window.location.pathname;
- window.location.replace(url);
- }
+} diff --git a/html/html/include/wui_rrdimage.mjs b/html/html/include/wui_rrdimage.mjs new file mode 100644 index 000000000..5254b1e98 --- /dev/null +++ b/html/html/include/wui_rrdimage.mjs @@ -0,0 +1,30 @@ +/*############################################################################# +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Team info@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see http://www.gnu.org/licenses/. # +# # +#############################################################################*/
+// IPFire Web User Interface - JavaScript module
+import {WUIcore_moduleBase as WUI_module} from "./wui_core.mjs";
+//--- RRDtool graph images --- +export class WUImodule_rrdimage extends WUI_module {
- constructor(translations) {
- super(translations);
- }
+} diff --git a/html/html/themes/ipfire/include/functions.pl b/html/html/themes/ipfire/include/functions.pl index cbd05d109..445597e51 100644 --- a/html/html/themes/ipfire/include/functions.pl +++ b/html/html/themes/ipfire/include/functions.pl @@ -113,7 +113,8 @@ print <<END;
<title>$headline - $title</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="shortcut icon" href="/favicon.ico" /> - <script type="text/javascript" src="/include/jquery.js"></script> + <script src="/include/jquery.js"></script> + <script src="/include/wui.js" type="module"></script> <script src="/include/rrdimage.js"></script>
$extrahead
2.37.1.windows.1
Hi Leo,
On 12/04/2023 09:14, Leo-Andres Hofmann wrote:
Hi Michael,
Am 11.04.2023 um 14:58 schrieb Michael Tremer:
Hello,
Thanks for the patchset.
It is a lot of code to look through indeed.
Yes, I'm aware this is a bit unannounced and a lot of code. Thank you for looking at it!
On 1 Apr 2023, at 15:43, Leo-Andres Hofmannhofmann@leo-andres.de wrote:
This patch is an effort to modernize and unify the WUI JavaScript. It will be possible to share e.g. translation strings and common functions between the different modules.
This uses native ES6 modules that are supported by all major browsers. Therefore, no change to the toolchain is necessary.
Signed-off-by: Leo-Andres Hofmannhofmann@leo-andres.de
Hi all,
this patchset brings new modern Javascript modules to the webinterface.
I suppose there is no way around this.
My thinking was: At the moment, the web interface is quite old-fashined and uses relatively little javascript, scattered in multiple files. But new features are still added on a fairly regular basis. So I think it makes sense to start a good structure now rather than later.
Is this all written from scratch? Did you base this on anything or use some existing code as inspiration?
This is written from scratch, in the sense that it is not just a modified copy of some existing library. However, I have read the documentation of other libraries before, and there are only so many reasonable ways to implement this. So I haven't tried to reinvent the wheel, and you'll certainly be able to find similarities to existing libraries. But the actual source code in this patchset is 100% my own work.
- The module.enable/readyState logic is inspired by jQuery's .ready
and the HTML DOM readyState property
- The i18n class is based on the work I did for pakfire.js and
extended to work with the module concept
- To learn how to create the CSS overlay window, I followed the
w3schools tutorial "Overlay"
I have done some changes on the dhcp.cgi and urlxlrator.cgi pages to replace bgcolor, which was deprecated in HTML 4.x and no longer supported at all in HTML 5, with its CSS equivalent approach. I used your CSS approach on the zone config cgi page as the basis for what I changed.
My plan was to go through the other cgi pages doing the same type of changes.
Should I hold on that until you have made this change to the CSS approach with the CSS Overlay?
Regards, Adolf.
- The async/await interface for the dialog box is inspired by jQuery's
deferred.then() interface for Ajax calls, but implemented with native JS
Are they any existing libraries that could be useful for example pulling in the translation?
I looked at a few popular libraries, but in my opinion they are too extensive and do not fit well with the existing web interface. (jquery.i18n, Polyglot, ...) For example, some libraries I found prefer to load the texts from a JSON file. This would result in additional language files parallel to the existing system. Besides, dynamically changing translations based on user agent settings may break the form field mapping: if ($cgiparams{'ACTION'} eq $Lang::tr{'save'} )... If we didn't have to pay attention to many specifics of the old CGIs, I would also prefer an existing library.
Best, Leo
-Michael
Still on my to-do/wish list:
- run make.sh lang
- convert pakfire.js to a module
- convert/rewrite refreshInetInfo.js, maybe add load displays to all interfaces in index.cgi
I hope this is in a presentable state to see where it is headed. Let me know what you think and as always thanks for reading/testing/reviewing :)
Best, Leo
config/etc/mime.types | 1 + html/html/include/wui.js | 47 ++++++ html/html/include/wui_core.mjs | 154 +++++++++++++++++++ html/html/include/wui_rrdimage.mjs | 30 ++++ html/html/themes/ipfire/include/functions.pl | 3 +- 5 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 html/html/include/wui.js create mode 100644 html/html/include/wui_core.mjs create mode 100644 html/html/include/wui_rrdimage.mjs
diff --git a/config/etc/mime.types b/config/etc/mime.types index af82d4886..bb3816db9 100644 --- a/config/etc/mime.types +++ b/config/etc/mime.types @@ -252,6 +252,7 @@ multipart/voice-message text/css css text/directory text/enriched +text/javascript mjs text/plain asc txt text/prs.lines.tag text/rfc822-headers diff --git a/html/html/include/wui.js b/html/html/include/wui.js new file mode 100644 index 000000000..e65924e29 --- /dev/null +++ b/html/html/include/wui.js @@ -0,0 +1,47 @@ +/*############################################################################# +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Teaminfo@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, seehttp://www.gnu.org/licenses/. # +# # +#############################################################################*/
+// IPFire Web User Interface +// Collection of JavaScript functions and modules (requires jQuery)
+import {WUIcore_i18n as WUI_i18n} from "./wui_core.mjs";
+import {WUImodule_rrdimage as WUI_rrdimage} from "./wui_rrdimage.mjs";
+//--- WUI main class --- +class WUImain {
- constructor() {
- //- Public properties -
- // Translation strings
- this.i18n = new WUI_i18n();
- //- Modules -
- // RRDtool graph images
- this.rrdimage = new WUI_rrdimage(this.i18n);
- //- Defaults -
- // These modules are available on every page:
- this.rrdimage.enabled = true;
- }
+}
+//### Initialize WUI ### +const wui = new WUImain(); +export default wui; diff --git a/html/html/include/wui_core.mjs b/html/html/include/wui_core.mjs new file mode 100644 index 000000000..b7b729396 --- /dev/null +++ b/html/html/include/wui_core.mjs @@ -0,0 +1,154 @@ +/*############################################################################# +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Teaminfo@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see<http://www. gnu.org/licenses/>. # +# # +#############################################################################*/
+// IPFire Web User Interface - JavaScript module +// Core functions & module helpers
+//--- Module template --- +// Make sure that overridden functions are still executed with super()! +export class WUIcore_moduleBase {
- //- Private properties -
- #enabled; // Activation state, disabled by default
- #readyState; // Loading state similar to Document.readyState
- #namespace; // Namespace derived from the class name (without "WUImod_" prefix)
- //- Class constructor -
- constructor(translations) {
- this.i18n = translations;
- this.#enabled = false;
- this.#readyState = "loading";
- this.#namespace = this.constructor.name.slice(10);
- }
- // Module activation state
- // Note: Because a module likely changes the DOM, it is not intended that it can
- // be cleanly disabled again. Disabling it anyway will trigger "_handleModuleDestroy".
- set enabled(state) {
- if(this.#enabled !== state) {
- this.#enabled = state;
- if(state) {
- this._handleModuleEnable();
- } else {
- this._handleModuleDestroy();
- }
- }
- }
- get enabled() {
- return this.#enabled;
- }
- // Module loading state
- // loading: Instance created
- // interactive: Module enabled, document not ready
- // complete: DOMContentLoaded/jQuery.ready event fired
- // destroyed: Module disabled, event handlers removed
- get readyState() {
- return this.#readyState;
- }
- // Module namespace
- get namespace() {
- return this.#namespace;
- }
- //### Protected properties ###
- // Module activation state event handlers
- _handleModuleEnable() {
- if(this.#readyState === "loading") {
- this.#readyState = "interactive";
- // Attach jQuery.ready event
- $(this._handleDOMReady.bind(this));
- }
- }
- _handleModuleDestroy() {
- this.#readyState = "destroyed";
- // Attempt to remove all event handlers of this module
- $("body").off(`.${this.namespace}`);
- }
- // DOMContentLoaded/jQuery.ready event handler
- // The module must be enabled for this event to be triggered once
- _handleDOMReady() {
- this.#readyState = "complete";
- }
- // Translations quick access in module namespace
- _i18n(key) {
- return this.i18n.get(key, this.namespace);
- }
+}
+//--- Simple translation strings helper --- +export class WUIcore_i18n {
- //- Private properties -
- #strings;
- //- Class constructor -
- constructor() {
- this.#strings = Object.create(null); //Object without prototypes
- }
- // Default module prefix for general translations
- get #mainModule() {
- return "wui";
- }
- // Get translation
- get(key, module = this.#mainModule) {
- const index = `${module}%${key}`;
- if(Object.hasOwn(this.#strings, index)) {
- return this.#strings[index];
- }
- return `(missing string '${key}' in '${module}')`;
- }
- // Add translation
- add(key, value, module = this.#mainModule) {
- if(typeof value === "string" || typeof value === "number") {
- this.#strings[`${module}%${key}`] = value;
- }
- }
- // Load key/translation JS object
- // Format: {"key": "translation"}
- load(translations, module = this.#mainModule) {
- if(translations instanceof Object) {
- Object.entries(translations).forEach(([key, value]) => {
- this.add(key, value, module);
- });
- }
- }
+}
+//--- Static utility functions --- +export class WUIcore_utilities {
- // Reload document (clears POST/GET data from history)
- static reloadDocument() {
- const url = window.location.origin + window.location.pathname;
- window.location.replace(url);
- }
+} diff --git a/html/html/include/wui_rrdimage.mjs b/html/html/include/wui_rrdimage.mjs new file mode 100644 index 000000000..5254b1e98 --- /dev/null +++ b/html/html/include/wui_rrdimage.mjs @@ -0,0 +1,30 @@ +/*############################################################################# +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Teaminfo@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, seehttp://www.gnu.org/licenses/. # +# # +#############################################################################*/
+// IPFire Web User Interface - JavaScript module
+import {WUIcore_moduleBase as WUI_module} from "./wui_core.mjs";
+//--- RRDtool graph images --- +export class WUImodule_rrdimage extends WUI_module {
- constructor(translations) {
- super(translations);
- }
+} diff --git a/html/html/themes/ipfire/include/functions.pl b/html/html/themes/ipfire/include/functions.pl index cbd05d109..445597e51 100644 --- a/html/html/themes/ipfire/include/functions.pl +++ b/html/html/themes/ipfire/include/functions.pl @@ -113,7 +113,8 @@ print <<END;
<title>$headline - $title</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="shortcut icon" href="/favicon.ico" /> - <script type="text/javascript" src="/include/jquery.js"></script> + <script src="/include/jquery.js"></script> + <script src="/include/wui.js" type="module"></script> <script src="/include/rrdimage.js"></script>
$extrahead
2.37.1.windows.1
Hi Adolf,
Thanks for the heads up!
Am 13.04.2023 um 13:42 schrieb Adolf Belka:
Hi Leo,
On 12/04/2023 09:14, Leo-Andres Hofmann wrote:
Hi Michael,
Am 11.04.2023 um 14:58 schrieb Michael Tremer:
Hello,
Thanks for the patchset.
It is a lot of code to look through indeed.
Yes, I'm aware this is a bit unannounced and a lot of code. Thank you for looking at it!
On 1 Apr 2023, at 15:43, Leo-Andres Hofmannhofmann@leo-andres.de wrote:
This patch is an effort to modernize and unify the WUI JavaScript. It will be possible to share e.g. translation strings and common functions between the different modules.
This uses native ES6 modules that are supported by all major browsers. Therefore, no change to the toolchain is necessary.
Signed-off-by: Leo-Andres Hofmannhofmann@leo-andres.de
Hi all,
this patchset brings new modern Javascript modules to the webinterface.
I suppose there is no way around this.
My thinking was: At the moment, the web interface is quite old-fashined and uses relatively little javascript, scattered in multiple files. But new features are still added on a fairly regular basis. So I think it makes sense to start a good structure now rather than later.
Is this all written from scratch? Did you base this on anything or use some existing code as inspiration?
This is written from scratch, in the sense that it is not just a modified copy of some existing library. However, I have read the documentation of other libraries before, and there are only so many reasonable ways to implement this. So I haven't tried to reinvent the wheel, and you'll certainly be able to find similarities to existing libraries. But the actual source code in this patchset is 100% my own work.
- The module.enable/readyState logic is inspired by jQuery's .ready
and the HTML DOM readyState property
- The i18n class is based on the work I did for pakfire.js and
extended to work with the module concept
- To learn how to create the CSS overlay window, I followed the
w3schools tutorial "Overlay"
I have done some changes on the dhcp.cgi and urlxlrator.cgi pages to replace bgcolor, which was deprecated in HTML 4.x and no longer supported at all in HTML 5, with its CSS equivalent approach. I used your CSS approach on the zone config cgi page as the basis for what I changed.
My plan was to go through the other cgi pages doing the same type of changes.
Should I hold on that until you have made this change to the CSS approach with the CSS Overlay?
I mostly add new things here, so I don't think this could be a problem. You can go ahead!
By the way, if you want to make things a bit easier for yourself: Check out the nth-child CSS selector: "tr:nth-child(2n+3)" With this, you can do the color assignment for odd/even rows entirely in CSS and remove all the uncessary "if ($id % 2) color1..., else color2..." Perl code. Then you only need to print a plain HTML table, everything else is done by the browser.
Best, Leo
Regards, Adolf.
- The async/await interface for the dialog box is inspired by
jQuery's deferred.then() interface for Ajax calls, but implemented with native JS
Are they any existing libraries that could be useful for example pulling in the translation?
I looked at a few popular libraries, but in my opinion they are too extensive and do not fit well with the existing web interface. (jquery.i18n, Polyglot, ...) For example, some libraries I found prefer to load the texts from a JSON file. This would result in additional language files parallel to the existing system. Besides, dynamically changing translations based on user agent settings may break the form field mapping: if ($cgiparams{'ACTION'} eq $Lang::tr{'save'} )... If we didn't have to pay attention to many specifics of the old CGIs, I would also prefer an existing library.
Best, Leo
-Michael
Still on my to-do/wish list:
- run make.sh lang
- convert pakfire.js to a module
- convert/rewrite refreshInetInfo.js, maybe add load displays to
all interfaces in index.cgi
I hope this is in a presentable state to see where it is headed. Let me know what you think and as always thanks for reading/testing/reviewing :)
Best, Leo
config/etc/mime.types | 1 + html/html/include/wui.js | 47 ++++++ html/html/include/wui_core.mjs | 154 +++++++++++++++++++ html/html/include/wui_rrdimage.mjs | 30 ++++ html/html/themes/ipfire/include/functions.pl | 3 +- 5 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 html/html/include/wui.js create mode 100644 html/html/include/wui_core.mjs create mode 100644 html/html/include/wui_rrdimage.mjs
diff --git a/config/etc/mime.types b/config/etc/mime.types index af82d4886..bb3816db9 100644 --- a/config/etc/mime.types +++ b/config/etc/mime.types @@ -252,6 +252,7 @@ multipart/voice-message text/css css text/directory text/enriched +text/javascript mjs text/plain asc txt text/prs.lines.tag text/rfc822-headers diff --git a/html/html/include/wui.js b/html/html/include/wui.js new file mode 100644 index 000000000..e65924e29 --- /dev/null +++ b/html/html/include/wui.js @@ -0,0 +1,47 @@ +/*#############################################################################
+# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Teaminfo@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, seehttp://www.gnu.org/licenses/. # +# # +#############################################################################*/
+// IPFire Web User Interface +// Collection of JavaScript functions and modules (requires jQuery)
+import {WUIcore_i18n as WUI_i18n} from "./wui_core.mjs";
+import {WUImodule_rrdimage as WUI_rrdimage} from "./wui_rrdimage.mjs";
+//--- WUI main class --- +class WUImain {
- constructor() {
- //- Public properties -
- // Translation strings
- this.i18n = new WUI_i18n();
- //- Modules -
- // RRDtool graph images
- this.rrdimage = new WUI_rrdimage(this.i18n);
- //- Defaults -
- // These modules are available on every page:
- this.rrdimage.enabled = true;
- }
+}
+//### Initialize WUI ### +const wui = new WUImain(); +export default wui; diff --git a/html/html/include/wui_core.mjs b/html/html/include/wui_core.mjs new file mode 100644 index 000000000..b7b729396 --- /dev/null +++ b/html/html/include/wui_core.mjs @@ -0,0 +1,154 @@ +/*#############################################################################
+# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Teaminfo@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see<http://www. gnu.org/licenses/>. # +# # +#############################################################################*/
+// IPFire Web User Interface - JavaScript module +// Core functions & module helpers
+//--- Module template --- +// Make sure that overridden functions are still executed with super()! +export class WUIcore_moduleBase {
- //- Private properties -
- #enabled; // Activation state, disabled by default
- #readyState; // Loading state similar to Document.readyState
- #namespace; // Namespace derived from the class name (without
"WUImod_" prefix)
- //- Class constructor -
- constructor(translations) {
- this.i18n = translations;
- this.#enabled = false;
- this.#readyState = "loading";
- this.#namespace = this.constructor.name.slice(10);
- }
- // Module activation state
- // Note: Because a module likely changes the DOM, it is not
intended that it can
- // be cleanly disabled again. Disabling it anyway will trigger
"_handleModuleDestroy".
- set enabled(state) {
- if(this.#enabled !== state) {
- this.#enabled = state;
- if(state) {
- this._handleModuleEnable();
- } else {
- this._handleModuleDestroy();
- }
- }
- }
- get enabled() {
- return this.#enabled;
- }
- // Module loading state
- // loading: Instance created
- // interactive: Module enabled, document not ready
- // complete: DOMContentLoaded/jQuery.ready event fired
- // destroyed: Module disabled, event handlers removed
- get readyState() {
- return this.#readyState;
- }
- // Module namespace
- get namespace() {
- return this.#namespace;
- }
- //### Protected properties ###
- // Module activation state event handlers
- _handleModuleEnable() {
- if(this.#readyState === "loading") {
- this.#readyState = "interactive";
- // Attach jQuery.ready event
- $(this._handleDOMReady.bind(this));
- }
- }
- _handleModuleDestroy() {
- this.#readyState = "destroyed";
- // Attempt to remove all event handlers of this module
- $("body").off(`.${this.namespace}`);
- }
- // DOMContentLoaded/jQuery.ready event handler
- // The module must be enabled for this event to be triggered once
- _handleDOMReady() {
- this.#readyState = "complete";
- }
- // Translations quick access in module namespace
- _i18n(key) {
- return this.i18n.get(key, this.namespace);
- }
+}
+//--- Simple translation strings helper --- +export class WUIcore_i18n {
- //- Private properties -
- #strings;
- //- Class constructor -
- constructor() {
- this.#strings = Object.create(null); //Object without prototypes
- }
- // Default module prefix for general translations
- get #mainModule() {
- return "wui";
- }
- // Get translation
- get(key, module = this.#mainModule) {
- const index = `${module}%${key}`;
- if(Object.hasOwn(this.#strings, index)) {
- return this.#strings[index];
- }
- return `(missing string '${key}' in '${module}')`;
- }
- // Add translation
- add(key, value, module = this.#mainModule) {
- if(typeof value === "string" || typeof value === "number") {
- this.#strings[`${module}%${key}`] = value;
- }
- }
- // Load key/translation JS object
- // Format: {"key": "translation"}
- load(translations, module = this.#mainModule) {
- if(translations instanceof Object) {
- Object.entries(translations).forEach(([key, value]) => {
- this.add(key, value, module);
- });
- }
- }
+}
+//--- Static utility functions --- +export class WUIcore_utilities {
- // Reload document (clears POST/GET data from history)
- static reloadDocument() {
- const url = window.location.origin + window.location.pathname;
- window.location.replace(url);
- }
+} diff --git a/html/html/include/wui_rrdimage.mjs b/html/html/include/wui_rrdimage.mjs new file mode 100644 index 000000000..5254b1e98 --- /dev/null +++ b/html/html/include/wui_rrdimage.mjs @@ -0,0 +1,30 @@ +/*#############################################################################
+# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2023 IPFire Teaminfo@ipfire.org # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, seehttp://www.gnu.org/licenses/. # +# # +#############################################################################*/
+// IPFire Web User Interface - JavaScript module
+import {WUIcore_moduleBase as WUI_module} from "./wui_core.mjs";
+//--- RRDtool graph images --- +export class WUImodule_rrdimage extends WUI_module {
- constructor(translations) {
- super(translations);
- }
+} diff --git a/html/html/themes/ipfire/include/functions.pl b/html/html/themes/ipfire/include/functions.pl index cbd05d109..445597e51 100644 --- a/html/html/themes/ipfire/include/functions.pl +++ b/html/html/themes/ipfire/include/functions.pl @@ -113,7 +113,8 @@ print <<END;
<title>$headline - $title</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="shortcut icon" href="/favicon.ico" /> - <script type="text/javascript" src="/include/jquery.js"></script> + <script src="/include/jquery.js"></script> + <script src="/include/wui.js" type="module"></script> <script src="/include/rrdimage.js"></script>
$extrahead
2.37.1.windows.1