import CancelablePromise from "cancelable-promise"; import { get, Readable, writable, Writable } from "svelte/store"; import { iframeListener } from "../../Api/IframeListener"; import { coWebsiteManager } from "../CoWebsiteManager"; import type { CoWebsite, CoWebsiteState } from "./CoWesbite"; export class SimpleCoWebsite implements CoWebsite { protected id: string; protected url: URL; protected state: Writable; protected iframe?: HTMLIFrameElement; protected loadIframe?: CancelablePromise; protected allowApi?: boolean; protected allowPolicy?: string; protected widthPercent?: number; protected closable: boolean; constructor(url: URL, allowApi?: boolean, allowPolicy?: string, widthPercent?: number, closable?: boolean) { this.id = coWebsiteManager.generateUniqueId(); this.url = url; this.state = writable("asleep" as CoWebsiteState); this.allowApi = allowApi; this.allowPolicy = allowPolicy; this.widthPercent = widthPercent; this.closable = closable ?? false; } getId(): string { return this.id; } getUrl(): URL { return this.url; } getState(): CoWebsiteState { return get(this.state); } getStateSubscriber(): Readable { return this.state; } getIframe(): HTMLIFrameElement | undefined { return this.iframe; } getLoadIframe(): CancelablePromise | undefined { return this.loadIframe; } getWidthPercent(): number | undefined { return this.widthPercent; } isClosable(): boolean { return this.closable; } load(): CancelablePromise { this.loadIframe = new CancelablePromise((resolve, reject, cancel) => { this.state.set("loading"); const iframe = document.createElement("iframe"); this.iframe = iframe; this.iframe.src = this.url.toString(); this.iframe.id = this.id; if (this.allowPolicy) { this.iframe.allow = this.allowPolicy; } if (this.allowApi) { iframeListener.registerIframe(this.iframe); } this.iframe.classList.add("pixel"); const onloadPromise = new Promise((resolve) => { if (this.iframe) { this.iframe.onload = () => { this.state.set("ready"); resolve(); }; } }); const onTimeoutPromise = new Promise((resolve) => { setTimeout(() => resolve(), 2000); }); coWebsiteManager.getCoWebsiteBuffer().appendChild(this.iframe); const race = CancelablePromise.race([onloadPromise, onTimeoutPromise]) .then(() => { return resolve(iframe); }) .catch((err) => { console.error("Error on co-website loading => ", err); return reject(); }); cancel(() => { race.cancel(); this.unload().catch((err) => { console.error("Cannot unload co-website while cancel loading", err); }); }); }); return this.loadIframe; } unload(): Promise { return new Promise((resolve) => { if (this.iframe) { if (this.allowApi) { iframeListener.unregisterIframe(this.iframe); } this.iframe.parentNode?.removeChild(this.iframe); } if (this.loadIframe) { this.loadIframe.cancel(); this.loadIframe = undefined; } this.state.set("asleep"); resolve(); }); } }