You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2016/10/24 23:01:20 UTC
svn commit: r1766457 -
/myfaces/core/branches/2.3.x/api/src/main/javascript/META-INF/resources/myfaces/api/jsf.js
Author: lu4242
Date: Mon Oct 24 23:01:20 2016
New Revision: 1766457
URL: http://svn.apache.org/viewvc?rev=1766457&view=rev
Log:
MYFACES-4069 Implement f:websocket and related api (javascript)
Modified:
myfaces/core/branches/2.3.x/api/src/main/javascript/META-INF/resources/myfaces/api/jsf.js
Modified: myfaces/core/branches/2.3.x/api/src/main/javascript/META-INF/resources/myfaces/api/jsf.js
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.3.x/api/src/main/javascript/META-INF/resources/myfaces/api/jsf.js?rev=1766457&r1=1766456&r2=1766457&view=diff
==============================================================================
--- myfaces/core/branches/2.3.x/api/src/main/javascript/META-INF/resources/myfaces/api/jsf.js (original)
+++ myfaces/core/branches/2.3.x/api/src/main/javascript/META-INF/resources/myfaces/api/jsf.js Mon Oct 24 23:01:20 2016
@@ -227,4 +227,233 @@ if (!jsf.util) {
}
}
+if (!jsf.push) {
+ /**
+ * @namespace jsf.push
+ */
+ jsf.push = new function() {
+
+ // "Constant" fields ----------------------------------------------------------------------------------------------
+ var URL_PROTOCOL = window.location.protocol.replace("http", "ws") + "//";
+ var RECONNECT_INTERVAL = 500;
+ var MAX_RECONNECT_ATTEMPTS = 25;
+ var REASON_EXPIRED = "Expired";
+
+ // Private static fields ------------------------------------------------------------------------------------------
+
+ /* socket map by token */
+ var sockets = {};
+ /* component attributes by clientId */
+ var components = {};
+ /* client ids by token (share websocket connection) */
+ var clientIdsByTokens = {};
+ var self = {};
+
+ // Private constructor 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.
+ */
+ function Socket(channelToken, url, channel) {
+
+ // Private fields -----------------------------------------------------------------------------------------
+
+ var socket;
+ var reconnectAttempts;
+ var self = this;
+
+ // Public functions ---------------------------------------------------------------------------------------
+
+ /**
+ * Opens the reconnecting web socket.
+ */
+ self.open = function() {
+ if (socket && socket.readyState == 1) {
+ return;
+ }
+
+ socket = new WebSocket(url);
+
+ socket.onopen = function(event) {
+ if (reconnectAttempts == null) {
+ var clientIds = clientIdsByTokens[channelToken];
+ for (var i = clientIds.length - 1; i >= 0; i--){
+ var socketClientId = clientIds[i];
+ components[socketClientId]['onopen'](channel);
+ }
+ }
+ reconnectAttempts = 0;
+ }
+
+ socket.onmessage = function(event) {
+ var message = JSON.parse(event.data);
+ for (var i = clientIdsByTokens[channelToken].length - 1; i >= 0; i--){
+ var socketClientId = clientIdsByTokens[channelToken][i];
+ if(document.getElementById(socketClientId)) {
+ try{
+ components[socketClientId]['onmessage'](message, channel, event);
+ }catch(e){
+ //Ignore
+ }
+ var behaviors = components[socketClientId]['behaviors'];
+ var functions = behaviors[message];
+ if (functions && functions.length) {
+ for (var j = 0; j < functions.length; j++) {
+ try{
+ functions[j](null);
+ }catch(e){
+ //Ignore
+ }
+ }
+ }
+ } else {
+ clientIdsByTokens[channelToken].splice(i,1);
+ }
+ }
+ if (clientIdsByTokens[channelToken].length == 0){
+ //tag dissapeared
+ self.close();
+ }
+
+ }
+
+ socket.onclose = function(event) {
+ if (!socket
+ || (event.code == 1000 && event.reason == REASON_EXPIRED)
+ || (event.code == 1008)
+ || (reconnectAttempts == null)
+ || (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS))
+ {
+ var clientIds = clientIdsByTokens[channelToken];
+ for (var i = clientIds.length - 1; i >= 0; i--){
+ var socketClientId = clientIds[i];
+ components[socketClientId]['onclose'](event.code, channel, event);
+ }
+ }
+ else {
+ setTimeout(self.open, RECONNECT_INTERVAL * reconnectAttempts++);
+ }
+ }
+ }
+
+ /**
+ * Closes the reconnecting web socket.
+ */
+ self.close = function() {
+ if (socket) {
+ var s = socket;
+ socket = null;
+ s.close();
+ }
+ }
+
+ }
+
+ // Public static functions ----------------------------------------------------------------------------------------
+
+ /**
+ *
+ * @param {function} onopen The function to be invoked when the web socket is opened.
+ * @param {function} onmessage The function to be invoked when a message is received.
+ * @param {function} onclose The function to be invoked when the web socket is closed.
+ * @param {boolean} autoconnect Whether or not to immediately open the socket. Defaults to <code>false</code>.
+ */
+ this.init = function(socketClientId, uri, onopen, onmessage, onclose, behaviorScripts, autoconnect) {
+
+ onclose = resolveFunction(onclose);
+ var channel = uri.split(/\?/)[0];
+
+ if (!window.WebSocket) { // IE6-9.
+ onclose(-1, channel);
+ return;
+ }
+
+ var channelToken = uri.substr(uri.indexOf('?')+1);
+
+ if (!components[socketClientId]) {
+ components[socketClientId] = {
+ 'channelToken': channelToken,
+ 'onopen': resolveFunction(onopen),
+ 'onmessage' : resolveFunction(onmessage),
+ 'onclose': onclose,
+ 'behaviors': behaviorScripts,
+ 'autoconnect': autoconnect};
+ if (!clientIdsByTokens[channelToken]) {
+ clientIdsByTokens[channelToken] = [];
+ }
+ clientIdsByTokens[channelToken].push(socketClientId);
+ if (!sockets[channelToken]){
+ sockets[channelToken] = new Socket(channelToken,
+ getBaseURL(uri), channel);
+ }
+ }
+
+ if (autoconnect) {
+ this.open(socketClientId);
+ }
+ }
+
+ /**
+ * Open the web socket on the given channel.
+ * @param {string} channel The name of the web socket channel.
+ * @throws {Error} When channel is unknown.
+ */
+ this.open = function(socketClientId) {
+ getSocket(components[socketClientId]['channelToken']).open();
+ }
+
+ /**
+ * Close the web socket on the given channel.
+ * @param {string} channel The name of the web socket channel.
+ * @throws {Error} When channel is unknown.
+ */
+ this.close = function(socketClientId) {
+ getSocket(components[socketClientId]['channelToken']).close();
+ }
+
+ // Private static functions ---------------------------------------------------------------------------------------
+
+ /**
+ *
+ */
+ function getBaseURL(url) {
+ if (url.indexOf("://") < 0)
+ {
+ var base = window.location.hostname+":"+window.location.port
+ return URL_PROTOCOL + base + url;
+ }else
+ {
+ return url;
+ }
+ }
+
+ /**
+ * Get socket associated with given channelToken.
+ * @param {string} channelToken The name of the web socket channelToken.
+ * @return {Socket} Socket associated with given channelToken.
+ * @throws {Error} When channelToken is unknown, you may need to initialize
+ * it first via <code>init()</code> function.
+ */
+ function getSocket(channelToken) {
+ var socket = sockets[channelToken];
+ if (socket) {
+ return socket;
+ } else {
+ throw new Error("Unknown channelToken: " + channelToken);
+ }
+ }
+
+ function resolveFunction(fn) {
+ return (typeof fn !== "function") && (fn = window[fn] || function(){}), fn;
+ }
+ // Expose self to public ------------------------------------------------------------------------------------------
+
+ //return self;
+ }
+}