/** @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 <close /> 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]);
}
);