You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lo...@apache.org on 2019/01/02 09:53:38 UTC

[myfaces-tobago] 01/04: TOBAGO-1951 Update tobago-jsf.js with JSF 2.3

This is an automated email from the ASF dual-hosted git repository.

lofwyr pushed a commit to branch tobago-4.x
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 66fb28e2422c6c6443f9993a65e0b7be13412f9a
Author: Udo Schnurpfeil <lo...@apache.org>
AuthorDate: Mon Nov 12 20:13:28 2018 +0100

    TOBAGO-1951 Update tobago-jsf.js with JSF 2.3
    
    (cherry picked from commit 601f3ad5da6954afb2beb0d5bf37405dee2f5bf2)
---
 .../tobago-bootstrap/_version/js/tobago-jsf.js     | 744 +++++++++++++++------
 1 file changed, 546 insertions(+), 198 deletions(-)

diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/js/tobago-jsf.js b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/js/tobago-jsf.js
index 80d4788..0bfc4f8 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/js/tobago-jsf.js
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/META-INF/resources/tobago/standard/tobago-bootstrap/_version/js/tobago-jsf.js
@@ -15,7 +15,7 @@
  */
 
 /*
- * XXX This is a modified copy of MyFaces 2.2.13-SNAPSHOT (2018-04-04) jsf-uncompressed-full.js
+ * XXX This is a modified copy of MyFaces 2.3.3-SNAPSHOT (2018-11-12) jsf-uncompressed-full.js
  * XXX Modifications are marked with TOBAGO-JSF-JS
  */
 
@@ -5182,14 +5182,18 @@ if (_MF_SINGLTN) {
                 return v.value;	//	string
             }
 
-            // this should work on Opera 7, but it's a little on the crashy side
-            if ((node.getAttributeNode) && (node.getAttributeNode(ta))) {
-                return (node.getAttributeNode(ta)).value;	//	string
-            } else if (node.getAttribute(ta)) {
+
+
+            if (node.getAttribute(ta)) {
                 return node.getAttribute(ta);	//	string
             } else if (node.getAttribute(ta.toLowerCase())) {
                 return node.getAttribute(ta.toLowerCase());	//	string
+
             }
+            //there used to be a getAttributeNode check here for really old
+            //browsers, I had to remove it because of a firefox warning
+            //which uses a regexp scan for this method to be deprecated
+
             return null;	//	string
         },
 
@@ -5660,6 +5664,9 @@ if (!myfaces.oam) {
                         form.submit();
                     }
                     catch(e) {
+                        if (window.console) {
+                            console.error(e);
+                        }
                     }
                 }
 
@@ -5669,6 +5676,9 @@ if (!myfaces.oam) {
                     form.submit();
                 }
                 catch(e) {
+                    if (window.console) {
+                        console.error(e);
+                    }
                 }
             }
 
