UNPKG

iportal

Version:

web-portal

260 lines (244 loc) 8.09 kB
import { Module } from '../Module/index' import { Transform } from '../Transform' import { ApplicationBase } from './base' import provider from './provider' import { ModuleManifest, ModuleConfig, ServiceWorkerInstallConfig, ApplicationSafeAreaValue, GlobalCSSVariables, TransformActionOrigin } from '../types' class Application extends ApplicationBase { public transform = new Transform(this) constructor () { super() provider(this) } get activeModule (): Module | undefined { const id = this.transform.id const module = this.modules[id] return module } get preActiveModule (): Module | undefined { const id = this.transform.od const module = this.modules[id] return module } public to = this.transform.to public add (id: string, manifest: ModuleManifest) { if (this.modules[id]) { return this.modules[id] } const process = this.config?.moduleManifestProcess if (process) { manifest = process(manifest) || manifest } return this.modules[id] = new Module(id, manifest, this) } public del (module: Module) { return new Promise<void>((resolve, reject) => { delete this.modules[module.id] if (module.status.init) { module.destroy().then(resolve).catch(reject) } else { resolve() } }) } public get (id: string): Promise<Module> { return new Promise((resolve, reject) => { if (typeof this.modules[id] === 'object') return resolve(this.modules[id]) const modulePromise = this.options.modules[id] switch (typeof modulePromise) { case 'function': this.promiseModule(modulePromise).then((manifest) => { resolve(this.add(id, manifest)) }).catch(reject) break case 'object': resolve(this.add(id, modulePromise)) break default: const url = id if (!this.moduleSrcVerify(url)) { reject() break } const module = this.createModuleByURL(url, {}, id) if (module) { resolve(module) } else { reject() } break } }) } public cloneAsNewModule (module: Module, id?: string, config = {}) { if (!id) return module const newConfig = Object.assign({}, module.model.config, config) const newModule = this.add(id, Object.assign({}, module.model, { config: newConfig })) return newModule } public createModuleByURL (url: string, config?: { [key in Extract<keyof ModuleConfig, string>]?: string | number }, cloneAs?: string): Module | undefined { const newModuleId = decodeURIComponent(url) const modules = this.modules const sameModule = modules[newModuleId] ?? this.getModuleByURL(url) if (sameModule) return cloneAs ? this.cloneAsNewModule(sameModule, cloneAs, config) : sameModule return this.add(newModuleId, { config: Object.assign({ title: '', rel: 'module', level: (this.activeModule?.config.level ?? 0) + 1, free: true, source: { src: url }, background: 'auto', timeout: 0, animation: 'inherit', transient: true }, config) }) } public pushWindow (url: string, title = '', preset = 'slide', cloneAs?: string, touches?: TouchEvent | TransformActionOrigin) { const resolve = this.resolveURL(url) const search = resolve.search if (!this.moduleSrcVerify(url)) { return Promise.reject('Illegal') } return new Promise<void>((resolve, reject) => { const module = this.createModuleByURL(url, { title, animation: preset }, cloneAs) if (module) { this.transform.to(module.id, module.config?.source?.html ? search : '', 1, touches).then(resolve).catch(reject) } }) } public beforehandDependencies (dependencies: string[] = [], prerender = true): Promise<any | void> { const allPromise: Promise<any>[] = [] for (const dep of dependencies) { allPromise.push(new Promise((resolve, reject) => { this.get(dep).then((module) => { if (prerender) { module.prerender().then(resolve).catch(resolve) } else { resolve('') } }).catch(reject) })) } return new Promise((resolve, reject) => { Promise.all(allPromise).then(resolve).catch(reject) }) } public updateSafeArea (data: ApplicationSafeAreaValue) { this.trigger('safeAreaChange', data) } public updateGlobalCSSVariables (data: GlobalCSSVariables) { this.trigger('globalCSSVariablesChange', data) } public createWorkerURL (source: Array<string>, version: string): string { const script = ` this.addEventListener('install', function(event) { event.waitUntil( caches.open('v_${version}').then(function(cache) { return cache.addAll(["${source.join('","')}"]) }) ) }) this.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request).then(function() { return fetch(event.request).then(function(response) { return caches.open('v_${version}').then(function(cache) { cache.put(event.request, response.clone()) return response }) }) }) ) }) self.addEventListener('activate', function(event) { event.waitUntil( caches.keys().then(function(keyList) { return Promise.all(keyList.map(function(key) { if (['v_${version}'].indexOf(key) === -1) { return caches.delete(key) } })) }) ) }) ` return URL.createObjectURL(new Blob([script], { type: 'text/html' })) } public install (options: ServiceWorkerInstallConfig, version: string) { const { swPathUrl, scope = '', source = [] } = options if (!swPathUrl && !source) return Promise.reject() return new Promise<ServiceWorkerRegistration>((resolve, reject) => { if ('serviceWorker' in navigator) { navigator.serviceWorker.register(swPathUrl || this.createWorkerURL(source, version), { scope }).then((reg) => { if (reg.installing) { console.log('Service worker installing') } else if (reg.waiting) { console.log('Service worker installed') } else if (reg.active) { console.log('Service worker active') } resolve(reg) }).catch((error) => { reject(error) }) } }) } private mountSystem () { if (this.options.modules['system']) { this.get('system').then(() => { this.transform.to('system', undefined, -1).then(() => { this.trigger('systemDidMount') }) }) } } private mountFramework () { this.get('frameworks').then((module) => { const route = this.route const config = module.config const index = config.index || '' const preindex = config.preindex const id = route.id || index this.config = config this.transform.setup({ singleFlow: config.singleFlow, singleLock: config.singleLock, index, defaultIndex: id, notFound: config.notFind, limit: Math.max(config.limit || 7, 2), exists: this.exists, defaultAnimation: config.animation, holdBack: config.holdBack }) this.transform.to('frameworks', undefined, -1).then(() => { this.trigger('frameworksDidMount') }) if (id !== index && id !== preindex) { if (preindex) this.transform.pushState(preindex) if (id) this.transform.to(id) return } if (id) this.transform.to(id) }).catch(() => { this.console.error('Module frameworks must be included!', 'Serious!', '') }) } public start () { this.setExists() this.mountFramework() this.mountSystem() } } export { Application }