@jitsi/js-utils
Version:
Utilities for Jitsi JS projects
152 lines (151 loc) • 5.24 kB
JavaScript
/* 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();
}
}