Files
OfficialSite/assets/scripts/app.js

162 lines
4.7 KiB
JavaScript

import { APP_NAME, $document, $pjaxWrapper } from './utils/environment';
import globals from './globals';
import { arrayContains, removeFromArray } from './utils/array';
import { getNodeData } from './utils/html';
import { isFunction } from './utils/is';
// Basic modules
import * as modules from './modules';
const MODULE_NAME = 'App';
const EVENT_NAMESPACE = `${APP_NAME}.${MODULE_NAME}`;
export const EVENT = {
INIT_MODULES: `initModules.${EVENT_NAMESPACE}`,
INIT_SCOPED_MODULES: `initScopedModules.${EVENT_NAMESPACE}`,
DELETE_SCOPED_MODULES: `deleteScopedModules.${EVENT_NAMESPACE}`
};
class App {
constructor() {
this.modules = modules;
this.currentModules = [];
$document.on(EVENT.INIT_MODULES, (event) => {
this.initGlobals(event.firstBlood)
.deleteModules(event)
.initModules(event);
});
$document.on(EVENT.INIT_SCOPED_MODULES, (event) => {
this.initModules(event);
});
$document.on(EVENT.DELETE_SCOPED_MODULES, (event) => {
this.deleteModules(event);
});
}
/**
* Destroy all existing modules or a specific scope of modules
* @param {Object} event The event being triggered.
* @return {Object} Self (allows chaining)
*/
deleteModules(event) {
let destroyAll = true;
let moduleIds = [];
// Check for scope first
if (event.$scope instanceof jQuery && event.$scope.length > 0) {
// Modules within scope
const $modules = event.$scope.find('[data-module]');
// Determine their uids
moduleIds = $.makeArray($modules.map(function(index) {
return $modules.eq(index).data('uid');
}));
if (moduleIds.length > 0) {
destroyAll = false;
} else {
return this;
}
}
// Loop modules and destroying all of them, or specific ones
let i = this.currentModules.length;
while (i--) {
if (destroyAll || arrayContains(moduleIds, this.currentModules[i].uid)) {
removeFromArray(moduleIds, this.currentModules[i].uid);
this.currentModules[i].destroy();
this.currentModules.splice(i, 1);
}
}
return this;
}
/**
* Execute global functions and settings
* Allows you to initialize global modules only once if you need
* (ex.: when using Barba.js or SmoothState.js)
* @return {Object} Self (allows chaining)
*/
initGlobals(firstBlood) {
globals(firstBlood);
return this;
}
/**
* Find modules and initialize them
* @param {Object} event The event being triggered.
* @return {Object} Self (allows chaining)
*/
initModules(event) {
// Elements with module
let $moduleEls = [];
// If first blood, load all modules in the DOM
// If scoped, render elements with modules
// If Barba, load modules contained in Barba container
if (event.firstBlood) {
$moduleEls = $document.find('[data-module]');
} else if (event.$scope instanceof jQuery && event.$scope.length > 0) {
$moduleEls = event.$scope.find('[data-module]');
} else if (event.isPjax) {
$moduleEls = $pjaxWrapper.find('[data-module]');
}
// Loop through elements
let i = 0;
const elsLen = $moduleEls.length;
for (; i < elsLen; i++) {
// Current element
let el = $moduleEls[i];
// All data- attributes considered as options
let options = getNodeData(el);
// Add current DOM element and jQuery element
options.el = el;
options.$el = $moduleEls.eq(i);
// Module does exist at this point
let attr = options.module;
// Splitting modules found in the data-attribute
let moduleIdents = attr.split(/[,\s]+/g);
// Loop modules
let j = 0;
let modulesLen = moduleIdents.length;
for (; j < modulesLen; j++) {
let moduleAttr = moduleIdents[j];
if (typeof this.modules[moduleAttr] === 'function') {
let module = new this.modules[moduleAttr](options);
this.currentModules.push(module);
module.init();
}
}
}
return this;
}
}
// IIFE for loading the application
// ==========================================================================
(function() {
new App();
$document.triggerHandler({
type: EVENT.INIT_MODULES,
firstBlood: true
});
})();