@@ -7242,28 +7252,29 @@ _MF_CLS(_PFX_XHR+"_IFrameRequest", myfaces._impl.xhrCore._AjaxRequest,
 _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhrCore._AjaxResponse.prototype */ {
 
     /*partial response types*/
-    RESP_PARTIAL:"partial-response",
-    RESP_TYPE_ERROR:"error",
-    RESP_TYPE_REDIRECT:"redirect",
-    RESP_TYPE_CHANGES:"changes",
+    RESP_PARTIAL: "partial-response",
+    RESP_TYPE_ERROR: "error",
+    RESP_TYPE_REDIRECT: "redirect",
+    RESP_TYPE_CHANGES: "changes",
 
     /*partial commands*/
-    CMD_CHANGES:"changes",
-    CMD_UPDATE:"update",
-    CMD_DELETE:"delete",
-    CMD_INSERT:"insert",
-    CMD_EVAL:"eval",
-    CMD_ERROR:"error",
-    CMD_ATTRIBUTES:"attributes",
-    CMD_EXTENSION:"extension",
-    CMD_REDIRECT:"redirect",
+    CMD_CHANGES: "changes",
+    CMD_UPDATE: "update",
+    CMD_DELETE: "delete",
+    CMD_INSERT: "insert",
+    CMD_EVAL: "eval",
+    CMD_ERROR: "error",
+    CMD_ATTRIBUTES: "attributes",
+    CMD_EXTENSION: "extension",
+    CMD_REDIRECT: "redirect",
 
     /*other constants*/
-    P_VIEWSTATE:"javax.faces.ViewState",
+    P_VIEWSTATE: "javax.faces.ViewState",
     P_CLIENTWINDOW: "javax.faces.ClientWindow",
-    P_VIEWROOT:"javax.faces.ViewRoot",
-    P_VIEWHEAD:"javax.faces.ViewHead",
-    P_VIEWBODY:"javax.faces.ViewBody",
+    P_VIEWROOT: "javax.faces.ViewRoot",
+    P_VIEWHEAD: "javax.faces.ViewHead",
+    P_VIEWBODY: "javax.faces.ViewBody",
+    P_RESOURCE: "javax.faces.Resource",
 
     /**
      * uses response to start Html element replacement
@@ -7277,7 +7288,7 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
      * and if the CDATA block contains a body section the document body must be replaced!
      *
      */
-    processResponse:function (request, context) {
+    processResponse: function (request, context) {
         //mfinternal handling, note, the mfinternal is only optional
         //according to the spec
         context._mfInternal = context._mfInternal || {};
@@ -7288,6 +7299,8 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
         mfInternal._updateForms = [];
         mfInternal.appliedViewState = null;
         mfInternal.appliedClientWindow = null;
+        mfInternal.namingModeId = null;
+
 
         try {
             var _Impl = this.attr("impl"), _Lang = this._Lang;
@@ -7323,6 +7336,18 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
                 }
             }
 
+
+            /**
+             * jsf 2.3 naming mode partial response,
+             * we either viewstate all forms (non id mode)
+             * or the forms under the viewroot defined by id
+             *
+             * @type {string} ... the naming mode id is set or an empty string
+             * definitely not a null value to avoid type confusions later on
+             */
+            mfInternal.namingModeId = (partials.id || "");
+
+
             var childNodesLength = partials.childNodes.length;
 
             for (var loop = 0; loop < childNodesLength; loop++) {
@@ -7350,21 +7375,28 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
             }
 
             //fixup missing viewStates due to spec deficiencies
-            if(mfInternal.appliedViewState) {
+            if (mfInternal.appliedViewState) {
                 this.fixViewStates(context);
             }
-            if(mfInternal.appliedClientWindow) {
+            if (mfInternal.appliedClientWindow) {
                 this.fixClientWindows(context);
             }
 
             //spec jsdoc, the success event must be sent from response
             _Impl.sendEvent(request, context, _Impl["SUCCESS"]);
+        } catch (e) {
 
+            if (window.console && window.console.error) {
+                //any error should be logged
+                console.error(e);
+            }
+            throw e;
         } finally {
             delete mfInternal._updateElems;
             delete mfInternal._updateForms;
             delete mfInternal.appliedViewState;
             delete mfInternal.appliedClientWindow;
+            delete mfInternal.namingModeId;
         }
     },
 
@@ -7373,7 +7405,7 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
      *
      * @param context
      */
-    fixViewStates:function (context) {
+    fixViewStates: function (context) {
         var _Lang = this._Lang;
         var mfInternal = context._mfInternal;
 
@@ -7381,82 +7413,73 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
             return;
         }
 
-        //if we set our no portlet env we safely can update all forms with
-        //the new viewstate
-        if (this._RT.getLocalOrGlobalConfig(context, "no_portlet_env", false)) {
-            for (var cnt = document.forms.length - 1; cnt >= 0; cnt--) {
-                this._setVSTCWForm(context, document.forms[cnt], mfInternal.appliedViewState, this.P_VIEWSTATE);
-            }
-            return;
-        }
-
-        // Now update the forms that were not replaced but forced to be updated, because contains child ajax tags
-        // we should only update forms with view state hidden field. If by some reason, the form was set to be
-        // updated but the form was replaced, it does not have hidden view state, so later in changeTrace processing the
-        // view state is updated.
-
-        //set the viewstates of all outer forms parents of our updated elements
-
-        _Lang.arrForEach(mfInternal._updateForms, function (elem) {
-            this._setVSTCWForm(context, elem, mfInternal.appliedViewState, this.P_VIEWSTATE);
-        }, 0, this);
-
-        //set the viewstate of all forms within our updated elements
-        _Lang.arrForEach(mfInternal._updateElems, function (elem) {
-            this._setVSTCWInnerForms(context, elem, mfInternal.appliedViewState, this.P_VIEWSTATE);
-        }, 0, this);
+        /**
+         * JSF 2.3 we set all the viewstates under a given declared viewRoot or all forms
+         * if none is given
+         */
+        this._updateJSFClientArtifacts(context,  mfInternal.appliedViewState, this.P_VIEWSTATE);
     },
 
-    fixClientWindows:function (context, theForm) {
+
+    fixClientWindows: function (context, theForm) {
         var _Lang = this._Lang;
         var mfInternal = context._mfInternal;
 
         if (null == mfInternal.appliedClientWindow) {
             return;
         }
-         //if we set our no portlet env we safely can update all forms with
-        //the new viewstate
-        if (this._RT.getLocalOrGlobalConfig(context, "no_portlet_env", false)) {
-            for (var cnt = document.forms.length - 1; cnt >= 0; cnt--) {
-                this._setVSTCWForm(context, document.forms[cnt], mfInternal.appliedClientWindow, this.P_CLIENTWINDOW);
-            }
-            return;
-        }
-        //set the client window of all outer form of updated elements
 
-        _Lang.arrForEach(mfInternal._updateForms, function (elem) {
-            this._setVSTCWForm(context, elem, mfInternal.appliedClientWindow, this.P_CLIENTWINDOW);
-        }, 0, this);
+        /**
+         * JSF 2.3 we set all the viewstates under a given declared viewRoot or all forms
+         * if none is given
+         */
+
+        this._updateJSFClientArtifacts(context, mfInternal.appliedClientWindow, this.P_CLIENTWINDOW);
 
-        //set the client window of all forms within our updated elements
-        _Lang.arrForEach(mfInternal._updateElems, function (elem) {
-            this._setVSTCWInnerForms(context, elem, mfInternal.appliedClientWindow, this.P_CLIENTWINDOW);
-        }, 0, this);
     },
 
+
     /**
-     * sets the viewstate element in a given form
+     * sets the a jsf artifact element with a given identifier to a new value or adds this element
      *
-     * @param theForm the form to which the element has to be set to
+     * @param theForm {Node} the form to which the element has to be set to
      * @param context the current request context
      */
-    _setVSTCWForm:function (context, theForm, value, identifier) {
-        theForm = this._Lang.byId(theForm);
-        var mfInternal = context._mfInternal;
+    _applyJSFArtifactValueToForm: function (context, theForm, value, identifier) {
 
         if (!theForm) return;
+        var _Lang = this._Lang;
+        var _Dom = this._Dom;
+        var prefix = this._getPrefix(context);
 
         //in IE7 looking up form elements with complex names (such as 'javax.faces.ViewState') fails in certain cases
         //iterate through the form elements to find the element, instead
-        var fieldToApply = this._Dom.getNamedElementFromForm(theForm, identifier);
+        var fieldsFound = [];
 
-        if (fieldToApply) {
-            this._Dom.setAttribute(fieldToApply, "value", value);
-        } else if (!fieldToApply) {
+        var elements = theForm.elements;
+        for (var i = 0, l = elements.length; i < l; i++) {
+            var e = elements[i];
+            //https://issues.apache.org/jira/browse/MYFACES-4230
+            //ie11 has a deviation from the standard behavior, we have to remap the null/undefined name
+            //to an empty string
+            var eName = e.name || "";
+
+            if (eName.indexOf(identifier) != -1) {
+                fieldsFound.push(e);
+            }
+        }
+
+        if (fieldsFound.length) {
+            _Lang.arrForEach(fieldsFound, function (fieldFound) {
+                _Dom.setAttribute(fieldFound, "value", value);
+            });
+        } else {
             var element = this._Dom.getDummyPlaceHolder();
-            //spec error, two elements with the same id should not be there, TODO recheck the space if the name does not suffice alone
-            element.innerHTML = ["<input type='hidden'", "id='", identifier+jsf.separatorchar+Math.random() , "' name='", identifier , "' value='" , value , "' />"].join("");
-            //now we go to proper dom handling after having to deal with another ie screwup
+
+            //per JSF 2.3 spec the identifier of the element must be unique in the dom tree
+            //otherwise we will break the html spec here
+            element.innerHTML = ["<input type='hidden'", "id='", this._fetchUniqueId(prefix, identifier), "' name='", identifier, "' value='", value, "' />"].join("");
+            //now we go to proper dom handling after having to deal with another ie screw-up
             try {
                 theForm.appendChild(element.childNodes[0]);
             } finally {
@@ -7465,25 +7488,125 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
         }
     },
 
-    _setVSTCWInnerForms:function (context, elem, value, identifier) {
+    _fetchUniqueId: function(prefix, identifier) {
+        var cnt = 0;
+        var retVal = prefix + identifier + jsf.separatorchar + cnt;
+        while(this._Dom.byId(retVal) != null) {
+            cnt++;
+            retVal = prefix + identifier + jsf.separatorchar + cnt;
+        }
+        return retVal;
+    },
+
+    /**
+     * updates/inserts the jsf client artifacts under a given viewroot element
+     *
+     * @param context the client context holding all request context data and some internal data
+     * @param elem the root to start with, must be a dom node not an identifier
+     * @param value the new value
+     * @param identifier the identifier for the client artifact aka javax.faces.ViewState, ClientWindowId etc...
+     *
+     * @private
+     */
+    _updateJSFClientArtifacts: function (context, value, identifier) {
 
-        var _Lang = this._Lang, _Dom = this._Dom;
-        elem = _Dom.byIdOrName(elem);
         //elem not found for whatever reason
         //https://issues.apache.org/jira/browse/MYFACES-3544
-        if (!elem) return;
 
-        var replacedForms = _Dom.findByTagName(elem, "form", false);
+        var prefix = this._getPrefix(context);
 
-        var applyVST = _Lang.hitch(this, function (elem) {
-            this._setVSTCWForm(context, elem, value, identifier);
-        });
+        //do we still need the issuing form update? I guess it is needed.
+        //jsf spec 2.3 and earlier all issuing forms must update
+        var sourceForm = (context._mfInternal._mfSourceFormId) ? this._Dom.byId(context._mfInternal._mfSourceFormId) : null;
+        if (sourceForm) {
+            sourceForm = this._Dom.byId(sourceForm);
+            if (sourceForm) {
+                //some cases where the source form cannot be updated
+                //because it is gone
+                this._applyJSFArtifactValueToForm(context, sourceForm, value, identifier);
+            }
+        }
+
+
+
+        var viewRoot = this._getViewRoot(context);
+        var forms = this._Dom.findByTagNames(viewRoot, {"form": 1}) || [];
+
+        //since the spec thanks to the over intrusive portlet api still is broken
+        //we need our old fallback hack for proper handling without having
+        //to deal with multiple render targets.
+
+
+        if(this._RT.getLocalOrGlobalConfig(context, "no_portlet_env", false)) {
+
+            //We update all elements under viewroot
+            //this clearly violates the jsf 2.3 jsdocs
+            //however I think that the jsdocs were sloppily updated
+            //because just updating the render targets under one viewroot and the issuing form
+            //again would leave broken viewstates, in the end the portlet spec is at fault here
+            //which came late to the game and expected all frameworks to adapt to their needs.
+            //instead of properly adapting to the frameworks
+            //now the viewroot mechanism per se would work, but people are dropping
+            //jsf 2.3 into old portlet containers which then expose the legacy behavior
+            //of having just one view root.
+            this._Lang.arrForEach(forms, this._Lang.hitch(this, function (elem) {
+                //update all forms which start with prefix (all render and execute targets
+                this._applyJSFArtifactValueToForm(context, elem, value, identifier);
+            }));
+        } else {
+
+
+            //check for a portlet condition a present viewroot
+
+            var viewRootId = viewRoot.id || "";
+
+            for(var cnt = 0; cnt < context._mfInternal._updateForms.length; cnt++) {
+                var updateForm = context._mfInternal._updateForms[cnt];
+
+                //follow the spec 2.3 path 1:1 we update the forms hosting the render targets which start
+                //with the viewroot
+                //if there is a viewroot present, however we seem to have a bug in myfaces
+                //even if we have a naming container response we
+                //cannot rely on the naming container being prefixed
+
+                //This atm is not bad, because we safely can assume
+                //that if no viewroot can be found we are under
+                //one single viewroot and can omit the prefix check
+                //(aka fallback into the old behavior)
+
+
+                if(updateForm.indexOf(viewRootId) != 0) {
+                    continue;
+                } else { //either an empty viewroot, or a namespace match
+                    this._applyJSFArtifactValueToForm(context, this._Dom.byId(updateForm), value, identifier);
+                }
+            }
 
-        try {
-            _Lang.arrForEach(replacedForms, applyVST, 0, this);
-        } finally {
-            applyVST = null;
         }
+
+    },
+
+    _getViewRoot: function (context) {
+        var prefix = this._getPrefix(context);
+        if (prefix == "") {
+            return document.getElementsByTagName("body")[0];
+        }
+        prefix = prefix.substr(0, prefix.length - 1);
+        var viewRoot = document.getElementById(prefix);
+        if (viewRoot) {
+            return viewRoot;
+        }
+        return document.getElementsByTagName("body")[0];
+    },
+
+
+    _getPrefix: function (context) {
+        var mfInternal = context._mfInternal;
+        var prefix = mfInternal.namingModeId;
+        if (prefix != "") {
+            prefix = prefix + jsf.separatorchar;
+        }
+        return prefix;
     },
 
     /**
@@ -7493,7 +7616,7 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
      * @param context the contect object
      * @param node the node in the xml hosting the error message
      */
-    processError:function (request, context, node) {
+    processError: function (request, context, node) {
         /**
          * <error>
          *      <error-name>String</error-name>
@@ -7501,7 +7624,7 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
          * <error>
          */
         var errorName = node.firstChild.textContent || node.firstChild.text || "",
-                errorMessage = node.childNodes[1].firstChild.data || "";
+            errorMessage = node.childNodes[1].firstChild.data || "";
 
         this.attr("impl").sendError(request, context, this.attr("impl").SERVER_ERROR, errorName, errorMessage, "myfaces._impl.xhrCore._AjaxResponse", "processError");
     },
@@ -7512,7 +7635,7 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
      * @param context the context
      * @param node the node hosting the redirect data
      */
-    processRedirect:function (request, context, node) {
+    processRedirect: function (request, context, node) {
         /**
          * <redirect url="url to redirect" />
          */
@@ -7527,7 +7650,8 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
         }
         window.location = redirectUrl;
         return true;
-    },
+    }
+    ,
 
     /**
      * main entry point for processing the changes
@@ -7538,7 +7662,7 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
      * @param context the context map
      * @param node the changes node to be processed
      */
-    processChanges:function (request, context, node) {
+    processChanges: function (request, context, node) {
         var changes = node.childNodes;
         var _Lang = this._Lang;
         //note we need to trace the changes which could affect our insert update or delete
@@ -7584,73 +7708,35 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
      * @param context the context map
      * @param node the changes node to be processed
      */
-    processUpdate:function (request, context, node) {
-        if ( (node.getAttribute('id').indexOf(this.P_VIEWSTATE) != -1) || (node.getAttribute('id').indexOf(this.P_CLIENTWINDOW) != -1) ) {
-            //update the submitting forms viewstate to the new value
-            // The source form has to be pulled out of the CURRENT document first because the context object
-            // may refer to an invalid document if an update of the entire body has occurred before this point.
-            var mfInternal = context._mfInternal,
-                    fuzzyFormDetection = this._Lang.hitch(this._Dom, this._Dom.fuzzyFormDetection);
-            var elemId = (mfInternal._mfSourceControlId) ? mfInternal._mfSourceControlId :
-                    ((context.source) ? context.source.id : null);
-
-            //theoretically a source of null can be given, then our form detection fails for
-            //the source element case and hence updateviewstate is skipped for the source
-            //form, but still render targets still can get the viewstate
-            var sourceForm = (mfInternal && mfInternal["_mfSourceFormId"] &&
-                    document.forms[mfInternal["_mfSourceFormId"]]) ?
-                    document.forms[mfInternal["_mfSourceFormId"]] : ((elemId) ? fuzzyFormDetection(elemId) : null);
-
-            if(node.getAttribute('id').indexOf(this.P_VIEWSTATE) != -1) {
+    processUpdate: function (request, context, node) {
+        var mfInternal = context._mfInternal;
+        if ((node.getAttribute('id').indexOf(this.P_VIEWSTATE) != -1) || (node.getAttribute('id').indexOf(this.P_CLIENTWINDOW) != -1)) {
+            if (node.getAttribute('id').indexOf(this.P_VIEWSTATE) != -1) {
 // begin TOBAGO-JSF-JS
                 mfInternal.appliedViewState = this._Lang.trim(this._Dom.concatCDATABlocks(node));
 /* original
                 mfInternal.appliedViewState = this._Dom.concatCDATABlocks(node);//node.firstChild.nodeValue;
 */
 // end TOBAGO-JSF-JS
-            } else if(node.getAttribute('id').indexOf(this.P_CLIENTWINDOW) != -1) {
+            } else if (node.getAttribute('id').indexOf(this.P_CLIENTWINDOW) != -1) {
                 mfInternal.appliedClientWindow = node.firstChild.nodeValue;
             }
-            //source form could not be determined either over the form identifer or the element
-            //we now skip this phase and just add everything we need for the fixup code
-
-            if (!sourceForm) {
-                //no source form found is not an error because
-                //we might be able to recover one way or the other
-                return true;
-            }
-
-            mfInternal._updateForms.push(sourceForm.id);
-
         }
         else {
             // response may contain several blocks
             var cDataBlock = this._Dom.concatCDATABlocks(node),
-                    resultNode = null,
-                    pushOpRes = this._Lang.hitch(this, this._pushOperationResult);
+                resultNode = null,
+                pushOpRes = this._Lang.hitch(this, this._pushOperationResult);
 
             switch (node.getAttribute('id')) {
                 case this.P_VIEWROOT:
 
                     cDataBlock = cDataBlock.substring(cDataBlock.indexOf("<html"));
-// begin TOBAGO-JSF-JS
-//                     var newDoc = document.open("text/html", "replace");
-//                     newDoc.write(cDataBlock);
-//                     newDoc.close();
-                //change only the body, because change the head doesn't work with CSP
-                    var newPage = jQuery(cDataBlock).filter(".tobago-page");
-                    jQuery(".tobago-page").replaceWith(newPage);
-                    resultNode = document.documentElement;
 
-/* original
                     var parsedData = this._replaceHead(request, context, cDataBlock);
 
-                    resultNode = ('undefined' != typeof parsedData && null != parsedData) ? this._replaceBody(request, context, cDataBlock, parsedData) : this._replaceBody(request, context, cDataBlock);
-*/
-// end TOBAGO-JSF-JS
-                    if (resultNode) {
-                        pushOpRes(context, resultNode);
-                    }
+                    ('undefined' != typeof parsedData && null != parsedData) ? this._replaceBody(request, context, cDataBlock, parsedData) : this._replaceBody(request, context, cDataBlock);
+
                     break;
                 case this.P_VIEWHEAD:
                     //we cannot replace the head, almost no browser allows this, some of them throw errors
@@ -7665,8 +7751,12 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
                         pushOpRes(context, resultNode);
                     }
                     break;
+                case this.P_RESOURCE:
 
+                    this._addResourceToHead(request, context, cDataBlock);
+                    break;
                 default:
+
 // begin TOBAGO-JSF-JS
 // XXX hack
                 if (node.getAttribute('id') === document.getElementsByClassName("tobago-page")[0].getAttribute('id')) {
@@ -7692,9 +7782,9 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
         return true;
     },
 
-    _pushOperationResult:function (context, resultNode) {
+    _pushOperationResult: function(context, resultNode) {
         var mfInternal = context._mfInternal;
-        var pushSubnode = this._Lang.hitch(this, function (currNode) {
+        var pushSubnode = this._Lang.hitch(this, function(currNode) {
             var parentForm = this._Dom.getParent(currNode, "form");
             //if possible we work over the ids
             //so that elements later replaced are referenced
@@ -7706,17 +7796,40 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
                 mfInternal._updateElems.push(currNode.id || currNode);
             }
         });
+
+        var pushEmbedded = this._Lang.hitch(this, function(currNode) {
+            if(currNode.tagName && this._Lang.equalsIgnoreCase(currNode.tagName, "form")) {
+                if(currNode.id)  { //should not happen but just in case someone manipulates the html
+                    mfInternal._updateForms.push(currNode.id);
+                }
+            } else {
+                var childForms = this._Dom.findByTagName(currNode, "form");
+                if(childForms && childForms.length) {
+                    for(var cnt = 0; cnt < childForms.length; cnt++) {
+                        if(childForms[cnt].id) {
+                            mfInternal._updateForms.push(childForms[cnt].id);
+                        }
+                    }
+                }
+            }
+
+        });
+
+
         var isArr = 'undefined' != typeof resultNode.length && 'undefined' == typeof resultNode.nodeType;
         if (isArr && resultNode.length) {
             for (var cnt = 0; cnt < resultNode.length; cnt++) {
                 pushSubnode(resultNode[cnt]);
+                pushEmbedded(resultNode[cnt]);
             }
         } else if (!isArr) {
             pushSubnode(resultNode);
+            pushEmbedded(resultNode);
         }
 
     },
 
+
     /**
      * replaces a current head theoretically,
      * pratically only the scripts are evaled anew since nothing else
@@ -7728,15 +7841,15 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
      *
      * @return an xml representation of the page for further processing if possible
      */
-    _replaceHead:function (request, context, newData) {
+    _replaceHead: function (request, context, newData) {
 
         var _Lang = this._Lang,
-                _Dom = this._Dom,
-                isWebkit = this._RT.browser.isWebKit,
-        //we have to work around an xml parsing bug in Webkit
-        //see https://issues.apache.org/jira/browse/MYFACES-3061
-                doc = (!isWebkit) ? _Lang.parseXML(newData) : null,
-                newHead = null;
+            _Dom = this._Dom,
+            isWebkit = this._RT.browser.isWebKit,
+            //we have to work around an xml parsing bug in Webkit
+            //see https://issues.apache.org/jira/browse/MYFACES-3061
+            doc = (!isWebkit) ? _Lang.parseXML(newData) : null,
+            newHead = null;
 
         if (!isWebkit && _Lang.isXMLParseError(doc)) {
             doc = _Lang.parseXML(newData.replace(/<!\-\-[\s\n]*<!\-\-/g, "<!--").replace(/\/\/-->[\s\n]*\/\/-->/g, "//-->"));
@@ -7765,7 +7878,7 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
             newHead = doc.getElementsByTagName("head")[0];
         }
 
-        var oldTags = _Dom.findByTagNames(document.getElementsByTagName("head")[0], {"link":true, "style":true});
+        var oldTags = _Dom.findByTagNames(document.getElementsByTagName("head")[0], {"link": true, "style": true});
         _Dom.runCss(newHead, true);
         _Dom.deleteItems(oldTags);
 
@@ -7776,6 +7889,13 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
         return doc;
     },
 
+    _addResourceToHead: function (request, context, newData) {
+        var lastHeadChildTag = document.getElementsByTagName("head")[0].lastChild;
+
+        this._Dom.insertAfter(lastHeadChildTag, newData);
+
+    },
+
     /**
      * special method to handle the body dom manipulation,
      * replacing the entire body does not work fully by simply adding a second body
@@ -7787,14 +7907,14 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
      * @param {String} newData the markup which replaces the old dom node!
      * @param {Node} parsedData (optional) preparsed XML representation data of the current document
      */
-    _replaceBody:function (request, context, newData /*varargs*/) {
+    _replaceBody: function (request, context, newData /*varargs*/) {
         var _RT = this._RT,
-                _Dom = this._Dom,
-                _Lang = this._Lang,
+            _Dom = this._Dom,
+            _Lang = this._Lang,
 
-                oldBody = document.getElementsByTagName("body")[0],
-                placeHolder = document.createElement("div"),
-                isWebkit = _RT.browser.isWebKit;
+            oldBody = document.getElementsByTagName("body")[0],
+            placeHolder = document.createElement("div"),
+            isWebkit = _RT.browser.isWebKit;
 
         placeHolder.id = "myfaces_bodyplaceholder";
 
@@ -7860,11 +7980,11 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
      * @param {Object} itemIdToReplace (String|Node) - ID of the element to replace
      * @param {String} markup - the new tag
      */
-    replaceHtmlItem:function (request, context, itemIdToReplace, markup) {
+    replaceHtmlItem: function (request, context, itemIdToReplace, markup) {
         var _Lang = this._Lang, _Dom = this._Dom;
 
         var item = (!_Lang.isString(itemIdToReplace)) ? itemIdToReplace :
-                _Dom.byIdOrName(itemIdToReplace);
+            _Dom.byIdOrName(itemIdToReplace);
 
         if (!item) {
             throw this._raiseError(new Error(), _Lang.getMessage("ERR_ITEM_ID_NOTFOUND", null, "_AjaxResponse.replaceHtmlItem", (itemIdToReplace) ? itemIdToReplace.toString() : "undefined"), "replaceHtmlItem");
@@ -7881,12 +8001,12 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
      * @return true upon successful completion, false otherwise
      *
      **/
-    processInsert:function (request, context, node) {
+    processInsert: function (request, context, node) {
         /*remapping global namespaces for speed and readability reasons*/
         var _Dom = this._Dom,
-                _Lang = this._Lang,
-        //determine which path to go:
-                insertData = this._parseInsertData(request, context, node);
+            _Lang = this._Lang,
+            //determine which path to go:
+            insertData = this._parseInsertData(request, context, node);
 
         if (!insertData) return false;
 
@@ -7920,18 +8040,18 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
      * TODO we have to find a mechanism to replace the direct sendError calls with a javascript exception
      * which we then can use for cleaner error code handling
      */
-    _parseInsertData:function (request, context, node) {
+    _parseInsertData: function (request, context, node) {
         var _Lang = this._Lang,
-                _Dom = this._Dom,
-                concatCDATA = _Dom.concatCDATABlocks,
+            _Dom = this._Dom,
+            concatCDATA = _Dom.concatCDATABlocks,
 
-                INSERT_TYPE_BEFORE = "insertBefore",
-                INSERT_TYPE_AFTER = "insertAfter",
+            INSERT_TYPE_BEFORE = "insertBefore",
+            INSERT_TYPE_AFTER = "insertAfter",
 
-                id = node.getAttribute("id"),
-                beforeId = node.getAttribute("before"),
-                afterId = node.getAttribute("after"),
-                ret = {};
+            id = node.getAttribute("id"),
+            beforeId = node.getAttribute("before"),
+            afterId = node.getAttribute("after"),
+            ret = {};
 
         //now we have to make a distinction between two different parsing paths
         //due to a spec malalignment
@@ -7964,18 +8084,18 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
             ret.cDataBlock = concatCDATA(node.childNodes[0]);
         } else {
             throw this._raiseError(new Error(), [_Lang.getMessage("ERR_PPR_IDREQ"),
-                                                 "\n ",
-                                                 _Lang.getMessage("ERR_PPR_INSERTBEFID")].join(""), "_parseInsertData");
+                "\n ",
+                _Lang.getMessage("ERR_PPR_INSERTBEFID")].join(""), "_parseInsertData");
         }
         ret.opId = _Lang.trim(ret.opId);
         return ret;
     },
 
-    processDelete:function (request, context, node) {
+    processDelete: function (request, context, node) {
 
         var _Lang = this._Lang,
-                _Dom = this._Dom,
-                deleteId = node.getAttribute('id');
+            _Dom = this._Dom,
+            deleteId = node.getAttribute('id');
 
         if (!deleteId) {
             throw this._raiseError(new Error(), _Lang.getMessage("ERR_PPR_UNKNOWNCID", null, "_AjaxResponse.processDelete", ""), "processDelete");
@@ -7995,7 +8115,7 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
         return true;
     },
 
-    processAttributes:function (request, context, node) {
+    processAttributes: function (request, context, node) {
         //we now route into our attributes function to bypass
         //IE quirks mode incompatibilities to the biggest possible extent
         //most browsers just have to do a setAttributes but IE
@@ -8003,8 +8123,8 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
         //myfaces._impl._util.this._Dom.setAttribute(domNode, attribute, value;
 
         var _Lang = this._Lang,
-        //<attributes id="id of element"> <attribute name="attribute name" value="attribute value" />* </attributes>
-                elemId = node.getAttribute('id');
+            //<attributes id="id of element"> <attribute name="attribute name" value="attribute value" />* </attributes>
+            elemId = node.getAttribute('id');
 
         if (!elemId) {
             throw this._raiseError(new Error(), "Error in attributes, id not in xml markup", "processAttributes");
@@ -8016,8 +8136,8 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
         }
         for (var loop2 = 0; loop2 < childNodes.length; loop2++) {
             var attributesNode = childNodes[loop2],
-                    attrName = attributesNode.getAttribute("name"),
-                    attrValue = attributesNode.getAttribute("value");
+                attrName = attributesNode.getAttribute("name"),
+                attrValue = attributesNode.getAttribute("value");
 
             if (!attrName) {
                 continue;
@@ -8058,7 +8178,7 @@ _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhr
      * @param title the title of the error (optional)
      * @param name the name of the error (optional)
      */
-    _raiseError:function (error, message, caller, title, name) {
+    _raiseError: function (error, message, caller, title, name) {
         var _Impl = this.attr("impl");
         var finalTitle = title || _Impl.MALFORMEDXML;
         var finalName = name || _Impl.MALFORMEDXML;
@@ -8912,8 +9032,8 @@ _MF_SINGLTN(_PFX_CORE + "Impl", _MF_OBJECT, /**  @lends myfaces._impl.core.Impl.
      * @param {Object} request the request object which comes from the xhr cycle
      * @param {Object} context (Map) the context object being pushed over the xhr cycle keeping additional metadata
      * @param {String} name the error name
-     * @param {String} serverErrorName the server error name in case of a server error
-     * @param {String} serverErrorMessage the server error message in case of a server error
+     * @param {String} errorName the server error name in case of a server error
+     * @param {String} errorMessage the server error message in case of a server error
      * @param {String} caller optional caller reference for extended error messages
      * @param {String} callFunc optional caller Function reference for extended error messages
      *
@@ -8926,12 +9046,12 @@ _MF_SINGLTN(_PFX_CORE + "Impl", _MF_OBJECT, /**  @lends myfaces._impl.core.Impl.
      *
      *
      */
-    sendError:function sendError(/*Object*/request, /*Object*/ context, /*String*/ name, /*String*/ serverErrorName, /*String*/ serverErrorMessage, caller, callFunc) {
+    sendError:function sendError(/*Object*/request, /*Object*/ context, /*String*/ name, /*String*/ errorName, /*String*/ errorMessage, caller, callFunc) {
         var _Lang = myfaces._impl._util._Lang;
         var UNKNOWN = _Lang.getMessage("UNKNOWN");
 
         var eventData = {};
-        //we keep this in a closure because we might reuse it for our serverErrorMessage
+        //we keep this in a closure because we might reuse it for our errorMessage
         var malFormedMessage = function () {
             return (name && name === myfaces._impl.core.Impl.MALFORMEDXML) ? _Lang.getMessage("ERR_MALFORMEDXML") : "";
         };
@@ -8941,8 +9061,8 @@ _MF_SINGLTN(_PFX_CORE + "Impl", _MF_OBJECT, /**  @lends myfaces._impl.core.Impl.
         eventData.type = this.ERROR;
 
         eventData.status = name || UNKNOWN;
-        eventData.serverErrorName = serverErrorName || UNKNOWN;
-        eventData.serverErrorMessage = serverErrorMessage || UNKNOWN;
+        eventData.errorName = errorName || UNKNOWN;
+        eventData.errorMessage = errorMessage || UNKNOWN;
 
         try {
             eventData.source = context.source || UNKNOWN;
@@ -8954,9 +9074,9 @@ _MF_SINGLTN(_PFX_CORE + "Impl", _MF_OBJECT, /**  @lends myfaces._impl.core.Impl.
         }
         //extended error message only in dev mode
         if (jsf.getProjectStage() === "Development") {
-            eventData.serverErrorMessage = eventData.serverErrorMessage || "";
-            eventData.serverErrorMessage = (caller) ? eventData.serverErrorMessage + "\nCalling class: " + caller : eventData.serverErrorMessage;
-            eventData.serverErrorMessage = (callFunc) ? eventData.serverErrorMessage + "\n Calling function: " + callFunc : eventData.serverErrorMessage;
+            eventData.errorMessage = eventData.errorMessage || "";
+            eventData.errorMessage = (caller) ? eventData.errorMessage + "\nCalling class: " + caller : eventData.errorMessage;
+            eventData.errorMessage = (callFunc) ? eventData.errorMessage + "\n Calling function: " + callFunc : eventData.errorMessage;
         }
 
         /**/
@@ -8974,14 +9094,14 @@ _MF_SINGLTN(_PFX_CORE + "Impl", _MF_OBJECT, /**  @lends myfaces._impl.core.Impl.
             //we remap the function to achieve a better compressability
                     pushMsg = _Lang.hitch(finalMessage, finalMessage.push);
 
-            (serverErrorMessage) ? pushMsg(_Lang.getMessage("MSG_ERROR_MESSAGE") + " " + serverErrorMessage + "\n") : null;
+            (errorMessage) ? pushMsg(_Lang.getMessage("MSG_ERROR_MESSAGE") + " " + errorMessage + "\n") : null;
 
             pushMsg(DIVIDER);
 
             (caller) ? pushMsg("Calling class:" + caller) : null;
             (callFunc) ? pushMsg("Calling function:" + callFunc) : null;
             (name) ? pushMsg(_Lang.getMessage("MSG_ERROR_NAME") + " " + name) : null;
-            (serverErrorName && name != serverErrorName) ? pushMsg("Server error name: " + serverErrorName) : null;
+            (errorName && name != errorName) ? pushMsg("Server error name: " + errorName) : null;
 
             pushMsg(malFormedMessage());
             pushMsg(DIVIDER);
@@ -9474,8 +9594,8 @@ if (!jsf.ajax) {
 		* <ul>
          *     <li> errorData.type : &quot;error&quot;</li>
          *     <li> errorData.status : the error status message</li>
-         *     <li> errorData.serverErrorName : the server error name in case of a server error</li>
-         *     <li> errorData.serverErrorMessage : the server error message in case of a server error</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>
@@ -9535,5 +9655,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 = 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;
+  }
+}