iportal
Version:
web-portal
260 lines (244 loc) • 8.09 kB
text/typescript
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
}