const TELLER_MESSAGE_TIMEOUT = 1000; class Teller { private channel: string; private listener?: (event: MessageEvent>) => void; /** * Cross-origin solution to communicate between tabs, windows, and iframes. Usage: - Create a new Teller instance. - If you want to use a custom message type, you can pass it to Teller. - const tellerManager = new Teller(); - Call tellerManager.sendMessage({message, targetWindow, origin}) to send a message to the other tab/window/iframe. - Call tellerManager.setupListener({onReceiveMessage}) to listen for messages from the other tab/window/iframe. - Call tellerManager.close(); to close the active listener. Details on: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage */ constructor(options: TellerOptions) { this.listener = undefined; this.channel = options.channel; } /** * Only messages from `source`, the window we opened, served from `origin` * are delivered. Any window holding a reference to this page can post to it * (an embedded iframe, the site that opened it), and the channel name is * public, so a channel match alone would let them answer for Pera Web. */ public setupListener({ onReceiveMessage, origin, source }: { onReceiveMessage: (event: MessageEvent>) => void; /** URL whose origin the messages must come from. */ origin: string; source: MessageEventSource | null; }) { // Close the existing listener if it exists this.close(); const expectedOrigin = new URL(origin).origin; this.listener = (event: MessageEvent>) => { if (!source || event.source !== source || event.origin !== expectedOrigin) { return; } if (typeof event.data === "object") { try { if (event.data.channel === this.channel) { onReceiveMessage(event); } } catch (error) { console.error(error); } } }; // Set the new listener window.addEventListener("message", this.listener); } public sendMessage({ message, targetWindow, origin, timeout = TELLER_MESSAGE_TIMEOUT }: TellerMessageOptions) { setTimeout(() => { const tellerMessage = { channel: this.channel, message }; targetWindow.postMessage(tellerMessage, { // if the origin is not specified, the message will not be origin specific targetOrigin: origin || "*" }); }, timeout); } public close() { if (this.listener) { window.removeEventListener("message", this.listener); this.listener = undefined; } } } export default Teller;