From af6a21438e640bbfe22247c07ecc9e1771f75a17 Mon Sep 17 00:00:00 2001 From: Felix Hamann Date: Wed, 3 Apr 2019 23:21:12 +0200 Subject: [PATCH] add new JS utility registry and proof-of-concept utility --- src/Foundation.hs | 27 ++++---- static/js/utils/poc.js | 33 +++++++++ static/js/utils/registry.js | 109 ++++++++++++++++++++++++++++++ static/js/utils/setup.js | 114 -------------------------------- templates/default-layout.hamlet | 2 +- 5 files changed, 157 insertions(+), 128 deletions(-) create mode 100644 static/js/utils/poc.js create mode 100644 static/js/utils/registry.js delete mode 100644 static/js/utils/setup.js diff --git a/src/Foundation.hs b/src/Foundation.hs index 0d8e5d909..a15dd5a9b 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -1065,19 +1065,20 @@ siteLayout' headingOverride widget = do addScript $ StaticR js_polyfills_fetchPolyfill_js addScript $ StaticR js_polyfills_urlPolyfill_js -- JavaScript utils - addScript $ StaticR js_utils_alerts_js - addScript $ StaticR js_utils_asidenav_js - addScript $ StaticR js_utils_asyncForm_js - addScript $ StaticR js_utils_asyncTable_js - addScript $ StaticR js_utils_asyncTableFilter_js - addScript $ StaticR js_utils_checkAll_js - addScript $ StaticR js_utils_httpClient_js - addScript $ StaticR js_utils_form_js - addScript $ StaticR js_utils_inputs_js - addScript $ StaticR js_utils_modal_js - addScript $ StaticR js_utils_setup_js - addScript $ StaticR js_utils_showHide_js - addScript $ StaticR js_utils_tabber_js + -- addScript $ StaticR js_utils_alerts_js + -- addScript $ StaticR js_utils_asidenav_js + -- addScript $ StaticR js_utils_asyncForm_js + -- addScript $ StaticR js_utils_asyncTable_js + -- addScript $ StaticR js_utils_asyncTableFilter_js + -- addScript $ StaticR js_utils_checkAll_js + -- addScript $ StaticR js_utils_httpClient_js + -- addScript $ StaticR js_utils_form_js + -- addScript $ StaticR js_utils_inputs_js + -- addScript $ StaticR js_utils_modal_js + addScript $ StaticR js_utils_registry_js + addScript $ StaticR js_utils_poc_js + -- addScript $ StaticR js_utils_showHide_js + -- addScript $ StaticR js_utils_tabber_js addStylesheet $ StaticR css_utils_alerts_scss addStylesheet $ StaticR css_utils_asidenav_scss addStylesheet $ StaticR css_utils_asyncForm_scss diff --git a/static/js/utils/poc.js b/static/js/utils/poc.js new file mode 100644 index 000000000..5d87a0f82 --- /dev/null +++ b/static/js/utils/poc.js @@ -0,0 +1,33 @@ +(function() { + + var UTIL_NAME = 'poc'; + var UTIL_SELECTOR = '[uw-poc]'; + + var util = function(element) { + + function _init() { + var color = 'red'; + if (element.dataset.color) { + color = element.dataset.color; + } + element.style.outline = '1px solid ' + color; + } + + _init(); + + return { + name: UTIL_NAME, + element: element, + destroy: function() {}, + }; + }; + + if (UtilRegistry) { + UtilRegistry.register({ + name: UTIL_NAME, + selector: UTIL_SELECTOR, + setup: util, + }); + } + +})(); diff --git a/static/js/utils/registry.js b/static/js/utils/registry.js new file mode 100644 index 000000000..775b58fe4 --- /dev/null +++ b/static/js/utils/registry.js @@ -0,0 +1,109 @@ +(function() { + 'use strict'; + + var registeredUtils = []; + var activeUtilInstances = []; + + // Registry + // (revealing module pattern) + window.UtilRegistry = (function() { + + /** + * function registerUtil + * + * utils need to have at least these properties: + * name: string | utils name, e.g. 'example' + * selector: string | utils selector, e.g. '[uw-example]' + * setup: Function | utils setup function, see below + * + * setup function must return instance object with at least these properties: + * name: string | utils name + * element: HTMLElement | element the util is applied to + * destroy: Function | function to destroy the util and remove any listeners + * + * @param util Object Utility that should be added to the registry + */ + function registerUtil(util) { + console.log('registering util', util); + registeredUtils.push(util); + } + + function deregisterUtil(name, destroy) { + var utilIndex = _findUtilIndex(name); + + if (utilIndex >= 0) { + if (destroy === true) { + _destroyUtilInstances(name); + } + + registeredUtils.splice(utilIndex, 1); + } + } + + function setupAllUtils() { + console.log('setting up all registered utils'); + registeredUtils.forEach(function(util) { + setupUtil(util); + }); + } + + function setupUtil(util, scope) { + console.log('setting up util', { util }); + scope = scope || document; + + if (util && typeof util.setup === 'function') { + const elements = _findUtilElements(util, scope); + + elements.forEach(function(element) { + var utilInstance = util.setup(element); + if (utilInstance) { + activeUtilInstances.push(utilInstance); + } + }); + } + } + + function findUtil(name) { + return registeredUtils.find(function(util) { + return util.name === name; + }); + } + + function _findUtilElements(util, scope) { + return Array.from(scope.querySelectorAll(util.selector)); + } + + function _findUtilIndex(name) { + return registeredUtils.findIndex(function(util) { + return util.name === name; + }); + } + + function _destroyUtilInstances(name) { + console.log('TODO: destroy util instances', { name }); + // call destroy on each activeUtilInstance that matches the name + } + + // public API + return { + register: registerUtil, + deregister: deregisterUtil, + setupAll: setupAllUtils, + setup: setupUtil, + find: findUtil, + } + })(); + + document.addEventListener('DOMContentLoaded', function() { + window.UtilRegistry.setupAll(); + }); + + + // REMOVE ME. JUST HERE TO AVOID JS ERRORS + window.utils = { + setup: function(name) { + console.log('not really setting up', name); + }, + }; + +})(); diff --git a/static/js/utils/setup.js b/static/js/utils/setup.js deleted file mode 100644 index bb3bb0e3d..000000000 --- a/static/js/utils/setup.js +++ /dev/null @@ -1,114 +0,0 @@ -(function() { - 'use strict'; - - window.utils = window.utils || {}; - - var registeredSetupListeners = {}; - var activeInstances = {}; - -/** - * setup function to initiate a util (utilName) on a scope (sope) with options (options). - * - * Utils need to be defined as property of `window.utils` and need to accept a scope and (optionally) options. - * Example: `window.utils.autoSubmit = function(scope, options) { ... };` - */ - - window.utils.setup = function(utilName, scope, options) { - if (!utilName || !scope) { - return; - } - - options = options || {}; - - var utilInstance; - - // i18n - if (window.I18N) { - options.i18n = window.I18N; - } - - if (activeInstances[utilName]) { - var instanceWithSameScope = activeInstances[utilName] - .filter(function(instance) { return !!instance; }) - .find(function(instance) { - return instance.scope === scope; - }); - var isAlreadySetup = !!instanceWithSameScope; - - if (isAlreadySetup) { - console.warn('Trying to setup a JS utility that\'s already been set up', { utility: utilName, scope, options }); - if (!options.force) { - return false; - } - } - } - - function setup() { - var listener = function(event) { - if (event.detail.targetUtil !== utilName) { - return false; - } - - if (options.setupFunction) { - utilInstance = options.setupFunction(scope, options); - } else { - var util = window.utils[utilName]; - if (!util) { - throw new Error('"' + utilName + '" is not a known js util'); - } - - utilInstance = util(scope, options); - } - - if (utilInstance) { - if (activeInstances[utilName] && Array.isArray(activeInstances[utilName])) { - activeInstances[utilName].push(utilInstance); - } else { - activeInstances[utilName] = [ utilInstance ]; - } - } - }; - - if (registeredSetupListeners[utilName] && Array.isArray(registeredSetupListeners[utilName])) { - window.utils.teardown(utilName); - } - - if (!registeredSetupListeners[utilName] || Array.isArray(registeredSetupListeners[utilName])) { - registeredSetupListeners[utilName] = []; - } - registeredSetupListeners[utilName].push(listener); - - document.addEventListener('setup', listener); - - document.dispatchEvent(new CustomEvent('setup', { - detail: { targetUtil: utilName, module: 'none' }, - bubbles: true, - cancelable: true, - })); - } - - setup(); - - return utilInstance; - }; - - window.utils.teardown = function(utilName, destroy) { - if (registeredSetupListeners[utilName]) { - registeredSetupListeners[utilName] - .filter(function(listener) { return !!listener }) - .forEach(function(listener) { - document.removeEventListener('setup', listener); - }); - delete registeredSetupListeners[utilName]; - } - - if (destroy === true && activeInstances[utilName]) { - activeInstances[utilName] - .filter(function(instance) { return !!instance }) - .forEach(function(instance) { - instance.destroy(); - }); - delete activeInstances[utilName]; - } - } -})(); diff --git a/templates/default-layout.hamlet b/templates/default-layout.hamlet index f43282fb0..7a679af27 100644 --- a/templates/default-layout.hamlet +++ b/templates/default-layout.hamlet @@ -6,7 +6,7 @@ $if not isModal
-
+
$if not isModal