/** @type {ConnectionManager} */ let manager; const Status = { ERROR: 0, CONNECTING: 1, CONNFAIL: 2, AUTHENTICATING: 3, AUTHFAIL: 4, CONNECTED: 5, DISCONNECTED: 6, DISCONNECTING: 7, ATTACHED: 8, REDIRECT: 9, CONNTIMEOUT: 10, BINDREQUIRED: 11, ATTACHFAIL: 12, }; /** Class: ConnectionManager * * Manages the shared websocket connection as well as the ports of the * connected tabs. */ class ConnectionManager { constructor() { /** @type {MessagePort[]} */ this.ports = []; } /** @param {MessagePort} port */ addPort(port) { this.ports.push(port); port.addEventListener('message', (e) => { const method = e.data[0]; try { this[/** @type {'send'|'_closeSocket'}*/ (method)](e.data.splice(1)); } catch (e) { console?.error(e); } }); port.start(); } /** * @param {[string, string]} data */ _connect(data) { this.jid = data[1]; this._closeSocket(); this.socket = new WebSocket(data[0], 'xmpp'); this.socket.onopen = () => this._onOpen(); this.socket.onerror = (e) => this._onError(e); this.socket.onclose = (e) => this._onClose(e); this.socket.onmessage = (message) => this._onMessage(message); } _attach() { if (this.socket && this.socket.readyState !== WebSocket.CLOSED) { this.ports.forEach((p) => p.postMessage(['_attachCallback', Status.ATTACHED, this.jid])); } else { this.ports.forEach((p) => p.postMessage(['_attachCallback', Status.ATTACHFAIL])); } } /** @param {string} str */ send(str) { this.socket.send(str); } /** @param {string} str */ close(str) { if (this.socket && this.socket.readyState !== WebSocket.CLOSED) { try { this.socket.send(str); } catch (e) { this.ports.forEach((p) => p.postMessage(['log', 'error', e])); this.ports.forEach((p) => p.postMessage(['log', 'error', "Couldn't send tag."])); } } } _onOpen() { this.ports.forEach((p) => p.postMessage(['_onOpen'])); } /** @param {CloseEvent} e */ _onClose(e) { this.ports.forEach((p) => p.postMessage(['_onClose', e.reason])); } /** @param {MessageEvent} message */ _onMessage(message) { const o = { 'data': message.data }; this.ports.forEach((p) => p.postMessage(['_onMessage', o])); } /** @param {Event} error */ _onError(error) { this.ports.forEach((p) => p.postMessage(['_onError', error])); } _closeSocket() { if (this.socket) { try { this.socket.onclose = null; this.socket.onerror = null; this.socket.onmessage = null; this.socket.close(); } catch (e) { this.ports.forEach((p) => p.postMessage(['log', 'error', e])); } } this.socket = null; } } addEventListener( 'connect', /** @param {MessageEvent} e */ (e) => { manager = manager || new ConnectionManager(); manager.addPort(e.ports[0]); } );