const EVENT_MODULE_LOADED = "module-loaded"; class Startup { constructor(modules) { this.modules = modules; this.cb = null; this.isErrored = false; this.isAllLoadedFired = false; document.addEventListener(EVENT_MODULE_LOADED, this.allModulesLoaded.bind(this)); } setCb(cb) { this.cb = cb; } setErrCb(cb) { this.errCb = cb; } finishModule(name) { name = Startup.getName(name); this.modules[name] = !0; PrettyConsole.debug(name, "Module Loading Done"); document.dispatchEvent(new CustomEvent(EVENT_MODULE_LOADED, {detail: name})); } moduleErrored(name, error) { name = Startup.getName(name); if (this.errCb) { this.errCb(name, error); } this.isErrored = true; document.dispatchEvent(new CustomEvent(`module-errored`, { data: {name, error} })); } isModuleLoaded(names, cb) { let self = this; if (typeof names === 'string') { names = [names]; } let pendingModules = names.filter(function (module) { return !self.modules[Startup.getName(module)] }); if (pendingModules.length > 0) { function listener() { document.removeEventListener(EVENT_MODULE_LOADED, listener); self.isModuleLoaded.bind(self)(pendingModules, cb); } document.addEventListener(EVENT_MODULE_LOADED, listener); } else { cb(); } } registerModule(name) { this.modules[Startup.getName(name)] = false; } allModulesLoaded() { if (this.isErrored) { return false; } if (this.isAllLoadedFired) { return true; } for (let module in this.modules) { if (this.modules.hasOwnProperty(module) && !this.modules[module]) { return false; } } this.isAllLoadedFired = true; if (this.cb) { this.cb(); } document.dispatchEvent(new CustomEvent('startupFin')); return true; } ignite() { document.dispatchEvent(new CustomEvent('ignite')); } registerInit(onInit) { document.addEventListener('ignite', onInit); } loadOtherJsFiles(files) { for (let file of files) { this.registerModule(file[1]); let script = document.createElement('script'); script.onload = () => { moduleLoader.finishModule(file[1]); } script.onerror = (err) => { moduleLoader.moduleErrored(file[1], err); } script.src = file[0]; document.body.appendChild(script); } } static getName(name) { if (typeof name === 'function') { return name.name; } return name; } } class Loader { constructor() { document.addEventListener(EVENT_MODULE_LOADED, this.loadProgress.bind(this)); document.addEventListener('startupFin', this.finish.bind(this)); this.loadingText = document.querySelector('v-loading-text'); this.loadingPerc = document.querySelector('v-loading-stats'); this.loadingScreen = document.querySelector('v-loading-screen'); this.loaded = 0; this.toLoad = 0; this.isErrored = false; } addToLoadItem(i) { this.toLoad += i; } addLoadedItems(i) { this.loaded += i; this.loadProgress(); } loadProgress() { if (this.isErrored) return; let loaded = 0, keys = Object.keys(moduleLoader.modules); for (let key of keys) { if (moduleLoader.modules[key]) { loaded++; } } loaded += this.loaded; let toLoad = keys.length + this.toLoad; const perc = ((loaded / toLoad) * 100).toFixed(2) + "%"; this.loadingScreen.style.setProperty('--loading-scale', perc); this.loadingPerc.innerText = `${loaded} / ${toLoad} (${perc})`; } onError(module, error) { this.isErrored = true; this.loadingText.innerText = "Errored"; this.loadingPerc.innerHTML = `Module ${module} failed to load
${error}`; this.loadingScreen.style.setProperty('--loading-color', '#fa0000'); PrettyConsole.error("Startup", `Module ${module} failed to load`) } finish() { document.removeEventListener('module-loaded-progress', this.loadProgress.bind(this)); } } const moduleLoader = new Startup({ 'Startup': false, }); const loader = new Loader(); moduleLoader.setErrCb(loader.onError.bind(loader)); const preLoader = new PreLoader(); moduleLoader.isModuleLoaded('PreLoader', () => { moduleLoader.loadOtherJsFiles([ ['/out/app/app.min.js', 'app.js'], ]); });