UNPKG

iportal

Version:

web-portal

207 lines (192 loc) 6.56 kB
import { ModuleState } from './state' import { ModuleManifest, Application } from '../types' class ModuleMount extends ModuleState { private mutationObserver!: MutationObserver constructor (id: string, model: ModuleManifest, application: Application) { super(id, model, application) } public timeTick () { if (Date.now() - this.createTime > (this.config.timeout || 3600000)) { if (this.view) this.destroy() } } public show () { if (this.viewType === 'portal') { (this.view as HTMLPortalElement)?.activate() return } if (this.viewType !== 'iframe') return for (const task of this.darkTask) { task() } this.darkTask = [] this.visibility = true this.trigger('visible') this.triggerWindow('module-visible', 'moduleVisibilityState', 'visible') } public hide () { this.visibility = false this.trigger('hidden') this.triggerWindow('module-hidden', 'moduleVisibilityState', 'hidden') } public willShow () { this.trigger('willShow') this.triggerWindow('module-will-show', 'moduleVisibilityState', 'willVisible') } public willHide () { this.trigger('willHidden') this.triggerWindow('module-will-hidden', 'moduleVisibilityState', 'willHidden') } public triggerWindow (type: string, attributeName?: string, attributeValue?: string | object | boolean): void { if (this.viewType !== 'iframe') return const sandbox = this.view as HTMLIFrameElement const contentWindow = sandbox?.contentWindow?.window if (contentWindow) { contentWindow.postMessage({ type, historyDirection: this.application.transform.historyDirection }, '*') if (this.sameOrigin && attributeName) { contentWindow[attributeName] = attributeValue } } } public mediaGuard () { return new Promise<void>((resolve, reject) => { try { if (this.viewType !== 'iframe') return resolve() if (this.sandbox === undefined) return resolve() const view = this.view as HTMLIFrameElement const contentDocumentElement = view.contentDocument?.documentElement if (!contentDocumentElement) return reject() if (contentDocumentElement.getElementsByTagName('video')[0]) { const videos = contentDocumentElement.querySelectorAll('video') for (const index in videos) { const video = videos[index] if (!video?.paused) { video.pause() this.darkTask.push(() => { video.play() }) } } } if (contentDocumentElement.getElementsByTagName('audio')[0]) { const audios = contentDocumentElement.querySelectorAll('audio') for (const index in audios) { const audio = audios[index] if (!audio?.paused) { audio.pause() this.darkTask.push(() => { audio.play() }) } } } } catch (error) { reject() } }) } public destroy () { return new Promise<void>((resolve, reject) => { if (this.rel !== 'module') return reject() if (this.application.transform?.id === this.id) return reject() if (this.viewType === 'iframe') this.unload().catch(reject) this.elements.container.parentElement?.removeChild(this.elements.container) this.status.prefetch = this.status.preload = this.status.prerender = false this.view = null this.status.init = false this.darkTask = [] this.events?.destroy.bind(this)() this.trigger('destroy') resolve() }) } public observer (change: (record: MutationRecord[]) => void) { const target = this.sandbox ? this.sandbox.contentDocument?.documentElement : this.view if (!target) return const observer = new MutationObserver((record: MutationRecord[]) => { change(record) }) observer.observe(target, { subtree: true, attributes: true, childList: true, characterData: true, attributeOldValue: true, characterDataOldValue: true }) return observer } public fate () { return new Promise<void>((resolve, reject) => { if (this.rel !== 'module') return reject() if (this.config.background === true) return reject() if (this.config.background === false) return resolve() if (this.viewType !== 'iframe') return reject() if (this.sandbox === undefined) return reject() if (this.sameOrigin === false) return resolve() const view = this.view as HTMLIFrameElement try { const contentDocumentElement = view.contentDocument?.documentElement if (!contentDocumentElement) return reject() if (this.config.mediaGuard !== false) { this.mediaGuard().catch(resolve) } if (contentDocumentElement.getElementsByTagName('object')[0]) return resolve() if (contentDocumentElement.getElementsByTagName('embed')[0]) return resolve() if (contentDocumentElement.getElementsByTagName('applet')[0]) return resolve() if (contentDocumentElement.getElementsByTagName('iframe')[0]) return resolve() if (contentDocumentElement) { const counter = { times: 0 } const observer = this.observer(() => { counter.times++ if (counter.times > 1000) { resolve() this.mutationObserver.disconnect() } }) if (!observer) return this.mutationObserver = observer setTimeout(() => { if (counter.times > 10) resolve() }, 3000) } else { reject() } } catch (error) { reject() } }) } public unfate () { this.mutationObserver?.disconnect() } private unload () { return new Promise<void>((resolve, reject) => { this.unfate() const sandbox = this.view as HTMLIFrameElement if (!sandbox) return resolve() sandbox.style.display = 'none' sandbox.src = 'about:blank' try { const contentWindow = sandbox.contentWindow?.window const contentDocument = sandbox.contentDocument contentWindow?.location.reload() contentDocument?.open() contentDocument?.write('') contentDocument?.close() } catch (error) { reject() } sandbox.parentElement?.removeChild(sandbox) resolve() }) } } export { ModuleMount }