| /* |
| * 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. |
| */ |
| |
| /** |
| *MyFaces core javascripting libraries |
| * |
| * Those are the central public API functions in the JSF2 |
| * Ajax API! They handle the entire form submit and ajax send |
| * and resolve cycle! |
| */ |
| |
| /** |
| * reserve the root namespace |
| */ |
| if ('undefined' != typeof OpenAjax && ('undefined' == typeof jsf || null == typeof jsf)) { |
| OpenAjax.hub.registerLibrary("jsf", "www.sun.com", "1.0", null); |
| } |
| //just in case openajax has failed (testing environment) |
| /** |
| * @ignore |
| */ |
| if (!window.jsf) { |
| /** |
| * @namespace jsf |
| */ |
| var jsf = new function() { |
| /* |
| * Version of the implementation for the jsf.js. |
| * <p /> |
| * as specified within the jsf specifications jsf.html: |
| * <ul> |
| * <li>left two digits major release number</li> |
| * <li>middle two digits minor spec release number</li> |
| * <li>right two digits bug release number</li> |
| * </ul> |
| * @constant |
| */ |
| this.specversion = 220000; |
| /** |
| * Implementation version as specified within the jsf specification. |
| * <p /> |
| * A number increased with every implementation version |
| * and reset by moving to a new spec release number |
| * |
| * @constant |
| */ |
| this.implversion = 0; |
| |
| /** |
| * SeparatorChar as defined by UINamingContainer.getNamingContainerSeparatorChar() |
| * @type {Char} |
| */ |
| this.separatorchar = getSeparatorChar(); |
| |
| /** |
| * This method is responsible for the return of a given project stage as defined |
| * by the jsf specification. |
| * <p/> |
| * Valid return values are: |
| * <ul> |
| * <li>"Production"</li> |
| * <li>"Development"</li> |
| * <li>"SystemTest"</li> |
| * <li>"UnitTest"</li> |
| * </li> |
| * |
| * @return {String} the current project state emitted by the server side method: |
| * <i>javax.faces.application.Application.getProjectStage()</i> |
| */ |
| this.getProjectStage = function() { |
| var impl = myfaces._impl.core._Runtime.getGlobalConfig("jsfAjaxImpl", myfaces._impl.core.Impl); |
| return impl.getProjectStage(); |
| }; |
| |
| /** |
| * collect and encode data for a given form element (must be of type form) |
| * find the javax.faces.ViewState element and encode its value as well! |
| * return a concatenated string of the encoded values! |
| * |
| * @throws an exception in case of the given element not being of type form! |
| * https://issues.apache.org/jira/browse/MYFACES-2110 |
| */ |
| this.getViewState = function(formElement) { |
| /*we are not allowed to add the impl on a global scope so we have to inline the code*/ |
| var impl = myfaces._impl.core._Runtime.getGlobalConfig("jsfAjaxImpl", myfaces._impl.core.Impl); |
| return impl.getViewState(formElement); |
| }; |
| |
| /** |
| * returns the window identifier for the given node / window |
| * @param {optional String | DomNode} the node for which the client identifier has to be determined |
| * @return the window identifier or null if none is found |
| */ |
| this.getClientWindow = function() { |
| /*we are not allowed to add the impl on a global scope so we have to inline the code*/ |
| var impl = myfaces._impl.core._Runtime.getGlobalConfig("jsfAjaxImpl", myfaces._impl.core.Impl); |
| return (arguments.length)? impl.getClientWindow(arguments[0]) : impl.getClientWindow(); |
| } |
| |
| //private helper functions |
| function getSeparatorChar() { |
| var impl = myfaces._impl.core._Runtime.getGlobalConfig("jsfAjaxImpl", myfaces._impl.core.Impl); |
| return impl.getSeparatorChar(); |
| } |
| |
| }; |
| //jsdoc helper to avoid warnings, we map later |
| window.jsf = jsf; |
| } |
| |
| /** |
| * just to make sure no questions arise, I simply prefer here a weak |
| * typeless comparison just in case some frameworks try to interfere |
| * by overriding null or fiddeling around with undefined or typeof in some ways |
| * it is safer in this case than the standard way of doing a strong comparison |
| **/ |
| if (!jsf.ajax) { |
| /** |
| * @namespace jsf.ajax |
| */ |
| jsf.ajax = new function() { |
| |
| |
| /** |
| * this function has to send the ajax requests |
| * |
| * following request conditions must be met: |
| * <ul> |
| * <li> the request must be sent asynchronously! </li> |
| * <li> the request must be a POST!!! request </li> |
| * <li> the request url must be the form action attribute </li> |
| * <li> all requests must be queued with a client side request queue to ensure the request ordering!</li> |
| * </ul> |
| * |
| * @param {String|Node} element: any dom element no matter being it html or jsf, from which the event is emitted |
| * @param {EVENT} event: any javascript event supported by that object |
| * @param {Map} options : map of options being pushed into the ajax cycle |
| */ |
| this.request = function(element, event, options) { |
| if (!options) { |
| options = {}; |
| } |
| /*we are not allowed to add the impl on a global scope so we have to inline the code*/ |
| var impl = myfaces._impl.core._Runtime.getGlobalConfig("jsfAjaxImpl", myfaces._impl.core.Impl); |
| return impl.request(element, event, options); |
| }; |
| |
| /** |
| * Adds an error handler to our global error queue. |
| * the error handler must be of the format <i>function errorListener(<errorData>)</i> |
| * with errorData being of following format: |
| * <ul> |
| * <li> errorData.type : "error"</li> |
| * <li> errorData.status : the error status message</li> |
| * <li> errorData.errorName : the server error name in case of a server error</li> |
| * <li> errorData.errorMessage : the server error message in case of a server error</li> |
| * <li> errorData.source : the issuing source element which triggered the request </li> |
| * <li> eventData.responseCode: the response code (aka http request response code, 401 etc...) </li> |
| * <li> eventData.responseText: the request response text </li> |
| * <li> eventData.responseXML: the request response xml </li> |
| * </ul> |
| * |
| * @param {function} errorListener error handler must be of the format <i>function errorListener(<errorData>)</i> |
| */ |
| this.addOnError = function(/*function*/errorListener) { |
| var impl = myfaces._impl.core._Runtime.getGlobalConfig("jsfAjaxImpl", myfaces._impl.core.Impl); |
| return impl.addOnError(errorListener); |
| }; |
| |
| /** |
| * Adds a global event listener to the ajax event queue. The event listener must be a function |
| * of following format: <i>function eventListener(<eventData>)</i> |
| * |
| * @param {function} eventListener event must be of the format <i>function eventListener(<eventData>)</i> |
| */ |
| this.addOnEvent = function(/*function*/eventListener) { |
| var impl = myfaces._impl.core._Runtime.getGlobalConfig("jsfAjaxImpl", myfaces._impl.core.Impl); |
| return impl.addOnEvent(eventListener); |
| }; |
| |
| /** |
| * processes the ajax response if the ajax request completes successfully |
| * @param request the ajax request! |
| * @param context the ajax context! |
| */ |
| this.response = function(/*xhr request object*/request, context) { |
| var impl = myfaces._impl.core._Runtime.getGlobalConfig("jsfAjaxImpl", myfaces._impl.core.Impl); |
| return impl.response(request, context); |
| }; |
| } |
| } |
| |
| if (!jsf.util) { |
| /** |
| * @namespace jsf.util |
| */ |
| jsf.util = new function() { |
| |
| /** |
| * varargs function which executes a chain of code (functions or any other code) |
| * |
| * if any of the code returns false, the execution |
| * is terminated prematurely skipping the rest of the code! |
| * |
| * @param {DomNode} source, the callee object |
| * @param {Event} event, the event object of the callee event triggering this function |
| * @param {optional} functions to be chained, if any of those return false the chain is broken |
| */ |
| this.chain = function(source, event) { |
| var impl = myfaces._impl.core._Runtime.getGlobalConfig("jsfAjaxImpl", myfaces._impl.core.Impl); |
| return impl.chain.apply(impl, arguments); |
| }; |
| } |
| } |
| |
| 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 = 0; |
| 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) { |
| 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) |
| || (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, channel, onopen, onmessage, onclose, behaviorScripts, autoconnect) { |
| |
| onclose = resolveFunction(onclose); |
| |
| 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; |
| } |
| } |
| |
| |
| (!window.myfaces) ? window.myfaces = {} : null; |
| if (!myfaces.ab) { |
| /* |
| * Shortcut of the jsf.ajax.request, to shorten the rendered JS. |
| */ |
| myfaces.ab = function(source, event, eventName, execute, render, options) { |
| if (!options) { |
| options = {}; |
| } |
| |
| if (eventName) { |
| options["javax.faces.behavior.event"] = eventName; |
| } |
| if (execute) { |
| options["execute"] = execute; |
| } |
| if (render) { |
| options["render"] = render; |
| } |
| |
| jsf.ajax.request(source, event, options); |
| }; |
| } |