173 lines
5.6 KiB
JavaScript
173 lines
5.6 KiB
JavaScript
/**
|
|
* A component handler interface using the revealing module design pattern.
|
|
* More details on this pattern design here:
|
|
* https://github.com/jasonmayes/wsk-component-design-pattern
|
|
* @author Jason Mayes.
|
|
*/
|
|
/* exported componentHandler */
|
|
var componentHandler = (function() {
|
|
'use strict';
|
|
|
|
var registeredComponents_ = [];
|
|
var createdComponents_ = [];
|
|
|
|
/**
|
|
* Searches registered components for a class we are interested in using.
|
|
* Optionally replaces a match with passed object if specified.
|
|
* @param {string} name The name of a class we want to use.
|
|
* @param {object} optReplace Optional object to replace match with.
|
|
* @return {object | false}
|
|
* @private
|
|
*/
|
|
function findRegisteredClass_(name, optReplace) {
|
|
for (var i = 0; i < registeredComponents_.length; i++) {
|
|
if (registeredComponents_[i].className === name) {
|
|
if (optReplace !== undefined) {
|
|
registeredComponents_[i] = optReplace;
|
|
}
|
|
return registeredComponents_[i];
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Searches existing DOM for elements of our component type and upgrades them
|
|
* if they have not already been upgraded.
|
|
* @param {string} jsClass the programatic name of the element class we need
|
|
* to create a new instance of.
|
|
* @param {string} cssClass the name of the CSS class elements of this type
|
|
* will have.
|
|
*/
|
|
function upgradeDomInternal(jsClass, cssClass) {
|
|
if (jsClass === undefined && cssClass === undefined) {
|
|
for (var i = 0; i < registeredComponents_.length; i++) {
|
|
upgradeDomInternal(registeredComponents_[i].className,
|
|
registeredComponents_[i].cssClass);
|
|
}
|
|
} else {
|
|
if (cssClass === undefined) {
|
|
var registeredClass = findRegisteredClass_(jsClass);
|
|
if (registeredClass) {
|
|
cssClass = registeredClass.cssClass;
|
|
}
|
|
}
|
|
|
|
var elements = document.querySelectorAll('.' + cssClass);
|
|
for (var n = 0; n < elements.length; n++) {
|
|
upgradeElementInternal(elements[n], jsClass);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Upgrades a specific element rather than all in the DOM.
|
|
* @param {HTMLElement} element The element we wish to upgrade.
|
|
* @param {string} jsClass The name of the class we want to upgrade
|
|
* the element to.
|
|
*/
|
|
function upgradeElementInternal(element, jsClass) {
|
|
// Only upgrade elements that have not already been upgraded.
|
|
var dataUpgraded = element.getAttribute('data-upgraded');
|
|
|
|
if (dataUpgraded === null || dataUpgraded.indexOf(jsClass) === -1) {
|
|
// Upgrade element.
|
|
if (dataUpgraded === null) {
|
|
dataUpgraded = '';
|
|
}
|
|
element.setAttribute('data-upgraded', dataUpgraded + ',' + jsClass);
|
|
var registeredClass = findRegisteredClass_(jsClass);
|
|
if (registeredClass) {
|
|
createdComponents_.push(new registeredClass.classConstructor(element));
|
|
// Call any callbacks the user has registered with this component type.
|
|
registeredClass.callbacks.forEach(function (callback) {
|
|
callback(element);
|
|
});
|
|
} else {
|
|
// If component creator forgot to register, try and see if
|
|
// it is in global scope.
|
|
createdComponents_.push(new window[jsClass](element));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Registers a class for future use and attempts to upgrade existing DOM.
|
|
* @param {object} config An object containing:
|
|
* {constructor: Constructor, classAsString: string, cssClass: string}
|
|
*/
|
|
function registerInternal(config) {
|
|
var newConfig = {
|
|
'classConstructor': config.constructor,
|
|
'className': config.classAsString,
|
|
'cssClass': config.cssClass,
|
|
'callbacks': []
|
|
};
|
|
|
|
var found = findRegisteredClass_(config.classAsString, newConfig);
|
|
|
|
if (!found) {
|
|
registeredComponents_.push(newConfig);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Allows user to be alerted to any upgrades that are performed for a given
|
|
* component type
|
|
* @param {string} jsClass The class name of the WSK component we wish
|
|
* to hook into for any upgrades performed.
|
|
* @param {function} callback The function to call upon an upgrade. This
|
|
* function should expect 1 parameter - the HTMLElement which got upgraded.
|
|
*/
|
|
function registerUpgradedCallbackInternal(jsClass, callback) {
|
|
var regClass = findRegisteredClass_(jsClass);
|
|
if (regClass) {
|
|
regClass.callbacks.push(callback);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Upgrades all registered components found in the current DOM. This is
|
|
* automatically called on window load.
|
|
*/
|
|
function upgradeAllRegisteredInternal() {
|
|
for (var n = 0; n < registeredComponents_.length; n++) {
|
|
upgradeDomInternal(registeredComponents_[n].className);
|
|
}
|
|
}
|
|
|
|
|
|
// Now return the functions that should be made public with their publicly
|
|
// facing names...
|
|
return {
|
|
upgradeDom: upgradeDomInternal,
|
|
upgradeElement: upgradeElementInternal,
|
|
upgradeAllRegistered: upgradeAllRegisteredInternal,
|
|
registerUpgradedCallback: registerUpgradedCallbackInternal,
|
|
register: registerInternal
|
|
};
|
|
})();
|
|
|
|
|
|
window.addEventListener('load', function() {
|
|
'use strict';
|
|
|
|
/**
|
|
* Performs a "Cutting the mustard" test. If the browser supports the features
|
|
* tested, adds a wsk-js class to the <html> element. It then upgrades all WSK
|
|
* components requiring JavaScript.
|
|
*/
|
|
if ('classList' in document.createElement('div') && 'querySelector' in document &&
|
|
'addEventListener' in window && Array.prototype.forEach) {
|
|
document.documentElement.classList.add('wsk-js');
|
|
componentHandler.upgradeAllRegistered();
|
|
} else {
|
|
componentHandler.upgradeElement = componentHandler.register = function () { };
|
|
}
|
|
});
|