From 5a3897790662564d31d42fa313ae44159008b408 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 12 Nov 2020 14:33:29 +0100 Subject: [PATCH] js: Add `define.js` --- library/Icinga/Web/JavaScript.php | 24 +++++-- public/js/define.js | 102 ++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 5 deletions(-) create mode 100644 public/js/define.js diff --git a/library/Icinga/Web/JavaScript.php b/library/Icinga/Web/JavaScript.php index 54f28453d..56c0bf982 100644 --- a/library/Icinga/Web/JavaScript.php +++ b/library/Icinga/Web/JavaScript.php @@ -14,7 +14,7 @@ class JavaScript /** @var string */ const DEFINE_RE = '/(?)/'; - protected static $jsFiles = array( + protected static $jsFiles = [ 'js/helpers.js', 'js/icinga.js', 'js/icinga/logger.js', @@ -41,12 +41,16 @@ class JavaScript 'js/icinga/behavior/filtereditor.js', 'js/icinga/behavior/selectable.js', 'js/icinga/behavior/modal.js' - ); + ]; - protected static $vendorFiles = array( + protected static $vendorFiles = [ 'js/vendor/jquery-3.4.1', 'js/vendor/jquery-migrate-3.1.0' - ); + ]; + + protected static $baseFiles = [ + 'js/define.js' + ]; public static function sendMinified() { @@ -74,6 +78,12 @@ class JavaScript $vendorFiles[] = $basedir . '/' . $file . $min . '.js'; } + // Prepare base file list + $baseFiles = []; + foreach (self::$baseFiles as $file) { + $baseFiles[] = $basedir . '/' . $file; + } + // Prepare library file list $libraryFiles = []; foreach (Icinga::app()->getLibraries() as $library) { @@ -107,7 +117,7 @@ class JavaScript } $sharedFiles = array_unique($sharedFiles); - $files = array_merge($vendorFiles, $libraryFiles, $jsFiles, $sharedFiles); + $files = array_merge($vendorFiles, $baseFiles, $libraryFiles, $jsFiles, $sharedFiles); $request = Icinga::app()->getRequest(); $noCache = $request->getHeader('Cache-Control') === 'no-cache' || $request->getHeader('Pragma') === 'no-cache'; @@ -134,6 +144,10 @@ class JavaScript $out .= ';' . ltrim(trim(file_get_contents($file)), ';') . "\n"; } + foreach ($baseFiles as $file) { + $js .= file_get_contents($file) . "\n\n\n"; + } + // Library files need to be namespaced first before they can be included foreach (Icinga::app()->getLibraries() as $library) { foreach ($library->getJsAssets() as $file) { diff --git a/public/js/define.js b/public/js/define.js new file mode 100644 index 000000000..059e3ef80 --- /dev/null +++ b/public/js/define.js @@ -0,0 +1,102 @@ +/*! Icinga Web 2 | (c) 2020 Icinga GmbH | GPLv2+ */ + +(function(window) { + + 'use strict'; + + /** + * Provide a reference to be later required by foreign code + * + * @param {string} name Optional, defaults to the name (and path) of the file + * @param {string[]} requirements Optional, list of required references, may be relative if from the same package + * @param {function} factory Required, function that accepts as many params as there are requirements and that + * produces a value to be referenced + */ + var define = function (name, requirements, factory) { + define.defines[name] = { + requirements: requirements, + factory: factory, + ref: null + } + + define.resolve(name); + } + + /** + * Return whether the given name references a value + * + * @param {string} name The absolute name of the reference + * @return {boolean} + */ + define.has = function (name) { + return name in define.defines && define.defines[name]['ref'] !== null; + } + + /** + * Get the value of a reference + * + * @param {string} name The absolute name of the reference + * @return {*} + */ + define.get = function (name) { + return define.defines[name]['ref']; + } + + /** + * Set the value of a reference + * + * @param {string} name The absolute name of the reference + * @param {*} ref The value to reference + */ + define.set = function (name, ref) { + define.defines[name]['ref'] = ref; + } + + /** + * Resolve a reference and, if successful, dependent references + * + * @param {string} name The absolute name of the reference + * @return {boolean} + */ + define.resolve = function (name) { + var requirements = define.defines[name]['requirements']; + if (requirements.filter(define.has).length < requirements.length) { + return false; + } + + var requiredRefs = []; + for (var i = 0; i < requirements.length; i++) { + requiredRefs.push(define.get(requirements[i])); + } + + var factory = define.defines[name]['factory']; + define.set(name, factory.apply(null, requiredRefs)); + + for (var definedName in define.defines) { + if (define.defines[definedName]['requirements'].indexOf(name) >= 0) { + define.resolve(definedName); + } + } + } + + /** + * Require a reference + * + * @param {string} name The absolute name of the reference + * @return {*} + */ + var require = function(name) { + if (define.has(name)) { + return define.get(name); + } + + throw new ReferenceError(name + ' is not defined'); + } + + define.icinga = true; + define.defines = {}; + + window.define = define; + window.require = require; + +})(window);