UNPKG

@jitsi/js-utils

Version:

Utilities for Jitsi JS projects

152 lines (151 loc) 5.24 kB
/* eslint-disable */ import { safeJsonParse } from '../json'; /** * Creates a postMessage-based communication channel between windows. */ export default class Postis { /** * Creates a new Postis instance. * * @param {PostisOptions} options - Configuration options for the postis instance. */ constructor(options) { this.listeners = {}; this.sendBuffer = []; this.listenBuffer = {}; this._ready = false; this.readyMethod = "__ready__"; this.scope = options.scope; this.targetWindow = options.window; this.windowForEventListening = options.windowForEventListening || window; this.allowedOrigin = options.allowedOrigin; this.readyCheckID = `${Date.now()}-${Math.random()}`; // Bind the listener method to preserve 'this' context. this.listener = this.handleMessage.bind(this); this.windowForEventListening.addEventListener("message", this.listener, false); // Start readiness check. this.readynessCheck = window.setInterval(() => { this.send({ method: this.readyMethod, params: this.readyCheckID }); }, 50); // Listen for readiness check responses. this.listen(this.readyMethod, (id) => { if (id === this.readyCheckID) { window.clearInterval(this.readynessCheck); this._ready = true; for (let i = 0; i < this.sendBuffer.length; i++) { this.send(this.sendBuffer[i]); } this.sendBuffer = []; } else { this.send({ method: this.readyMethod, params: id }); } }); } /** * Handles incoming postMessage events. * * @param {MessageEvent} event - The message event from postMessage. * @returns {void} */ handleMessage(event) { if (this.allowedOrigin && event.origin !== this.allowedOrigin) { return; } let data; try { data = safeJsonParse(event.data); } catch (e) { return; } if (data && data.postis && data.scope === this.scope) { const listenersForMethod = this.listeners[data.method]; if (listenersForMethod) { for (let i = 0; i < listenersForMethod.length; i++) { listenersForMethod[i].call(null, data.params); } } else { this.listenBuffer[data.method] = this.listenBuffer[data.method] || []; this.listenBuffer[data.method].push(data.params); } } } /** * Registers a listener for a specific method. * * @param {string} method - The method name to listen for. * @param {Function} callback - The callback function to invoke when the method is received. * @returns {void} */ listen(method, callback) { this.listeners[method] = this.listeners[method] || []; this.listeners[method].push(callback); const listenBufferForMethod = this.listenBuffer[method]; if (listenBufferForMethod) { const listenersForMethod = this.listeners[method]; for (let i = 0; i < listenersForMethod.length; i++) { for (let j = 0; j < listenBufferForMethod.length; j++) { listenersForMethod[i].call(null, listenBufferForMethod[j]); } } } delete this.listenBuffer[method]; } /** * Sends a message to the target window. * * @param {SendOptions} opts - The message options containing method and params. * @returns {void} */ send(opts) { const method = opts.method; if ((this._ready || opts.method === this.readyMethod) && (this.targetWindow && typeof this.targetWindow.postMessage === "function")) { this.targetWindow.postMessage(JSON.stringify({ postis: true, scope: this.scope, method: method, params: opts.params }), "*"); } else { this.sendBuffer.push(opts); } } /** * Executes the callback when the connection is ready. * * @param {Function} callback - The callback to execute when ready. * @returns {void} */ ready(callback) { if (this._ready) { callback(); } else { setTimeout(() => { this.ready(callback); }, 50); } } /** * Destroys the postis instance and cleans up resources. * * @param {Function} [callback] - Optional callback to execute after cleanup. * @returns {void} */ destroy(callback) { window.clearInterval(this.readynessCheck); this._ready = false; if (this.windowForEventListening && typeof this.windowForEventListening.removeEventListener === "function") { this.windowForEventListening.removeEventListener("message", this.listener); } callback && callback(); } }