UNPKG

jsf.js_next_gen

Version:

A next generation typescript reimplementation of jsf.js

259 lines 11.9 kB
"use strict"; /*! Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to you under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.PushImpl = void 0; /** * Typescript port of the faces\.push part in the myfaces implementation */ const Const_1 = require("./core/Const"); const mona_dish_1 = require("mona-dish"); /** * Implementation class for the push functionality */ var PushImpl; (function (PushImpl) { const URL_PROTOCOL = mona_dish_1.DQ.global().location.protocol.replace("http", "ws") + "//"; // we expose the member variables for testing purposes // they are not directly touched outside of tests /* socket map by token */ PushImpl.sockets = {}; /* component attributes by clientId */ PushImpl.components = {}; /* client ids by token (share websocket connection) */ PushImpl.clientIdsByTokens = {}; // needed for testing function reset() { PushImpl.sockets = {}; PushImpl.components = {}; PushImpl.clientIdsByTokens = {}; } PushImpl.reset = reset; /* * Api implementations, exposed functions */ /** * @param socketClientId the sockets client identifier * @param url the uri to reach the socket * @param channel the channel name/id * @param onopen The function to be invoked when the web socket is opened. * @param onmessage The function to be invoked when a message is received. * @param onerror The function to be invoked when an error occurs. * @param onclose The function to be invoked when the web socket is closed. * @param behaviors functions which are invoked whenever a message is received * @param autoConnect Whether or not to automatically open the socket. Defaults to <code>false</code>. */ function init(socketClientId, url, channel, onopen, onmessage, onerror, onclose, behaviors, autoConnect) { var _a, _b, _c; onclose = resolveFunction(onclose); if (!mona_dish_1.DQ.global().WebSocket) { // IE6-9. onclose(-1, channel); return; } let channelToken = url.substr(url.indexOf('?') + 1); if (!PushImpl.components[socketClientId]) { PushImpl.components[socketClientId] = { 'channelToken': channelToken, 'onopen': resolveFunction(onopen), 'onmessage': resolveFunction(onmessage), 'onerror': resolveFunction(onerror), 'onclose': onclose, 'behaviors': behaviors, 'autoconnect': autoConnect }; if (!PushImpl.clientIdsByTokens[channelToken]) { PushImpl.clientIdsByTokens[channelToken] = []; } PushImpl.clientIdsByTokens[channelToken].push(socketClientId); if (!PushImpl.sockets[channelToken]) { PushImpl.sockets[channelToken] = new Socket(channelToken, getBaseURL(url), channel); } } if (autoConnect) { ((_b = (_a = mona_dish_1.DQ.global()) === null || _a === void 0 ? void 0 : _a.faces) !== null && _b !== void 0 ? _b : (_c = mona_dish_1.DQ.global()) === null || _c === void 0 ? void 0 : _c.jsf).push.open(socketClientId); } } PushImpl.init = init; function open(socketClientId) { var _a; getSocket((_a = PushImpl.components[socketClientId]) === null || _a === void 0 ? void 0 : _a.channelToken).open(); } PushImpl.open = open; function close(socketClientId) { getSocket(PushImpl.components[socketClientId].channelToken).close(); } PushImpl.close = close; // Private helper classes // Private classes functions ---------------------------------------------------------------------------------- /** * Creates a reconnecting web socket. When the web socket successfully connects on first attempt, then it will * automatically reconnect on timeout with cumulative intervals of 500ms with a maximum of 25 attempts (~3 minutes). * The <code>onclose</code> function will be called with the error code of the last attempt. * @constructor * @param {string} channelToken the channel token associated with this websocket connection * @param {string} url The URL of the web socket * @param {string} channel The name of the web socket channel. */ class Socket { constructor(channelToken, url, channel) { this.channelToken = channelToken; this.url = url; this.channel = channel; this.reconnectAttempts = 0; } open() { if (this.socket && this.socket.readyState == 1) { return; } this.socket = new WebSocket(this.url); this.bindCallbacks(); } // noinspection JSUnusedLocalSymbols onopen(event) { var _a, _b; if (!this.reconnectAttempts) { let clientIds = PushImpl.clientIdsByTokens[this.channelToken]; for (let i = clientIds.length - 1; i >= 0; i--) { let socketClientId = clientIds[i]; (_b = (_a = PushImpl.components[socketClientId]) === null || _a === void 0 ? void 0 : _a['onopen']) === null || _b === void 0 ? void 0 : _b.call(_a, this.channel); } } this.reconnectAttempts = 0; } onerror(event) { var _a, _b, _c; let message = JSON.parse((_a = event === null || event === void 0 ? void 0 : event.data) !== null && _a !== void 0 ? _a : null); //TODO replace this with a more readable Stream code for (let i = PushImpl.clientIdsByTokens[this.channelToken].length - 1; i >= 0; i--) { let socketClientId = PushImpl.clientIdsByTokens[this.channelToken][i]; if (document.getElementById(socketClientId)) { try { (_c = (_b = PushImpl.components[socketClientId]) === null || _b === void 0 ? void 0 : _b['onerror']) === null || _c === void 0 ? void 0 : _c.call(_b, message, this.channel, event); } catch (e) { //Ignore } } else { PushImpl.clientIdsByTokens[this.channelToken].splice(i, 1); } } if (PushImpl.clientIdsByTokens[this.channelToken].length == 0) { // tag disappeared this.close(); } } onmmessage(event) { var _a, _b, _c; let message = JSON.parse(event.data); for (let i = PushImpl.clientIdsByTokens[this.channelToken].length - 1; i >= 0; i--) { let socketClientId = PushImpl.clientIdsByTokens[this.channelToken][i]; if (document.getElementById(socketClientId)) { try { (_b = (_a = PushImpl.components[socketClientId]) === null || _a === void 0 ? void 0 : _a['onmessage']) === null || _b === void 0 ? void 0 : _b.call(_a, message, this.channel, event); } catch (e) { //Ignore } let behaviors = (_c = PushImpl.components === null || PushImpl.components === void 0 ? void 0 : PushImpl.components[socketClientId]) === null || _c === void 0 ? void 0 : _c['behaviors']; let functions = behaviors === null || behaviors === void 0 ? void 0 : behaviors[message]; if (functions && functions.length) { for (let j = 0; j < functions.length; j++) { try { functions[j](null); } catch (e) { //Ignore } } } } else { PushImpl.clientIdsByTokens[this.channelToken].splice(i, 1); } } if (PushImpl.clientIdsByTokens[this.channelToken].length == 0) { // tag disappeared this.close(); } } onclose(event) { var _a, _b; if (!this.socket || (event.code == 1000 && event.reason == Const_1.REASON_EXPIRED) || (event.code == 1008) || (!this.reconnectAttempts) || (this.reconnectAttempts >= Const_1.MAX_RECONNECT_ATTEMPTS)) { let clientIds = PushImpl.clientIdsByTokens[this.channelToken]; for (let i = clientIds.length - 1; i >= 0; i--) { let socketClientId = clientIds[i]; (_b = (_a = PushImpl.components === null || PushImpl.components === void 0 ? void 0 : PushImpl.components[socketClientId]) === null || _a === void 0 ? void 0 : _a['onclose']) === null || _b === void 0 ? void 0 : _b.call(_a, event === null || event === void 0 ? void 0 : event.code, this === null || this === void 0 ? void 0 : this.channel, event); } } else { setTimeout(this.open, Const_1.RECONNECT_INTERVAL * this.reconnectAttempts++); } } ; close() { if (this.socket) { let s = this.socket; this.socket = null; s.close(); } } /** * bind the callbacks to the socket callbacks */ bindCallbacks() { this.socket.onopen = (event) => this.onopen(event); this.socket.onmessage = (event) => this.onmmessage(event); this.socket.onclose = (event) => this.onclose(event); this.socket.onerror = (event) => this.onerror(event); } } // Private static functions --------------------------------------------------------------------------------------- function getBaseURL(url) { if (url.indexOf("://") < 0) { let base = mona_dish_1.DQ.global().location.hostname + ":" + mona_dish_1.DQ.global().location.port; return URL_PROTOCOL + base + url; } else { return url; } } /** * Get socket associated with given channelToken. * @param channelToken The name of the web socket channelToken. * @return Socket associated with given channelToken. * @throws Error, when the channelToken is unknown, you may need to initialize * it first via <code>init()</code> function. */ function getSocket(channelToken) { let socket = PushImpl.sockets[channelToken]; if (socket) { return socket; } else { throw new Error("Unknown channelToken: " + channelToken); } } function resolveFunction(fn = () => { }) { return ((typeof fn !== "function") && (fn = mona_dish_1.DQ.global()[fn]), fn); } })(PushImpl || (exports.PushImpl = PushImpl = {})); //# sourceMappingURL=PushImpl.js.map