unclean SPARC
This commit is contained in:
Executable
+566
@@ -0,0 +1,566 @@
|
||||
'use strict'
|
||||
/**
|
||||
___ ____ __ ____ ___
|
||||
/ __)( _ \ /__\ ( _ \ / __)
|
||||
___\__ \ )___//(__)\ ) /( (__
|
||||
(_______/(__) (__)(__)(_)\_) \___) SPA Rational Code
|
||||
By Mike & Nike
|
||||
|
||||
This file is part of Sparc by Mike & Nike.
|
||||
Sparc 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.
|
||||
- Sparc 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.
|
||||
Get your copy of the GNU General Public License at <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
* @category Core
|
||||
* @hideconstructor
|
||||
* @tutorial app-01-overview
|
||||
*/
|
||||
class Sparc {
|
||||
/**
|
||||
* Genaral app configuration, as soon as it is loaded
|
||||
* @tutorial app-02-configuration
|
||||
* */
|
||||
config = {};
|
||||
/** Classes definitions for MVC + Sparc libs follow the rule script-name = instantiable class name */
|
||||
LoadedClasses = {};
|
||||
/** Thirdparties and other libs don't necessarily have a class, or this naming conv. then store path only. */
|
||||
LoadedScripts = [];
|
||||
/** @type User */
|
||||
User = null
|
||||
/** @type Assets */
|
||||
Assets = null
|
||||
/** @type Router */
|
||||
Router = null
|
||||
/** @type Events */
|
||||
events = null
|
||||
/** @type Array<Object> */
|
||||
latestErrors = [];
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} ref
|
||||
* @param {*} cls
|
||||
* @param {*} replace
|
||||
*/
|
||||
registerClass(ref, cls, replace=false) {
|
||||
if(replace || (!this.LoadedClasses.hasOwnProperty(ref))){
|
||||
this.LoadedClasses[ref] = cls;
|
||||
} else {
|
||||
console.warn('Attempting to register already defined class ', ref);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
onBootReady() {
|
||||
console.log('Sparc boot loaded...');
|
||||
|
||||
// Instances shortcuts
|
||||
this.User = new this.LoadedClasses.User(); // This is User base class (no login)
|
||||
|
||||
// Static classes shortcuts
|
||||
this.Assets = Assets;
|
||||
|
||||
// General app config are loaded here
|
||||
this.Assets.loadJson({ 'name': 'config.json',
|
||||
'path': '/app/config/',
|
||||
}).then(this.onConfigLoaded.bind(this));
|
||||
// Instances shortcuts
|
||||
this.events = new this.LoadedClasses.Events(); // This is User base class (no login)
|
||||
this.addEvent = this.events.addEvent.bind(this.events);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
onWindowReady() {
|
||||
// MVC not yet loaded here, see remark below about loading base classes.
|
||||
Loader.loadScripts({ 'basepath':'/core/libs/',
|
||||
'scripts': ['Router', 'Logger', 'Assets','Events'],
|
||||
'dependencies':{ 'Router': ['../baseClasses/User'] },
|
||||
}).then(this.onBootReady.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} config
|
||||
*/
|
||||
onConfigLoaded(config){
|
||||
console.log('App config loaded...');
|
||||
if(!config) {
|
||||
console.error('Could not load SPARC config file !?');
|
||||
return;
|
||||
}
|
||||
this.config = config;
|
||||
if(('logger' in config) && ('enabled' in config.logger) && (config.logger.enabled)
|
||||
&& ('levels' in config.logger) && ('postUrl' in config.logger)){
|
||||
window.console = this.logger(window.console, config.logger);
|
||||
}
|
||||
|
||||
this.Assets.defaults = config.assets;
|
||||
|
||||
if( ('userLib' in config) && ('className' in config.userLib) && (config.userLib.className!='')) {
|
||||
Loader.loadScripts({ 'basepath':'/app/libs/',
|
||||
'scripts': [config.userLib.className],
|
||||
'dependencies':{ }, // Dependencies of the User class from config ?
|
||||
}).then(()=>{
|
||||
this.User = new this.LoadedClasses.User(); // Re-instantiate extnded class
|
||||
// Upon creation, we are ourselves, but it might change later. (no OTS yet) Philosophical, isn't it :D
|
||||
this.currentUser = this.User;
|
||||
this.onUserClassReady();
|
||||
});
|
||||
} else {
|
||||
this.onUserClassReady();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
onUserClassReady(){
|
||||
this.User.checkAuthenticated(()=>{
|
||||
if(this.User.isAuthenticated) {
|
||||
window.onbeforeunload = () => "Do you really want to leave EISMEA application ?";
|
||||
|
||||
// Loading base Classes, who can now use app.config.
|
||||
// If later we do multi-app then one config per app, and baseclass instantiated with its own config.
|
||||
Loader.loadScripts({
|
||||
'basepath':'/core/baseClasses/',
|
||||
'scripts': [
|
||||
'Model',
|
||||
'View',
|
||||
'MasterController'
|
||||
], // Look momy: sounds just like MVC ;-)
|
||||
'dependencies': {
|
||||
'MasterController': ['Controller']
|
||||
}, // Dependencies of the base classes ?
|
||||
}).then(this.loadLibraries.bind(this));
|
||||
|
||||
// Now load & start messageBus (must be authenticated, but can by done aside of loading the MVC core)
|
||||
if((this.config.messageBus.enabled) && ("Worker" in window) && ('WebSocket' in window)) {
|
||||
Loader.loadScripts({ 'basepath':'/core/libs/',
|
||||
'scripts': ['MessageBus'],
|
||||
}).then(() => {
|
||||
this.MessageBus = new this.LoadedClasses.MessageBus(this.config.messageBus, this.User.getMessageBusUserInfo());
|
||||
});
|
||||
} else if(this.config.messageBus.enabled){
|
||||
console.warn("Could not register MessageBus worker: Browser too old ?");
|
||||
}
|
||||
} else {
|
||||
console.log('User is not Authenticated, redirect to Login page');
|
||||
this.User.gotoLogin();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
loadLibraries(){
|
||||
// Loading custom libs & intermediary classes
|
||||
let allPromises = [];
|
||||
let lib, libclass;
|
||||
if('libsBaseClasses' in this.config.router){
|
||||
for(lib of this.config.router.libsBaseClasses) {
|
||||
if(Array.isArray(lib.onlyIfClasses) && !lib.onlyIfClasses.every(cls => (cls in this.LoadedClasses))) continue
|
||||
libclass = lib.classes[0]; // Let all assets be dependencies of the first script (if many)
|
||||
Loader.AssetsDependencies[libclass] = lib.assets;
|
||||
allPromises.push( Loader.loadScripts({
|
||||
'basepath':'/app/libs'+lib.path,
|
||||
'scripts': lib.classes,
|
||||
'dependencies': lib.dependencies,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
Promise.allSettled(allPromises).then(this.onMVCReady.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
onMVCReady() {
|
||||
// Now that we are authenticated, we have the config and the MVC
|
||||
// load the top routes and then route !
|
||||
|
||||
this.Assets.loadJson({ 'name': 'baseRoutes.json', 'path': '/app/config/' })
|
||||
.then(
|
||||
(topRoutes) =>{
|
||||
if(!this.Router) {
|
||||
//console.log('Top routes loaded...', app.config);
|
||||
var routerOptions = {
|
||||
'defaults' : this.config.router,
|
||||
'routes': topRoutes,
|
||||
'callback': () =>{ } // Called when *routed* (not when instanciated)
|
||||
};
|
||||
if('getRolesFrom' in this.config.router){ //Role can be an array of string or string representing a function
|
||||
var isCallable = Function([],"return(typeof("+this.config.router.getRolesFrom+")=='function');")
|
||||
if(isCallable()) routerOptions['roles'] = Function([],"return("+this.config.router.getRolesFrom+"());");
|
||||
else routerOptions['roles'] = this.config.router.getRolesFrom;
|
||||
} else routerOptions['roles']='';
|
||||
this.Router = new Router(routerOptions);
|
||||
}
|
||||
this.Router.route();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Let the browser cache boot loader components or not ? (true in prod or if you don't work on core)
|
||||
const bootBrowserCache = false;
|
||||
|
||||
/* Here is Sparc bootstrap, depends only on Loader core class defined below */
|
||||
if(typeof(app)=='undefined') var app = new Sparc();
|
||||
|
||||
window.onload = app.onWindowReady.bind(app);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
___ _____ __ ____ ____ ____
|
||||
( ) ( _ ) /__\ ( _ \( ___)( _ \
|
||||
) (_ )(_)( /(__)\ )(_) ))__) ) /
|
||||
(____)(_____)(__)(__)(____/(____)(_)\_) for SPARC
|
||||
By Mike & Nike
|
||||
This static class contains the mechanics for loading javascripts. <br>
|
||||
It will load any needed dependancies for you (with the help of the dependancies file) <br>
|
||||
It can be called just boot-load you application and all its dependancies , <br>
|
||||
and/or, alternatively, you can load widgets yourself anytime.
|
||||
|
||||
* @static
|
||||
* @hideconstructor
|
||||
* @category Core
|
||||
* @todo found some direct reference to the app global var which is a bit sloppy
|
||||
*/
|
||||
class Loader {
|
||||
static Dependencies = {};
|
||||
static LoadedDepFiles = [];
|
||||
static AssetsDependencies = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static resolveDeps(imps, deps){
|
||||
const leveler = (levels, root, l, trail=[]) => {
|
||||
if(deps.hasOwnProperty(root)){
|
||||
for(var dep of deps[root]){
|
||||
if(!levels.hasOwnProperty(dep)) levels[dep] = -1;
|
||||
if(levels[dep]<l) levels[dep]=l;
|
||||
if(trail.indexOf(dep)<0){ // detect circular dependency to avoid infinite recursion
|
||||
let curtrail = [...trail];
|
||||
trail.push(dep);
|
||||
leveler(levels, dep , l+1, trail);
|
||||
trail = curtrail;
|
||||
} else console.error('Circular depencency with '+dep, trail);
|
||||
}
|
||||
}
|
||||
}
|
||||
var levels={};
|
||||
// Build the dep tree of things we need, noting the depth.
|
||||
for(var imp of imps) { leveler(levels, imp, 1); } // Level starts at 1, zero reserved for roots
|
||||
// Add requested imports as last, unless already with higher priority (tree shaking)
|
||||
for(var imp of imps) { if(!levels.hasOwnProperty(imp)) levels[imp] = 0; }
|
||||
// Group by levels, as their loading can be paralellized (whereas on level must wait on the prev. one)
|
||||
var name, level; var byLevel = {};
|
||||
for([name, level] of Object.entries(levels)) {
|
||||
if(!byLevel.hasOwnProperty(level)) { byLevel[level]=[]; }
|
||||
byLevel[level].push(name);
|
||||
}
|
||||
return(byLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static Import(scriptNames, deps, importIdPrfx, basePath = './'){ // Will start with real import (_import) only when / if dependencies are loaded.
|
||||
var CurrentImportChain = {};
|
||||
if(typeof(deps)=='string') { // deps is a path to a json file containing the dep tree
|
||||
if( (deps!='') && (Loader.LoadedDepFiles.indexOf(deps)<0) ){
|
||||
Loader.DependenciesLoader = new Promise((resolve, reject) => {
|
||||
fetch(basePath+deps, { headers: { "Content-Type": "application/json; charset=utf-8" }})
|
||||
.then(res => res.json())
|
||||
.then(response => {
|
||||
Loader.Dependencies = { ...Loader.Dependencies, ...response}; // Merge with known deps
|
||||
Loader.LoadedDepFiles.push(deps);
|
||||
resolve();
|
||||
})
|
||||
.catch(err => { console.error('Error loading dependencies'); Loader.Dependencies = {}; reject(); });
|
||||
});
|
||||
} else {
|
||||
Loader.DependenciesLoader = new Promise((resolve, reject) => { resolve(); }); //Already loaded, nothing to do here
|
||||
}
|
||||
} else if(typeof(deps)=='object') { // deps is the dep-tree object
|
||||
Loader.DependenciesLoader = new Promise((resolve, reject) => {
|
||||
Loader.Dependencies = { ...Loader.Dependencies, ...deps }; // Merge with known deps
|
||||
resolve(); // already ready ! ;-)
|
||||
});
|
||||
} else { console.error('Bad dependencies !?'); return(false); }
|
||||
// Prepare a synchronous chain of asynchronous loads
|
||||
Loader.DependenciesLoader.then(()=>{
|
||||
var byLevelImps = Loader.resolveDeps(scriptNames, Loader.Dependencies);
|
||||
var total = Object.values(byLevelImps).reduce((tot,v)=> (tot+v.length), 0);
|
||||
document.dispatchEvent(new CustomEvent("LoaderProgressAddTodo", {'detail': { 'importID':importIdPrfx, 'addValue': total }}));
|
||||
for(var key in Object.getOwnPropertyNames(byLevelImps).sort().reverse()){
|
||||
CurrentImportChain[importIdPrfx+'_'+key] = {'imps': byLevelImps[key], 'path':basePath };
|
||||
}
|
||||
var firstImportId = importIdPrfx+'_'+key;
|
||||
document.addEventListener('LoaderAllReady', function(evt) { // Next async imports in the sync chain
|
||||
var nextImpNb = evt.detail.substr(0,evt.detail.lastIndexOf('_')+1)+(parseInt(evt.detail.substr(evt.detail.lastIndexOf('_')+1))-1);
|
||||
if(evt.detail != importIdPrfx+'_0'){
|
||||
if(CurrentImportChain.hasOwnProperty(nextImpNb)) {
|
||||
Loader._import(CurrentImportChain[nextImpNb]['imps'], nextImpNb,CurrentImportChain[nextImpNb]['path']);
|
||||
}
|
||||
}
|
||||
});
|
||||
// First load all assets
|
||||
Loader.LoadAssetDependencies(byLevelImps).then( () => {
|
||||
// Start the synchronous chain
|
||||
Loader._import(CurrentImportChain[firstImportId]['imps'], firstImportId,CurrentImportChain[firstImportId]['path']);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} byLevelImps
|
||||
* @returns {Array<Promise>}
|
||||
*/
|
||||
static LoadAssetDependencies(byLevelImps){
|
||||
let allImports = [];
|
||||
for(var level in byLevelImps) allImports=[...allImports, ...byLevelImps[level]];
|
||||
allImports = Array.from(new Set(allImports)); //remove dups
|
||||
allImports = allImports.map(this.path2ClassName);
|
||||
let allPromises = [];
|
||||
for(var classname of allImports){
|
||||
for(var assetType in Loader.AssetsDependencies[classname]){
|
||||
if(!Array.isArray(Loader.AssetsDependencies[classname][assetType])) {
|
||||
console.warn(`Bad config: Asset dependencies for ${classname} - ${assetType} is not an array !?`)
|
||||
continue;
|
||||
}
|
||||
for(var assetObj of Loader.AssetsDependencies[classname][assetType]){
|
||||
if((typeof(assetObj) != 'object') || (!('name' in assetObj))) {
|
||||
console.warn(`Bad config: One asset dependency for ${classname} - ${assetType} has no name !?`)
|
||||
continue;
|
||||
}
|
||||
switch(assetType){
|
||||
case 'fonts': allPromises.push(app.Assets.loadFont(assetObj));
|
||||
break;
|
||||
case 'styles': allPromises.push(app.Assets.loadCss(assetObj));
|
||||
break;
|
||||
case 'html': allPromises.push(app.Assets.loadHtml(assetObj));
|
||||
break;
|
||||
case 'views': assetObj.path = '/app/views/';
|
||||
allPromises.push(app.Assets.loadHtml(assetObj));
|
||||
break;
|
||||
case 'images': allPromises.push(app.Assets.getImage(assetObj));
|
||||
break;
|
||||
case 'json': allPromises.push(app.Assets.loadJson(assetObj));
|
||||
break;
|
||||
case 'sfx': allPromises.push(app.Assets.loadSound(assetObj));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return(Promise.allSettled(allPromises));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} src
|
||||
* @returns {Promise}
|
||||
*/
|
||||
static loadScript(src) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = document.createElement('script')
|
||||
script.type = 'text/javascript'
|
||||
script.onload = resolve
|
||||
script.onerror = reject
|
||||
script.src = src
|
||||
document.head.append(script)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static path2ClassName(scriptName){
|
||||
scriptName = scriptName.substr(scriptName.lastIndexOf('/')+1);
|
||||
if(scriptName.indexOf('.')==-1) scriptName+='.'; // also pure scriptname or path without extension compatible !
|
||||
return(scriptName.substr(0,scriptName.lastIndexOf('.')));
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static url2Path(url){
|
||||
var x = new URL(url);
|
||||
return(x.pathname);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static _import(names, importID, basePath){ // Here, all imports are made asynchronously, in parallel.
|
||||
var allLoaded = true;
|
||||
var nbLeft = names.length;
|
||||
for(var name of names){
|
||||
var className = this.path2ClassName(name);
|
||||
if(name.substring(0,4)!='http') var scriptFp = (basePath + '/' + name).replace(/\/+/g,'/')+'.js';
|
||||
else var scriptFp = name;
|
||||
if( (!(className in app.LoadedClasses)) && (app.LoadedScripts.indexOf(scriptFp)<0) ) {
|
||||
allLoaded = false;
|
||||
if( !bootBrowserCache ) scriptFp += '?'+importID;
|
||||
Loader.loadScript(scriptFp)
|
||||
.then((result) => {
|
||||
var scriptName= this.url2Path(result.currentTarget.src);
|
||||
// MCV classes and internal ones put themselves in app.loadedClasses,
|
||||
// Non-Sparc scripts don't so save their path.
|
||||
if( ('router' in app.config) &&
|
||||
!(scriptName.startsWith(app.config.router.controllersPath)) &&
|
||||
!(scriptName.startsWith(app.config.router.modelsPath)) &&
|
||||
!(scriptName.startsWith(app.config.router.viewsPath)) &&
|
||||
!(scriptName.startsWith('/core/'))
|
||||
) app.LoadedScripts.push(scriptName);
|
||||
var className = this.path2ClassName(result.currentTarget.src);
|
||||
document.dispatchEvent(new CustomEvent('Loader'+className+'Ready', { 'detail': importID}));
|
||||
document.dispatchEvent(new CustomEvent("LoaderProgressIncrement", {'detail': { 'importID': importID, 'success': true }}));
|
||||
nbLeft--;
|
||||
if(nbLeft<=0){
|
||||
document.dispatchEvent(new CustomEvent("LoaderAllReady", { 'detail': importID }));
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if((typeof(error)!='undefined') && (typeof(error.currentTarget)!='undefined')){
|
||||
var scriptName= error.currentTarget.src;
|
||||
var className = this.path2ClassName(error.currentTarget.src);
|
||||
console.error('Could not import Class '+className);
|
||||
document.dispatchEvent(new CustomEvent("LoaderError", { 'detail': {'importID': importID, 'className': className }}));
|
||||
} else console.error('Script loading error '+error);
|
||||
document.dispatchEvent(new CustomEvent("LoaderProgressIncrement", {'detail': { 'importID': importID, 'success': false }}));
|
||||
nbLeft--;
|
||||
});
|
||||
} else nbLeft--;
|
||||
}
|
||||
if(allLoaded){
|
||||
for(var name of names) document.dispatchEvent(new CustomEvent('Class'+name+'Ready', { 'detail':importID }));
|
||||
document.dispatchEvent(new CustomEvent("LoaderAllReady", { 'detail':importID }));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load a list of scripts.
|
||||
* @static
|
||||
* @param {string} basepath - base path (something like /sccripts/)
|
||||
* @param {Array<string>} scripts - array of scripts to load
|
||||
* @param {(string|object)} dependencies - Last minute Dependancies :tree object ( like {'scriptName': ['deps1', 'dep2,...]} ) or path to its json file.
|
||||
* @param {function} callback - Called when everything is loaded
|
||||
* @returns {Promise} A promise resolved whn-en all is loaded
|
||||
*/
|
||||
static loadScripts(args){
|
||||
// Start by filtering out scripts we already have
|
||||
if(!args.refresh) {
|
||||
args.scripts = args.scripts.filter((sname)=>{
|
||||
var className = this.path2ClassName(sname);
|
||||
return(!(className in app.LoadedClasses) && (!(sname in app.LoadedScripts)));
|
||||
});
|
||||
if(args.scripts.length==0) {
|
||||
if( (args.hasOwnProperty('callback')) && (typeof(args.callback)=='function') ){
|
||||
args.callback({});
|
||||
}
|
||||
return(new Promise((resolve) => { resolve(); }));
|
||||
}
|
||||
}
|
||||
var myImportID = crypto.randomUUID();
|
||||
if(!args.hasOwnProperty('dependencies')) args.dependencies = [];
|
||||
if(!args.hasOwnProperty('basepath')) args.basepath = '';
|
||||
return(new Promise((resolve, fail) => {
|
||||
document.addEventListener('LoaderAllReady', (evt) => { if(evt.detail==myImportID+'_0') resolve(); } );
|
||||
document.addEventListener('LoaderError', (evt) => { if(evt.detail==myImportID+'_0') fail(); } );
|
||||
Loader.Import(args.scripts, args.dependencies, myImportID, args.basepath);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a list of views.
|
||||
* @static
|
||||
* @param {array} Views - array of Views to load
|
||||
* @returns {string} The unique ID of the import
|
||||
*/
|
||||
static async loadViews(views){
|
||||
// Start with views templates
|
||||
let allPromises = []
|
||||
let templates = []
|
||||
let scripts = []
|
||||
let dependencies = {}
|
||||
for(var dep of views){
|
||||
if(typeof(dep) == 'string') {// just add view as dependency of controller, with correct path
|
||||
templates.push(dep+'.html')
|
||||
scripts.push(dep)
|
||||
} else if(typeof(dep) == 'object'){ // This view depends on other views
|
||||
scripts.push(dep.view);
|
||||
dependencies[dep.view]=dep.dependencies//.map(x=>'/app/views/'+x)
|
||||
templates.push(dep.view+'.html')
|
||||
}
|
||||
}
|
||||
|
||||
for(let view of templates){
|
||||
let assetObj = { path: '/app/views/', name: view }
|
||||
allPromises.push(app.Assets.loadHtml(assetObj));
|
||||
}
|
||||
await Promise.allSettled(allPromises);
|
||||
|
||||
// Finish with views scripts
|
||||
return(this.loadScripts({
|
||||
basepath:'/app/views/',
|
||||
scripts: scripts,
|
||||
dependencies: dependencies,
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* POLYFILLS
|
||||
*/
|
||||
|
||||
if(typeof(crypto.randomUUID)!='function'){
|
||||
crypto.randomUUID = () => {
|
||||
var buf = new Uint8Array(14);
|
||||
crypto.getRandomValues(buf);
|
||||
var uuid = Array.from(buf, byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');
|
||||
return(
|
||||
uuid.substring(0,8) + '-' +
|
||||
uuid.substring(10,4) + '-' +
|
||||
uuid.substring(14,4) + '-' +
|
||||
uuid.substring(18,4) + '-' +
|
||||
uuid.substring(22)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if(typeof(typeof(Promise.allSettled))!='function'){
|
||||
Promise.allSettled = ((promises) => Promise.all(
|
||||
promises.map(p => p
|
||||
.then(value => ({
|
||||
status: "fulfilled",
|
||||
value
|
||||
}))
|
||||
.catch(reason => ({
|
||||
status: "rejected",
|
||||
reason
|
||||
}))
|
||||
)
|
||||
));
|
||||
}
|
||||
Reference in New Issue
Block a user