{"version":3,"sources":["window-interface.ts"],"sourcesContent":["import { Runtime } from \"../../runtime/runtime.ts\";\nimport { Endpoint, Target } from \"../../types/addressing.ts\";\nimport { CommunicationInterface, CommunicationInterfaceSocket, InterfaceDirection, InterfaceProperties } from \"../communication-interface.ts\";\nimport { communicationHub } from \"../communication-hub.ts\";\nimport { arrayBufferToBase64, base64ToArrayBuffer } from \"../../utils/utils.ts\";\n\nexport class WindowInterfaceSocket extends CommunicationInterfaceSocket {\n    constructor(public readonly window: Window, public readonly windowOrigin: string, public readonly transmissionMode: \"buffer\"|\"json\" = \"buffer\") {\n        super();\n    }\n\n    handleReceive = (event: MessageEvent) => {\n        if (event.origin == this.windowOrigin) {\n            if (event.data instanceof ArrayBuffer) this.receive(event.data)\n            else if (typeof event.data == \"string\") this.receive(base64ToArrayBuffer(event.data))\n        }\n    }\n\n    open() {\n        globalThis.addEventListener('message', this.handleReceive);\n    }\n\n    close() {\n        globalThis.removeEventListener('message', this.handleReceive);\n    }\n\n    send(dxb: ArrayBuffer) {\n        try {\n            if (this.transmissionMode == \"json\") this.window.postMessage(arrayBufferToBase64(dxb), this.windowOrigin)\n            else this.window.postMessage(dxb, this.windowOrigin)\n            return true;\n        }\n        catch {\n            return false;\n        }\n    }\n}\n\n/**\n * Creates a direct DATEX communication channel between a parent and child window\n */\nexport class WindowInterface extends CommunicationInterface {\n    \n    public properties: InterfaceProperties = {\n        type: \"window\",\n        direction: InterfaceDirection.IN_OUT,\n        latency: 15,\n        bandwidth: 1_000_000\n    }\n\n    #windowOrIFrame: Window|HTMLIFrameElement\n    #windowOrigin: string\n    #isChild: boolean\n    #transmissionMode: \"buffer\"|\"json\"\n\n    get window() {\n        return this.#windowOrIFrame instanceof HTMLIFrameElement ? this.#windowOrIFrame.contentWindow! : this.#windowOrIFrame;\n    }\n\n    constructor(window: Window, windowOrigin?: string|URL, type?: \"parent\"|\"child\", transmissionMode?: \"buffer\"|\"json\")\n    constructor(iframe: HTMLIFrameElement, iframeOrigin?: string|URL, type?: \"parent\"|\"child\", transmissionMode?: \"buffer\"|\"json\" )\n    constructor(window: Window|HTMLIFrameElement, windowOrigin?: string|URL, type?: \"parent\"|\"child\", transmissionMode: \"buffer\"|\"json\" = \"buffer\") {\n        super()\n\n        let windowOriginURL = windowOrigin ? new URL(windowOrigin) : null;\n\n        this.#windowOrIFrame = window;\n        this.#transmissionMode = transmissionMode;\n\n        // is parent document, has iframe\n        if (window instanceof HTMLIFrameElement) {\n            this.#isChild = false;\n            // Modifying the sandbox attr does not make sense since here\n            // since the src is already set and iframe is sandboxed on load\n            // window.setAttribute(\"sandbox\", \"allow-popups-to-escape-sandbox allow-modals allow-forms allow-popups allow-scripts allow-same-origin allow-top-navigation\")\n            this.#windowOrigin = new URL(window.src).origin;\n            windowOriginURL = new URL(window.src);\n            this.logger.debug(\"initializing as parent window, child iframe origin: \" + this.#windowOrigin)\n        }\n        // is opened child window or inside iframe\n        else if (type !== \"parent\" && (type === \"child\" || window === self.window.opener || globalThis.self !== globalThis.top)) {\n            this.#isChild = true;\n\n            // explicitly set window origin\n            if (windowOriginURL) {\n                this.#windowOrigin = windowOriginURL.origin;\n            }\n            else {\n                // first try window.location.origin\n                try {\n                    this.#windowOrigin = window.location.origin;\n                }\n                // try document.referrer\n                catch {\n                    if (!document.referrer) throw new Error(\"The origin of the parent window cannot be determined automatically. Please provide windowOrigin as second argument.\");\n                    this.#windowOrigin = new URL(document.referrer).origin;\n                }\n            }\n            this.logger.debug(\"initializing as child window, parent window origin: \" + this.#windowOrigin)\n        }\n        // is the parent document\n        else {\n            this.#isChild = false;\n\n            // explicitly set window origin\n            if (windowOriginURL) {\n                this.#windowOrigin = windowOriginURL.origin;\n            }\n            else {\n                throw new Error(\"The origin of the child window cannot be determined automatically. Please provide windowOrigin as second argument.\");\t\t\t\t\n            }\n            this.logger.debug(\"initializing as parent window, child window origin: \" + this.#windowOrigin)\n        }\n\n        this.properties.name = windowOriginURL?.toString() || this.#windowOrigin;\n\n        globalThis.addEventListener(\"message\", this.onReceive);\n        if (this.#isChild) {\n            // if in sub window: send INIT to parent immediately\n            this.sendInit();\n        }\n        this.handleClose();\n    }\n\n    #connectedPromise = Promise.withResolvers<true>()\n\n    connect() {\n        return this.#connectedPromise.promise;\n    }\n\n    disconnect() {\n        // make sure event listener is removed if INIT not yet completed\n        globalThis.removeEventListener(\"message\", this.onReceive);\n    }\n    \n\n    private sendInit() {\n        this.window.postMessage({\n            type: \"INIT\",\n            endpoint: Runtime.endpoint.toString()\n        }, this.#windowOrigin);\n    }\n\n    onClose?: ()=>void\n\n    private handleClose() {\n        // check window.closed every second\n        const interval = setInterval(() => {\n            if (this.window?.closed) {\n                clearInterval(interval);\n                this.clearSockets()\n                this.onClose?.()\n            }\n        }, 1000);\n    }\n\n    private onReceive = (event: MessageEvent) => {\n        if (event.origin == this.#windowOrigin) {\n            const data = event.data;\n            if (data?.type == \"INIT\") {\n                this.#connectedPromise.resolve(true)\n                \n                // only one active socket allowed, remove existing\n                this.clearSockets();\n\n                const socket = new WindowInterfaceSocket(this.window, this.#windowOrigin, this.#transmissionMode)\n                this.addSocket(socket)\n\n                // if in parent: send INIT to window after initialized\n                if (!this.#isChild) this.sendInit();\n            }\n        }\n    }\n\n    cloneSocket(socket: WindowInterfaceSocket) {\n        return new WindowInterfaceSocket(socket.window, socket.windowOrigin, socket.transmissionMode);\n    }\n\n    \n    static createChildWindowInterface(childWindow: Window, windowOrigin: string|URL, transmissionMode?: \"buffer\"|\"json\") {\n        return new WindowInterface(childWindow, windowOrigin, \"parent\", transmissionMode)\n    }\n\n    static createChildIFrameInterface(iframe: HTMLIFrameElement, transmissionMode?: \"buffer\"|\"json\") {\n        return new WindowInterface(iframe, undefined, \"parent\", transmissionMode)\n    }\n\n    static createParentInterface(parentWindow: Window, windowOrigin?: string|URL, transmissionMode?: \"buffer\"|\"json\") {\n        return new WindowInterface(parentWindow, windowOrigin, \"child\", transmissionMode)\n    }\n\n\n    /**\n     * Opens a new window and registers a attached WindowInterface.\n     * The WindowInterface is automatically removed when the window is closed.\n     */\n    static createWindow(url: string | URL, target?: string, features?: string, connectionTimeout?: number) {\n        const newWindow = window.open(url, target, features);\n        if (!newWindow) return Promise.resolve({window: null, endpoint: null});\n        const windowInterface = this.createChildWindowInterface(newWindow, url)\n        \n        communicationHub.addInterface(windowInterface)\n        windowInterface.onClose = () => {\n            communicationHub.removeInterface(windowInterface)\n        }\n\n        return new Promise<{window:Window|null, endpoint: Endpoint|null}>((resolve) => {\n            windowInterface.addEventListener(\"connect\", e => {\n                resolve({\n                    window: newWindow,\n                    endpoint: e.endpoint\n                })\n            })\n            if (connectionTimeout!=null && isFinite(connectionTimeout)) { \n                setTimeout(() => {\n                    newWindow.close();\n                    resolve({window: newWindow, endpoint: null})\n                }, connectionTimeout);\n            }\n        })\n    }\n\n    /**\n     * Binds a Iframe and registers a attached WindowInterface.\n     * The WindowInterface is automatically removed when the iframe is closed.\n     */\n    static bindIFrame(iframe: HTMLIFrameElement, connectionTimeout?: number) {\n        const windowInterface = this.createChildIFrameInterface(iframe)\n        \n        communicationHub.addInterface(windowInterface)\n        windowInterface.onClose = () => {\n            communicationHub.removeInterface(windowInterface)\n        }\n\n        return new Promise<Endpoint|null>((resolve) => {\n            windowInterface.addEventListener(\"connect\", e => {\n                resolve(e.endpoint)\n            })\n            if (connectionTimeout!=null && isFinite(connectionTimeout)) { \n                setTimeout(() => {\n                    resolve(null)\n                }, connectionTimeout);\n            }\n        })\n    }\n\n}\n"],"names":["Runtime","CommunicationInterface","CommunicationInterfaceSocket","InterfaceDirection","communicationHub","arrayBufferToBase64","base64ToArrayBuffer","WindowInterfaceSocket","constructor","window","windowOrigin","transmissionMode","handleReceive","event","origin","data","ArrayBuffer","receive","open","globalThis","addEventListener","close","removeEventListener","send","dxb","postMessage","WindowInterface","properties","type","direction","IN_OUT","latency","bandwidth","HTMLIFrameElement","contentWindow","windowOriginURL","URL","src","logger","debug","self","opener","top","location","document","referrer","Error","name","toString","onReceive","sendInit","handleClose","Promise","withResolvers","connect","promise","disconnect","endpoint","onClose","interval","setInterval","closed","clearInterval","clearSockets","resolve","socket","addSocket","cloneSocket","createChildWindowInterface","childWindow","createChildIFrameInterface","iframe","undefined","createParentInterface","parentWindow","createWindow","url","target","features","connectionTimeout","newWindow","windowInterface","addInterface","removeInterface","e","isFinite","setTimeout","bindIFrame"],"mappings":"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","sourceRoot":"/lib/datex-core-js-legacy-0.3.15/network/communication-interfaces/","file":"window-interface.ts"}