1 /*
  2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3  *
  4  * Copyright 1997-2012 Sun Microsystems, Inc. All rights reserved.
  5  *
  6  * The contents of this file are subject to the terms of either the GNU
  7  * General Public License Version 2 only ("GPL") or the Common Development
  8  * and Distribution License("CDDL") (collectively, the "License").  You
  9  * may not use this file except in compliance with the License. You can obtain
 10  * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 11  * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 12  * language governing permissions and limitations under the License.
 13  *
 14  * When distributing the software, include this License Header Notice in each
 15  * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 16  * Sun designates this particular file as subject to the "Classpath" exception
 17  * as provided by Sun in the GPL Version 2 section of the License file that
 18  * accompanied this code.  If applicable, add the following below the License
 19  * Header, with the fields enclosed by brackets [] replaced by your own
 20  * identifying information: "Portions Copyrighted [year]
 21  * [name of copyright owner]"
 22  *
 23  * Contributor(s):
 24  *
 25  * If you wish your version of this file to be governed by only the CDDL or
 26  * only the GPL Version 2, indicate your decision by adding "[Contributor]
 27  * elects to include this software in this distribution under the [CDDL or GPL
 28  * Version 2] license."  If you don't indicate a single choice of license, a
 29  * recipient has the option to distribute your version of this file under
 30  * either the CDDL, the GPL Version 2 or to extend the choice of license to
 31  * its licensees as provided above.  However, if you add GPL Version 2 code
 32  * and therefore, elected the GPL Version 2 license, then the option applies
 33  * only if the new code is made subject to such option by the copyright
 34  * holder.
 35  *
 36  *
 37  * This file incorporates work covered by the following copyright and
 38  * permission notices:
 39  *
 40  * Copyright 2004 The Apache Software Foundation
 41  * Copyright 2004-2008 Emmanouil Batsis, mailto: mbatsis at users full stop sourceforge full stop net
 42  *
 43  * Licensed under the Apache License, Version 2.0 (the "License");
 44  * you may not use this file except in compliance with the License.
 45  * You may obtain a copy of the License at
 46  *
 47  *     http://www.apache.org/licenses/LICENSE-2.0
 48  *
 49  * Unless required by applicable law or agreed to in writing, software
 50  * distributed under the License is distributed on an "AS IS" BASIS,
 51  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 52  * See the License for the specific language governing permissions and
 53  * limitations under the License.
 54  */
 55 
 56 /**
 57  @project JSF JavaScript Library
 58  @version 2.2
 59  @description This is the standard implementation of the JSF JavaScript Library.
 60  */
 61 
 62 /**
 63  * Register with OpenAjax
 64  */
 65 if (typeof OpenAjax !== "undefined" &&
 66     typeof OpenAjax.hub.registerLibrary !== "undefined") {
 67     OpenAjax.hub.registerLibrary("jsf", "www.sun.com", "2.2", null);
 68 }
 69 
 70 // Detect if this is already loaded, and if loaded, if it's a higher version
 71 if (!((jsf && jsf.specversion && jsf.specversion >= 20000 ) &&
 72       (jsf.implversion && jsf.implversion >= 3))) {
 73 
 74     /**
 75      * <span class="changed_modified_2_2">The top level global namespace
 76      * for JavaServer Faces functionality.</span>
 77 
 78      * @name jsf
 79      * @namespace
 80      */
 81     var jsf = {};
 82 
 83     /**
 84 
 85      * <span class="changed_modified_2_2">The namespace for Ajax
 86      * functionality.</span>
 87 
 88      * @name jsf.ajax
 89      * @namespace
 90      * @exec
 91      */
 92     jsf.ajax = function() {
 93 
 94         var eventListeners = [];
 95         var errorListeners = [];
 96 
 97         var delayHandler = null;
 98         /**
 99          * Determine if the current browser is part of Microsoft's failed attempt at
100          * standards modification.
101          * @ignore
102          */
103         var isIE = function isIE() {
104             if (typeof isIECache !== "undefined") {
105                 return isIECache;
106             }
107             isIECache =
108                    document.all && window.ActiveXObject &&
109                    navigator.userAgent.toLowerCase().indexOf("msie") > -1 &&
110                    navigator.userAgent.toLowerCase().indexOf("opera") == -1;
111             return isIECache;
112         };
113         var isIECache;
114 
115         /**
116          * Determine if loading scripts into the page executes the script.
117          * This is instead of doing a complicated browser detection algorithm.  Some do, some don't.
118          * @returns {boolean} does including a script in the dom execute it?
119          * @ignore
120          */
121         var isAutoExec = function isAutoExec() {
122             try {
123                 if (typeof isAutoExecCache !== "undefined") {
124                     return isAutoExecCache;
125                 }
126                 var autoExecTestString = "<script>var mojarra = mojarra || {};mojarra.autoExecTest = true;</script>";
127                 var tempElement = document.createElement('span');
128                 tempElement.innerHTML = autoExecTestString;
129                 var body = document.getElementsByTagName('body')[0];
130                 var tempNode = body.appendChild(tempElement);
131                 if (mojarra && mojarra.autoExecTest) {
132                     isAutoExecCache = true;
133                     delete mojarra.autoExecTest;
134                 } else {
135                     isAutoExecCache = false;
136                 }
137                 deleteNode(tempNode);
138                 return isAutoExecCache;
139             } catch (ex) {
140                 // OK, that didn't work, we'll have to make an assumption
141                 if (typeof isAutoExecCache === "undefined") {
142                     isAutoExecCache = false;
143                 }
144                 return isAutoExecCache;
145             }
146         };
147         var isAutoExecCache;
148 
149         /**
150          * @ignore
151          */
152         var getTransport = function getTransport() {
153             var methods = [
154                 function() {
155                     return new XMLHttpRequest();
156                 },
157                 function() {
158                     return new ActiveXObject('Msxml2.XMLHTTP');
159                 },
160                 function() {
161                     return new ActiveXObject('Microsoft.XMLHTTP');
162                 }
163             ];
164 
165             var returnVal;
166             for (var i = 0, len = methods.length; i < len; i++) {
167                 try {
168                     returnVal = methods[i]();
169                 } catch(e) {
170                     continue;
171                 }
172                 return returnVal;
173             }
174             throw new Error('Could not create an XHR object.');
175         };
176 
177         /**
178          * Find instance of passed String via getElementById
179          * @ignore
180          */
181         var $ = function $() {
182             var results = [], element;
183             for (var i = 0; i < arguments.length; i++) {
184                 element = arguments[i];
185                 if (typeof element == 'string') {
186                     element = document.getElementById(element);
187                 }
188                 results.push(element);
189             }
190             return results.length > 1 ? results : results[0];
191         };
192 
193         /**
194          * Get the form element which encloses the supplied element.
195          * @param element - element to act against in search
196          * @returns form element representing enclosing form, or first form if none found.
197          * @ignore
198          */
199         var getForm = function getForm(element) {
200             if (element) {
201                 var form = $(element);
202                 while (form) {
203 
204                     if (form.nodeName && (form.nodeName.toLowerCase() == 'form')) {
205                         return form;
206                     }
207                     if (form.form) {
208                         return form.form;
209                     }
210                     if (form.parentNode) {
211                         form = form.parentNode;
212                     } else {
213                         form = null;
214                     }
215                 }
216                 return document.forms[0];
217             }
218             return null;
219         };
220 
221         /**
222          * Check if a value exists in an array
223          * @ignore
224          */
225         var isInArray = function isInArray(array, value) {
226             for (var i = 0; i < array.length; i++) {
227                 if (array[i] === value) {
228                     return true;
229                 }
230             }
231             return false;
232         };
233 
234 
235         /**
236          * Evaluate JavaScript code in a global context.
237          * @param src JavaScript code to evaluate
238          * @ignore
239          */
240         var globalEval = function globalEval(src) {
241             if (window.execScript) {
242                 window.execScript(src);
243                 return;
244             }
245             // We have to wrap the call in an anon function because of a firefox bug, where this is incorrectly set
246             // We need to explicitly call window.eval because of a Chrome peculiarity
247             var fn = function() {
248                 window.eval.call(window,src);
249             };
250             fn();
251         };
252 
253         /**
254          * Get all scripts from supplied string, return them as an array for later processing.
255          * @param str
256          * @returns {array} of script text
257          * @ignore
258          */
259         var stripScripts = function stripScripts(str) {
260             // Regex to find all scripts in a string
261             var findscripts = /<script[^>]*>([\S\s]*?)<\/script>/igm;
262             // Regex to find one script, to isolate it's content [2] and attributes [1]
263             var findscript = /<script([^>]*)>([\S\s]*?)<\/script>/im;
264             // Regex to remove leading cruft
265             var stripStart = /^\s*(<!--)*\s*(\/\/)*\s*(\/\*)*\s*\n*\**\n*\s*\*.*\n*\s*\*\/(<!\[CDATA\[)*/;
266             // Regex to find src attribute
267             var findsrc = /src="([\S]*?)"/im;
268             var findtype = /type="([\S]*?)"/im;
269             var initialnodes = [];
270             var scripts = [];
271             initialnodes = str.match(findscripts);
272             while (!!initialnodes && initialnodes.length > 0) {
273                 var scriptStr = [];
274                 scriptStr = initialnodes.shift().match(findscript);
275                 // check the type - skip if it not javascript type
276                 var type = [];
277                 type = scriptStr[1].match(findtype);
278                 if ( !!type && type[1]) {
279                     if (type[1] !== "text/javascript") {
280                         continue;
281                     }
282                 }
283                 var src = [];
284                 // check if src specified
285                 src = scriptStr[1].match(findsrc);
286                 var script;
287                 if ( !!src && src[1]) {
288                     // if this is a file, load it
289                     var url = src[1];
290                     // if this is another copy of jsf.js, don't load it
291                     // it's never necessary, and can make debugging difficult
292                     if (/\/javax.faces.resource\/jsf.js\?ln=javax\.faces/.test(url)) {
293                         script = false;
294                     } else {
295                         script = loadScript(url);
296                     }
297                 } else if (!!scriptStr && scriptStr[2]){
298                     // else get content of tag, without leading CDATA and such
299                     script = scriptStr[2].replace(stripStart,"");
300                 } else {
301                     script = false;
302                 }
303                 if (!!script) {
304                     scripts.push(script);
305                 }
306             }
307             return scripts;
308         };
309 
310         /**
311          * Load a script via a url, use synchronous XHR request.  This is liable to be slow,
312          * but it's probably the only correct way.
313          * @param url the url to load
314          * @ignore
315          */
316         var loadScript = function loadScript(url) {
317             var xhr = getTransport();
318             if (xhr === null) {
319                 return "";
320             }
321 
322             xhr.open("GET", url, false);
323             xhr.setRequestHeader("Content-Type", "application/x-javascript");
324             xhr.send(null);
325 
326             // PENDING graceful error handling
327             if (xhr.readyState == 4 && xhr.status == 200) {
328                     return xhr.responseText;
329             }
330 
331             return "";
332         };
333 
334         /**
335          * Run an array of scripts text
336          * @param scripts array of script nodes
337          * @ignore
338          */
339         var runScripts = function runScripts(scripts) {
340             if (!scripts || scripts.length === 0) {
341                 return;
342             }
343 
344             var head = document.getElementsByTagName('head')[0] || document.documentElement;
345             while (scripts.length) {
346                 // create script node
347                 var scriptNode = document.createElement('script');
348                 scriptNode.type = 'text/javascript';
349                 scriptNode.text = scripts.shift(); // add the code to the script node
350                 head.appendChild(scriptNode); // add it to the page
351                 head.removeChild(scriptNode); // then remove it
352             }
353         };
354 
355         /**
356          * Replace DOM element with a new tagname and supplied innerHTML
357          * @param element element to replace
358          * @param tempTagName new tag name to replace with
359          * @param src string new content for element
360          * @ignore
361          */
362         var elementReplaceStr = function elementReplaceStr(element, tempTagName, src) {
363 
364             var temp = document.createElement(tempTagName);
365             if (element.id) {
366                 temp.id = element.id;
367             }
368 
369             // Creating a head element isn't allowed in IE, and faulty in most browsers,
370             // so it is not allowed
371             if (element.nodeName.toLowerCase() === "head") {
372                 throw new Error("Attempted to replace a head element - this is not allowed.");
373             } else {
374                 var scripts = [];
375                 if (isAutoExec()) {
376                     temp.innerHTML = src;
377                 } else {
378                     // Get scripts from text
379                     scripts = stripScripts(src);
380                     // Remove scripts from text
381                     src = src.replace(/<script[^>]*type="text\/javascript"*>([\S\s]*?)<\/script>/igm,"");
382                     temp.innerHTML = src;
383                 }
384             }
385 
386             replaceNode(temp, element);            
387             cloneAttributes(temp, element);
388             runScripts(scripts);
389 
390         };
391 
392         /**
393          * Get a string with the concatenated values of all string nodes under the given node
394          * @param  oNode the given DOM node
395          * @param  deep boolean - whether to recursively scan the children nodes of the given node for text as well. Default is <code>false</code>
396          * @ignore
397          * Note:  This code originally from Sarissa: http://dev.abiss.gr/sarissa
398          * It has been modified to fit into the overall codebase
399          */
400         var getText = function getText(oNode, deep) {
401             var Node = {ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4,
402                 ENTITY_REFERENCE_NODE: 5,  ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7,
403                 COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10,
404                 DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12};
405 
406             var s = "";
407             var nodes = oNode.childNodes;
408             for (var i = 0; i < nodes.length; i++) {
409                 var node = nodes[i];
410                 var nodeType = node.nodeType;
411                 if (nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE) {
412                     s += node.data;
413                 } else if (deep === true && (nodeType == Node.ELEMENT_NODE ||
414                                              nodeType == Node.DOCUMENT_NODE ||
415                                              nodeType == Node.DOCUMENT_FRAGMENT_NODE)) {
416                     s += getText(node, true);
417                 }
418             }
419             return s;
420         };
421 
422         var PARSED_OK = "Document contains no parsing errors";
423         var PARSED_EMPTY = "Document is empty";
424         var PARSED_UNKNOWN_ERROR = "Not well-formed or other error";
425         var getParseErrorText;
426         if (isIE()) {
427             /**
428              * Note: This code orginally from Sarissa: http://dev.abiss.gr/sarissa
429              * @ignore
430              */
431             getParseErrorText = function (oDoc) {
432                 var parseErrorText = PARSED_OK;
433                 if (oDoc && oDoc.parseError && oDoc.parseError.errorCode && oDoc.parseError.errorCode !== 0) {
434                     parseErrorText = "XML Parsing Error: " + oDoc.parseError.reason +
435                                      "\nLocation: " + oDoc.parseError.url +
436                                      "\nLine Number " + oDoc.parseError.line + ", Column " +
437                                      oDoc.parseError.linepos +
438                                      ":\n" + oDoc.parseError.srcText +
439                                      "\n";
440                     for (var i = 0; i < oDoc.parseError.linepos; i++) {
441                         parseErrorText += "-";
442                     }
443                     parseErrorText += "^\n";
444                 }
445                 else if (oDoc.documentElement === null) {
446                     parseErrorText = PARSED_EMPTY;
447                 }
448                 return parseErrorText;
449             };
450         } else { // (non-IE)
451 
452             /**
453              * <p>Returns a human readable description of the parsing error. Useful
454              * for debugging. Tip: append the returned error string in a <pre>
455              * element if you want to render it.</p>
456              * @param  oDoc The target DOM document
457              * @returns {String} The parsing error description of the target Document in
458              *          human readable form (preformated text)
459              * @ignore
460              * Note:  This code orginally from Sarissa: http://dev.abiss.gr/sarissa
461              */
462             getParseErrorText = function (oDoc) {
463                 var parseErrorText = PARSED_OK;
464                 if ((!oDoc) || (!oDoc.documentElement)) {
465                     parseErrorText = PARSED_EMPTY;
466                 } else if (oDoc.documentElement.tagName == "parsererror") {
467                     parseErrorText = oDoc.documentElement.firstChild.data;
468                     parseErrorText += "\n" + oDoc.documentElement.firstChild.nextSibling.firstChild.data;
469                 } else if (oDoc.getElementsByTagName("parsererror").length > 0) {
470                     var parsererror = oDoc.getElementsByTagName("parsererror")[0];
471                     parseErrorText = getText(parsererror, true) + "\n";
472                 } else if (oDoc.parseError && oDoc.parseError.errorCode !== 0) {
473                     parseErrorText = PARSED_UNKNOWN_ERROR;
474                 }
475                 return parseErrorText;
476             };
477         }
478 
479         if ((typeof(document.importNode) == "undefined") && isIE()) {
480             try {
481                 /**
482                  * Implementation of importNode for the context window document in IE.
483                  * If <code>oNode</code> is a TextNode, <code>bChildren</code> is ignored.
484                  * @param oNode the Node to import
485                  * @param bChildren whether to include the children of oNode
486                  * @returns the imported node for further use
487                  * @ignore
488                  * Note:  This code orginally from Sarissa: http://dev.abiss.gr/sarissa
489                  */
490                 document.importNode = function(oNode, bChildren) {
491                     var tmp;
492                     if (oNode.nodeName == '#text') {
493                         return document.createTextNode(oNode.data);
494                     }
495                     else {
496                         if (oNode.nodeName == "tbody" || oNode.nodeName == "tr") {
497                             tmp = document.createElement("table");
498                         }
499                         else if (oNode.nodeName == "td") {
500                             tmp = document.createElement("tr");
501                         }
502                         else if (oNode.nodeName == "option") {
503                             tmp = document.createElement("select");
504                         }
505                         else {
506                             tmp = document.createElement("div");
507                         }
508                         if (bChildren) {
509                             tmp.innerHTML = oNode.xml ? oNode.xml : oNode.outerHTML;
510                         } else {
511                             tmp.innerHTML = oNode.xml ? oNode.cloneNode(false).xml : oNode.cloneNode(false).outerHTML;
512                         }
513                         return tmp.getElementsByTagName("*")[0];
514                     }
515                 };
516             } catch(e) {
517             }
518         }
519         // Setup Node type constants for those browsers that don't have them (IE)
520         var Node = {ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4,
521             ENTITY_REFERENCE_NODE: 5,  ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7,
522             COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10,
523             DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12};
524 
525         // PENDING - add support for removing handlers added via DOM 2 methods
526         /**
527          * Delete all events attached to a node
528          * @param node
529          * @ignore
530          */
531         var clearEvents = function clearEvents(node) {
532             if (!node) {
533                 return;
534             }
535 
536             // don't do anything for text and comment nodes - unnecessary
537             if (node.nodeType == Node.TEXT_NODE || node.nodeType == Node.COMMENT_NODE) {
538                 return;
539             }
540 
541             var events = ['abort', 'blur', 'change', 'error', 'focus', 'load', 'reset', 'resize', 'scroll', 'select', 'submit', 'unload',
542             'keydown', 'keypress', 'keyup', 'click', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'dblclick' ];
543             try {
544                 for (var e in events) {
545                     if (events.hasOwnProperty(e)) {
546                         node[e] = null;
547                     }
548                 }
549             } catch (ex) {
550                 // it's OK if it fails, at least we tried
551             }
552         };
553 
554         /**
555          * Determine if this current browser is IE9 or greater
556          * @param node
557          * @ignore
558          */
559         var isIE9Plus = function isIE9Plus() {
560             return typeof XDomainRequest !== "undefined" && typeof window.msPerformance !== "undefined";
561         }
562 
563 
564         /**
565          * Deletes node
566          * @param node
567          * @ignore
568          */
569         var deleteNode = function deleteNode(node) {
570             if (!node) {
571                 return;
572             }
573             if (!node.parentNode) {
574                 // if there's no parent, there's nothing to do
575                 return;
576             }
577             if (!isIE() || (isIE() && isIE9Plus())) {
578                 // nothing special required
579                 node.parentNode.removeChild(node);
580                 return;
581             }
582             // The rest of this code is specialcasing for IE
583             if (node.nodeName.toLowerCase() === "body") {
584                 // special case for removing body under IE.
585                 deleteChildren(node);
586                 try {
587                     node.outerHTML = '';
588                 } catch (ex) {
589                     // fails under some circumstances, but not in RI
590                     // supplied responses.  If we've gotten here, it's
591                     // fairly safe to leave a lingering body tag rather than
592                     // fail outright
593                 }
594                 return;
595             }
596             var temp = node.ownerDocument.createElement('div');
597             var parent = node.parentNode;
598             temp.appendChild(parent.removeChild(node));
599             // Now clean up the temporary element
600             try {
601                 temp.outerHTML = ''; //prevent leak in IE
602             } catch (ex) {
603                 // at least we tried.  Fails in some circumstances,
604                 // but not in RI supplied responses.  Better to leave a lingering
605                 // temporary div than to fail outright.
606             }
607         };
608 
609         /**
610          * Deletes all children of a node
611          * @param node
612          * @ignore
613          */
614         var deleteChildren = function deleteChildren(node) {
615             if (!node) {
616                 return;
617             }
618             for (var x = node.childNodes.length - 1; x >= 0; x--) { //delete all of node's children
619                 var childNode = node.childNodes[x];
620                 deleteNode(childNode);
621             }
622         };
623 
624         /**
625          * <p> Copies the childNodes of nodeFrom to nodeTo</p>
626          *
627          * @param  nodeFrom the Node to copy the childNodes from
628          * @param  nodeTo the Node to copy the childNodes to
629          * @ignore
630          * Note:  This code originally from Sarissa:  http://dev.abiss.gr/sarissa
631          * It has been modified to fit into the overall codebase
632          */
633         var copyChildNodes = function copyChildNodes(nodeFrom, nodeTo) {
634 
635             if ((!nodeFrom) || (!nodeTo)) {
636                 throw "Both source and destination nodes must be provided";
637             }
638 
639             deleteChildren(nodeTo);
640             var nodes = nodeFrom.childNodes;
641             // if within the same doc, just move, else copy and delete
642             if (nodeFrom.ownerDocument == nodeTo.ownerDocument) {
643                 while (nodeFrom.firstChild) {
644                     nodeTo.appendChild(nodeFrom.firstChild);
645                 }
646             } else {
647                 var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument;
648                 var i;
649                 if (typeof(ownerDoc.importNode) != "undefined") {
650                     for (i = 0; i < nodes.length; i++) {
651                         nodeTo.appendChild(ownerDoc.importNode(nodes[i], true));
652                     }
653                 } else {
654                     for (i = 0; i < nodes.length; i++) {
655                         nodeTo.appendChild(nodes[i].cloneNode(true));
656                     }
657                 }
658             }
659         };
660 
661 
662         /**
663          * Replace one node with another.  Necessary for handling IE memory leak.
664          * @param node
665          * @param newNode
666          * @ignore
667          */
668         var replaceNode = function replaceNode(newNode, node) {
669                if(isIE()){
670                     node.parentNode.insertBefore(newNode, node);
671                     deleteNode(node);
672                } else {
673                     node.parentNode.replaceChild(newNode, node);
674                }
675         };
676 
677         var propertyToAttribute = function propertyToAttribute(name) {
678             if (name === 'className') {
679                 return 'class';
680             } else if (name === 'xmllang') {
681                 return 'xml:lang';
682             } else {
683                 return name.toLowerCase();
684             }
685         };
686 
687         var isFunctionNative = function isFunctionNative(func) {
688             return /^\s*function[^{]+{\s*\[native code\]\s*}\s*$/.test(String(func));
689         };
690 
691         var detectAttributes = function detectAttributes(element) {
692             //test if 'hasAttribute' method is present and its native code is intact
693             //for example, Prototype can add its own implementation if missing
694             if (element.hasAttribute && isFunctionNative(element.hasAttribute)) {
695                 return function(name) {
696                     return element.hasAttribute(name);
697                 }
698             } else {
699                 try {
700                     //when accessing .getAttribute method without arguments does not throw an error then the method is not available
701                     element.getAttribute;
702 
703                     var html = element.outerHTML;
704                     var startTag = html.match(/^<[^>]*>/)[0];
705                     return function(name) {
706                         return startTag.indexOf(name + '=') > -1;
707                     }
708                 } catch (ex) {
709                     return function(name) {
710                         return element.getAttribute(name);
711                     }
712                 }
713             }
714         };
715 
716         /**
717          * copy all attributes from one element to another - except id
718          * @param target element to copy attributes to
719          * @param source element to copy attributes from
720          * @ignore
721          */
722         var cloneAttributes = function cloneAttributes(target, source) {
723 
724             // enumerate core element attributes - without 'dir' as special case
725             var coreElementProperties = ['className', 'title', 'lang', 'xmllang'];
726             // enumerate additional input element attributes
727             var inputElementProperties = [
728                 'name', 'value', 'size', 'maxLength', 'src', 'alt', 'useMap', 'tabIndex', 'accessKey', 'accept', 'type'
729             ];
730             // enumerate additional boolean input attributes
731             var inputElementBooleanProperties = [
732                 'checked', 'disabled', 'readOnly'
733             ];
734 
735             // Enumerate all the names of the event listeners
736             var listenerNames =
737                 [ 'onclick', 'ondblclick', 'onmousedown', 'onmousemove', 'onmouseout',
738                     'onmouseover', 'onmouseup', 'onkeydown', 'onkeypress', 'onkeyup',
739                     'onhelp', 'onblur', 'onfocus', 'onchange', 'onload', 'onunload', 'onabort',
740                     'onreset', 'onselect', 'onsubmit'
741                 ];
742 
743             var sourceAttributeDetector = detectAttributes(source);
744             var targetAttributeDetector = detectAttributes(target);
745 
746             var isInputElement = target.nodeName.toLowerCase() === 'input';
747             var propertyNames = isInputElement ? coreElementProperties.concat(inputElementProperties) : coreElementProperties;
748             var isXML = !source.ownerDocument.contentType || source.ownerDocument.contentType == 'text/xml';
749             for (var iIndex = 0, iLength = propertyNames.length; iIndex < iLength; iIndex++) {
750                 var propertyName = propertyNames[iIndex];
751                 var attributeName = propertyToAttribute(propertyName);
752                 if (sourceAttributeDetector(attributeName)) {
753                 
754                     //With IE 7 (quirks or standard mode) and IE 8/9 (quirks mode only), 
755                     //you cannot get the attribute using 'class'. You must use 'className'
756                     //which is the same value you use to get the indexed property. The only 
757                     //reliable way to detect this (without trying to evaluate the browser
758                     //mode and version) is to compare the two return values using 'className' 
759                     //to see if they exactly the same.  If they are, then use the property
760                     //name when using getAttribute.
761                     if( attributeName == 'class'){
762                         if( isIE() && (source.getAttribute(propertyName) === source[propertyName]) ){
763                             attributeName = propertyName;
764                         }
765                     }
766 
767                     var newValue = isXML ? source.getAttribute(attributeName) : source[propertyName];
768                     var oldValue = target[propertyName];
769                     if (oldValue != newValue) {
770                         target[propertyName] = newValue;
771                     }
772                 } else if (targetAttributeDetector(attributeName)) {
773                     //setting property to '' seems to be the only cross-browser method for removing an attribute
774                     target[propertyName] = '';
775                 }
776             }
777 
778             var booleanPropertyNames = isInputElement ? inputElementBooleanProperties : [];
779             for (var jIndex = 0, jLength = booleanPropertyNames.length; jIndex < jLength; jIndex++) {
780                 var booleanPropertyName = booleanPropertyNames[jIndex];
781                 var newBooleanValue = source[booleanPropertyName];
782                 var oldBooleanValue = target[booleanPropertyName];
783                 if (oldBooleanValue != newBooleanValue) {
784                     target[booleanPropertyName] = newBooleanValue;
785                 }
786             }
787 
788             //'style' attribute special case
789             if (sourceAttributeDetector('style')) {
790                 var newStyle;
791                 var oldStyle;
792                 if (isIE()) {
793                     newStyle = source.style.cssText;
794                     oldStyle = target.style.cssText;
795                     if (newStyle != oldStyle) {
796                         target.style.cssText = newStyle;
797                     }
798                 } else {
799                     newStyle = source.getAttribute('style');
800                     oldStyle = target.getAttribute('style');
801                     if (newStyle != oldStyle) {
802                         target.setAttribute('style', newStyle);
803                     }
804                 }
805             } else if (targetAttributeDetector('style')){
806                 target.removeAttribute('style');
807             }
808 
809             // Special case for 'dir' attribute
810             if (!isIE() && source.dir != target.dir) {
811                 if (sourceAttributeDetector('dir')) {
812                     target.dir = source.dir;
813                 } else if (targetAttributeDetector('dir')) {
814                     target.dir = '';
815                 }
816             }
817 
818             for (var lIndex = 0, lLength = listenerNames.length; lIndex < lLength; lIndex++) {
819                 var name = listenerNames[lIndex];
820                 target[name] = source[name] ? source[name] : null;
821                 if (source[name]) {
822                     source[name] = null;
823                 }
824             }
825 
826             //clone HTML5 data-* attributes
827             try{
828                 var targetDataset = target.dataset;
829                 var sourceDataset = source.dataset;
830                 if (targetDataset || sourceDataset) {
831                     //cleanup the dataset
832                     for (var tp in targetDataset) {
833                         delete targetDataset[tp];
834                     }
835                     //copy dataset's properties
836                     for (var sp in sourceDataset) {
837                         targetDataset[sp] = sourceDataset[sp];
838                     }
839                 }
840             } catch (ex) {
841                 //most probably dataset properties are not supported
842             }
843         };
844 
845         /**
846          * Replace an element from one document into another
847          * @param newElement new element to put in document
848          * @param origElement original element to replace
849          * @ignore
850          */
851         var elementReplace = function elementReplace(newElement, origElement) {
852             copyChildNodes(newElement, origElement);
853             // sadly, we have to reparse all over again
854             // to reregister the event handlers and styles
855             // PENDING do some performance tests on large pages
856             origElement.innerHTML = origElement.innerHTML;
857 
858             try {
859                 cloneAttributes(origElement, newElement);
860             } catch (ex) {
861                 // if in dev mode, report an error, else try to limp onward
862                 if (jsf.getProjectStage() == "Development") {
863                     throw new Error("Error updating attributes");
864                 }
865             }
866             deleteNode(newElement);
867 
868         };
869 
870         /**
871          * Create a new document, then select the body element within it
872          * @param docStr Stringified version of document to create
873          * @return element the body element
874          * @ignore
875          */
876         var getBodyElement = function getBodyElement(docStr) {
877 
878             var doc;  // intermediate document we'll create
879             var body; // Body element to return
880 
881             if (typeof DOMParser !== "undefined") {  // FF, S, Chrome
882                 doc = (new DOMParser()).parseFromString(docStr, "text/xml");
883             } else if (typeof ActiveXObject !== "undefined") { // IE
884                 doc = new ActiveXObject("MSXML2.DOMDocument");
885                 doc.loadXML(docStr);
886             } else {
887                 throw new Error("You don't seem to be running a supported browser");
888             }
889 
890             if (getParseErrorText(doc) !== PARSED_OK) {
891                 throw new Error(getParseErrorText(doc));
892             }
893 
894             body = doc.getElementsByTagName("body")[0];
895 
896             if (!body) {
897                 throw new Error("Can't find body tag in returned document.");
898             }
899 
900             return body;
901         };
902 
903         /**
904          * Find view state field for a given form.
905          * @param form
906          * @ignore
907          */
908         var getViewStateElement = function getViewStateElement(form) {
909             var viewStateElement = form['javax.faces.ViewState'];
910 
911             if (viewStateElement) {
912                 return viewStateElement;
913             } else {
914                 var formElements = form.elements;
915                 for (var i = 0, length = formElements.length; i < length; i++) {
916                     var formElement = formElements[i];
917                     if (formElement.name == 'javax.faces.ViewState') {
918                         return formElement;
919                     }
920                 }
921             }
922 
923             return undefined;
924         };
925 
926         /**
927          * Do update.
928          * @param element element to update
929          * @param context context of request
930          * @ignore
931          */
932         var doUpdate = function doUpdate(element, context, partialResponseId) {
933             var id, content, markup, state, windowId;
934             var stateForm, windowIdForm;
935             var scripts = []; // temp holding value for array of script nodes
936 
937             id = element.getAttribute('id');
938             var viewStateRegex = new RegExp("javax.faces.ViewState" +
939                                             jsf.separatorchar + ".*$");
940             var windowIdRegex = new RegExp("^.*" + jsf.separatorchar + 
941                                            "javax.faces.ClientWindow" +
942                                             jsf.separatorchar + ".*$");
943             if (id.match(viewStateRegex)) {
944 
945                 state = element.firstChild;
946 
947                 // Now set the view state from the server into the DOM
948                 // but only for the form that submitted the request.
949 
950                 stateForm = document.getElementById(context.formid);
951                 if (!stateForm || !stateForm.elements) {
952                     // if the form went away for some reason, or it lacks elements 
953                     // we're going to just return silently.
954                     return;
955                 }
956                 var field = getViewStateElement(stateForm);
957                 if (typeof field == 'undefined') {
958                     field = document.createElement("input");
959                     field.type = "hidden";
960                     field.name = "javax.faces.ViewState";
961                     stateForm.appendChild(field);
962                 }
963                 field.value = state.nodeValue;
964 
965                 // Now set the view state from the server into the DOM
966                 // for any form that is a render target.
967 
968                 if (typeof context.render !== 'undefined' && context.render !== null) {
969                     var temp = context.render.split(' ');
970                     for (var i = 0; i < temp.length; i++) {
971                         if (temp.hasOwnProperty(i)) {
972                             // See if the element is a form and
973                             // the form is not the one that caused the submission..
974                             var f = document.forms[temp[i]];
975                             if (typeof f !== 'undefined' && f !== null && f.id !== context.formid) {
976                                 field = getViewStateElement(f);
977                                 if (typeof field === 'undefined') {
978                                     field = document.createElement("input");
979                                     field.type = "hidden";
980                                     field.name = "javax.faces.ViewState";
981                                     f.appendChild(field);
982                                 }
983                                 field.value = state.nodeValue;
984                             }
985                         }
986                     }
987                 }
988                 return;
989             } else if (id.match(windowIdRegex)) {
990 
991                 windowId = element.firstChild;
992 
993                 // Now set the windowId from the server into the DOM
994                 // but only for the form that submitted the request.
995 
996                 windowIdForm = document.getElementById(context.formid);
997                 if (!windowIdForm || !windowIdForm.elements) {
998                     // if the form went away for some reason, or it lacks elements 
999                     // we're going to just return silently.
1000                     return;
1001                 }
1002                 var field = windowIdForm.elements["javax.faces.ClientWindow"];
1003                 if (typeof field == 'undefined') {
1004                     field = document.createElement("input");
1005                     field.type = "hidden";
1006                     field.name = "javax.faces.ClientWindow";
1007                     windowIdForm.appendChild(field);
1008                 }
1009                 field.value = windowId.nodeValue;
1010 
1011                 // Now set the windowId from the server into the DOM
1012                 // for any form that is a render target.
1013 
1014                 if (typeof context.render !== 'undefined' && context.render !== null) {
1015                     var temp = context.render.split(' ');
1016                     for (var i = 0; i < temp.length; i++) {
1017                         if (temp.hasOwnProperty(i)) {
1018                             // See if the element is a form and
1019                             // the form is not the one that caused the submission..
1020                             var f = document.forms[temp[i]];
1021                             if (typeof f !== 'undefined' && f !== null && f.id !== context.formid) {
1022                                 field = f.elements["javax.faces.ClientWindow"];
1023                                 if (typeof field === 'undefined') {
1024                                     field = document.createElement("input");
1025                                     field.type = "hidden";
1026                                     field.name = "javax.faces.ClientWindow";
1027                                     f.appendChild(field);
1028                                 }
1029                                 field.value = windowId.nodeValue;
1030                             }
1031                         }
1032                     }
1033                 }
1034                 return;
1035             }
1036 
1037             // join the CDATA sections in the markup
1038             markup = '';
1039             for (var j = 0; j < element.childNodes.length; j++) {
1040                 content = element.childNodes[j];
1041                 markup += content.nodeValue;
1042             }
1043 
1044             var src = markup;
1045 
1046             // If our special render all markup is present..
1047             if (id === "javax.faces.ViewRoot" || id === "javax.faces.ViewBody") {
1048                 var bodyStartEx = new RegExp("< *body[^>]*>", "gi");
1049                 var bodyEndEx = new RegExp("< */ *body[^>]*>", "gi");
1050                 var newsrc;
1051 
1052                 var docBody = document.getElementsByTagName("body")[0];
1053                 var bodyStart = bodyStartEx.exec(src);
1054 
1055                 if (bodyStart !== null) { // replace body tag
1056                     // First, try with XML manipulation
1057                     try {
1058                         // Get scripts from text
1059                         scripts = stripScripts(src);
1060                         // Remove scripts from text
1061                         newsrc = src.replace(/<script[^>]*type="text\/javascript"*>([\S\s]*?)<\/script>/igm, "");
1062                         elementReplace(getBodyElement(newsrc), docBody);
1063                         runScripts(scripts);
1064                     } catch (e) {
1065                         // OK, replacing the body didn't work with XML - fall back to quirks mode insert
1066                         var srcBody, bodyEnd;
1067                         // if src contains </body>
1068                         bodyEnd = bodyEndEx.exec(src);
1069                         if (bodyEnd !== null) {
1070                             srcBody = src.substring(bodyStartEx.lastIndex,
1071                                     bodyEnd.index);
1072                         } else { // can't find the </body> tag, punt
1073                             srcBody = src.substring(bodyStartEx.lastIndex);
1074                         }
1075                         // replace body contents with innerHTML - note, script handling happens within function
1076                         elementReplaceStr(docBody, "body", srcBody);
1077 
1078                     }
1079 
1080                 } else {  // replace body contents with innerHTML - note, script handling happens within function
1081                     elementReplaceStr(docBody, "body", src);
1082                 }
1083             } else if (id === "javax.faces.ViewHead") {
1084                 throw new Error("javax.faces.ViewHead not supported - browsers cannot reliably replace the head's contents");
1085             } else {
1086                 var d = $(id);
1087                 if (!d) {
1088                     throw new Error("During update: " + id + " not found");
1089                 }
1090                 var parent = d.parentNode;
1091                 // Trim space padding before assigning to innerHTML
1092                 var html = src.replace(/^\s+/g, '').replace(/\s+$/g, '');
1093                 var parserElement = document.createElement('div');
1094                 var tag = d.nodeName.toLowerCase();
1095                 var tableElements = ['td', 'th', 'tr', 'tbody', 'thead', 'tfoot'];
1096                 var isInTable = false;
1097                 for (var tei = 0, tel = tableElements.length; tei < tel; tei++) {
1098                     if (tableElements[tei] == tag) {
1099                         isInTable = true;
1100                         break;
1101                     }
1102                 }
1103                 if (isInTable) {
1104 
1105                     if (isAutoExec()) {
1106                         // Create html
1107                         parserElement.innerHTML = '<table>' + html + '</table>';
1108                     } else {
1109                         // Get the scripts from the text
1110                         scripts = stripScripts(html);
1111                         // Remove scripts from text
1112                         html = html.replace(/<script[^>]*type="text\/javascript"*>([\S\s]*?)<\/script>/igm,"");
1113                         parserElement.innerHTML = '<table>' + html + '</table>';
1114                     }
1115                     var newElement = parserElement.firstChild;
1116                     //some browsers will also create intermediary elements such as table>tbody>tr>td
1117                     while ((null !== newElement) && (id !== newElement.id)) {
1118                         newElement = newElement.firstChild;
1119                     }
1120                     parent.replaceChild(newElement, d);
1121                     runScripts(scripts);
1122                 } else if (d.nodeName.toLowerCase() === 'input') {
1123                     // special case handling for 'input' elements
1124                     // in order to not lose focus when updating,
1125                     // input elements need to be added in place.
1126                     parserElement = document.createElement('div');
1127                     parserElement.innerHTML = html;
1128                     newElement = parserElement.firstChild;
1129 
1130                     cloneAttributes(d, newElement);
1131                     deleteNode(parserElement);
1132                 } else if (html.length > 0) {
1133                     if (isAutoExec()) {
1134                         // Create html
1135                         parserElement.innerHTML = html;
1136                     } else {
1137                         // Get the scripts from the text
1138                         scripts = stripScripts(html);
1139                         // Remove scripts from text
1140                         html = html.replace(/<script[^>]*type="text\/javascript"*>([\S\s]*?)<\/script>/igm,"");
1141                         parserElement.innerHTML = html;
1142                     }
1143                     replaceNode(parserElement.firstChild, d);
1144                     deleteNode(parserElement);
1145                     runScripts(scripts);
1146                 }
1147             }
1148         };
1149 
1150         /**
1151          * Delete a node specified by the element.
1152          * @param element
1153          * @ignore
1154          */
1155         var doDelete = function doDelete(element) {
1156             var id = element.getAttribute('id');
1157             var target = $(id);
1158             deleteNode(target);
1159         };
1160 
1161         /**
1162          * Insert a node specified by the element.
1163          * @param element
1164          * @ignore
1165          */
1166         var doInsert = function doInsert(element) {
1167             var tablePattern = new RegExp("<\\s*(td|th|tr|tbody|thead|tfoot)", "i");
1168             var scripts = [];
1169             var target = $(element.firstChild.getAttribute('id'));
1170             var parent = target.parentNode;
1171             var html = element.firstChild.firstChild.nodeValue;
1172             var isInTable = tablePattern.test(html);
1173 
1174             if (!isAutoExec())  {
1175                 // Get the scripts from the text
1176                 scripts = stripScripts(html);
1177                 // Remove scripts from text
1178                 html = html.replace(/<script[^>]*type="text\/javascript"*>([\S\s]*?)<\/script>/igm,"");
1179             }
1180             var tempElement = document.createElement('div');
1181             var newElement = null;
1182             if (isInTable)  {
1183                 tempElement.innerHTML = '<table>' + html + '</table>';
1184                 newElement = tempElement.firstChild;
1185                 //some browsers will also create intermediary elements such as table>tbody>tr>td
1186                 //test for presence of id on the new element since we do not have it directly
1187                 while ((null !== newElement) && ("" == newElement.id)) {
1188                     newElement = newElement.firstChild;
1189                 }
1190             } else {
1191                 tempElement.innerHTML = html;
1192                 newElement = tempElement.firstChild;
1193             }
1194 
1195             if (element.firstChild.nodeName === 'after') {
1196                 // Get the next in the list, to insert before
1197                 target = target.nextSibling;
1198             }  // otherwise, this is a 'before' element
1199             if (!!tempElement.innerHTML) { // check if only scripts were inserted - if so, do nothing here
1200                 parent.insertBefore(newElement, target);
1201             }
1202             runScripts(scripts);
1203             deleteNode(tempElement);
1204         };
1205 
1206         /**
1207          * Modify attributes of given element id.
1208          * @param element
1209          * @ignore
1210          */
1211         var doAttributes = function doAttributes(element) {
1212 
1213             // Get id of element we'll act against
1214             var id = element.getAttribute('id');
1215 
1216             var target = $(id);
1217 
1218             if (!target) {
1219                 throw new Error("The specified id: " + id + " was not found in the page.");
1220             }
1221 
1222             // There can be multiple attributes modified.  Loop through the list.
1223             var nodes = element.childNodes;
1224             for (var i = 0; i < nodes.length; i++) {
1225                 var name = nodes[i].getAttribute('name');
1226                 var value = nodes[i].getAttribute('value');
1227                 if (!isIE()) {
1228                     if (name === 'value') {
1229                         target.value = value;
1230                     } else if (name === 'disabled') {
1231                         target.disabled = value;
1232                     } else {
1233                         target.setAttribute(name, value);
1234                     }
1235                 } else { // if it's IE, then quite a bit more work is required
1236                     if (name === 'class') {
1237                         name = 'className';
1238                         target.setAttribute(name, value, 0);
1239                     } else if (name === "for") {
1240                         name = 'htmlFor';
1241                         target.setAttribute(name, value, 0);
1242                     } else if (name === 'style') {
1243                         target.style.setAttribute('cssText', value, 0);
1244                     } else if (name.substring(0, 2) === 'on') {
1245                         var fn = function(value) {
1246                             return function() {
1247                                 window.execScript(value);
1248                             };
1249                         }(value);
1250                         target.setAttribute(name, fn, 0);
1251                     } else if (name === 'dir') {
1252                         if (jsf.getProjectStage() == 'Development') {
1253                             throw new Error("Cannot set 'dir' attribute in IE");
1254                         }
1255                     } else {
1256                         target.setAttribute(name, value, 0);
1257                     }
1258                 }
1259             }
1260         };
1261 
1262         /**
1263          * Eval the CDATA of the element.
1264          * @param element to eval
1265          * @ignore
1266          */
1267         var doEval = function doEval(element) {
1268             var evalText = element.firstChild.nodeValue;
1269             globalEval(evalText);
1270         };
1271 
1272         /**
1273          * Ajax Request Queue
1274          * @ignore
1275          */
1276         var Queue = new function Queue() {
1277 
1278             // Create the internal queue
1279             var queue = [];
1280 
1281 
1282             // the amount of space at the front of the queue, initialised to zero
1283             var queueSpace = 0;
1284 
1285             /** Returns the size of this Queue. The size of a Queue is equal to the number
1286              * of elements that have been enqueued minus the number of elements that have
1287              * been dequeued.
1288              * @ignore
1289              */
1290             this.getSize = function getSize() {
1291                 return queue.length - queueSpace;
1292             };
1293 
1294             /** Returns true if this Queue is empty, and false otherwise. A Queue is empty
1295              * if the number of elements that have been enqueued equals the number of
1296              * elements that have been dequeued.
1297              * @ignore
1298              */
1299             this.isEmpty = function isEmpty() {
1300                 return (queue.length === 0);
1301             };
1302 
1303             /** Enqueues the specified element in this Queue.
1304              *
1305              * @param element - the element to enqueue
1306              * @ignore
1307              */
1308             this.enqueue = function enqueue(element) {
1309                 // Queue the request
1310                 queue.push(element);
1311             };
1312 
1313 
1314             /** Dequeues an element from this Queue. The oldest element in this Queue is
1315              * removed and returned. If this Queue is empty then undefined is returned.
1316              *
1317              * @returns Object The element that was removed from the queue.
1318              * @ignore
1319              */
1320             this.dequeue = function dequeue() {
1321                 // initialise the element to return to be undefined
1322                 var element = undefined;
1323 
1324                 // check whether the queue is empty
1325                 if (queue.length) {
1326                     // fetch the oldest element in the queue
1327                     element = queue[queueSpace];
1328 
1329                     // update the amount of space and check whether a shift should occur
1330                     if (++queueSpace * 2 >= queue.length) {
1331                         // set the queue equal to the non-empty portion of the queue
1332                         queue = queue.slice(queueSpace);
1333                         // reset the amount of space at the front of the queue
1334                         queueSpace = 0;
1335                     }
1336                 }
1337                 // return the removed element
1338                 try {
1339                     return element;
1340                 } finally {
1341                     element = null; // IE 6 leak prevention
1342                 }
1343             };
1344 
1345             /** Returns the oldest element in this Queue. If this Queue is empty then
1346              * undefined is returned. This function returns the same value as the dequeue
1347              * function, but does not remove the returned element from this Queue.
1348              * @ignore
1349              */
1350             this.getOldestElement = function getOldestElement() {
1351                 // initialise the element to return to be undefined
1352                 var element = undefined;
1353 
1354                 // if the queue is not element then fetch the oldest element in the queue
1355                 if (queue.length) {
1356                     element = queue[queueSpace];
1357                 }
1358                 // return the oldest element
1359                 try {
1360                     return element;
1361                 } finally {
1362                     element = null; //IE 6 leak prevention
1363                 }
1364             };
1365         }();
1366 
1367 
1368         /**
1369          * AjaxEngine handles Ajax implementation details.
1370          * @ignore
1371          */
1372         var AjaxEngine = function AjaxEngine() {
1373 
1374             var req = {};                  // Request Object
1375             req.url = null;                // Request URL
1376             req.context = {};              // Context of request and response
1377             req.context.sourceid = null;   // Source of this request
1378             req.context.onerror = null;    // Error handler for request
1379             req.context.onevent = null;    // Event handler for request
1380             req.context.formid = null;     // Form that's the context for this request
1381             req.xmlReq = null;             // XMLHttpRequest Object
1382             req.async = true;              // Default - Asynchronous
1383             req.parameters = {};           // Parameters For GET or POST
1384             req.queryString = null;        // Encoded Data For GET or POST
1385             req.method = null;             // GET or POST
1386             req.status = null;             // Response Status Code From Server
1387             req.fromQueue = false;         // Indicates if the request was taken off the queue
1388             // before being sent.  This prevents the request from
1389             // entering the queue redundantly.
1390 
1391             req.que = Queue;
1392 
1393             // Get an XMLHttpRequest Handle
1394             req.xmlReq = getTransport();
1395             if (req.xmlReq === null) {
1396                 return null;
1397             }
1398 
1399             function noop() {}
1400             
1401             // Set up request/response state callbacks
1402             /**
1403              * @ignore
1404              */
1405             req.xmlReq.onreadystatechange = function() {
1406                 if (req.xmlReq.readyState === 4) {
1407                     req.onComplete();
1408                     // next two lines prevent closure/ciruclar reference leaks
1409                     // of XHR instances in IE
1410                     req.xmlReq.onreadystatechange = noop;
1411                     req.xmlReq = null;
1412                 }
1413             };
1414 
1415             /**
1416              * This function is called when the request/response interaction
1417              * is complete.  If the return status code is successfull,
1418              * dequeue all requests from the queue that have completed.  If a
1419              * request has been found on the queue that has not been sent,
1420              * send the request.
1421              * @ignore
1422              */
1423             req.onComplete = function onComplete() {
1424                 if (req.xmlReq.status && (req.xmlReq.status >= 200 && req.xmlReq.status < 300)) {
1425                     sendEvent(req.xmlReq, req.context, "complete");
1426                     jsf.ajax.response(req.xmlReq, req.context);
1427                 } else {
1428                     sendEvent(req.xmlReq, req.context, "complete");
1429                     sendError(req.xmlReq, req.context, "httpError");
1430                 }
1431 
1432                 // Regardless of whether the request completed successfully (or not),
1433                 // dequeue requests that have been completed (readyState 4) and send
1434                 // requests that ready to be sent (readyState 0).
1435 
1436                 var nextReq = req.que.getOldestElement();
1437                 if (nextReq === null || typeof nextReq === 'undefined') {
1438                     return;
1439                 }
1440                 while ((typeof nextReq.xmlReq !== 'undefined' && nextReq.xmlReq !== null) &&
1441                        nextReq.xmlReq.readyState === 4) {
1442                     req.que.dequeue();
1443                     nextReq = req.que.getOldestElement();
1444                     if (nextReq === null || typeof nextReq === 'undefined') {
1445                         break;
1446                     }
1447                 }
1448                 if (nextReq === null || typeof nextReq === 'undefined') {
1449                     return;
1450                 }
1451                 if ((typeof nextReq.xmlReq !== 'undefined' && nextReq.xmlReq !== null) &&
1452                     nextReq.xmlReq.readyState === 0) {
1453                     nextReq.fromQueue = true;
1454                     nextReq.sendRequest();
1455                 }
1456             };
1457 
1458             /**
1459              * Utility method that accepts additional arguments for the AjaxEngine.
1460              * If an argument is passed in that matches an AjaxEngine property, the
1461              * argument value becomes the value of the AjaxEngine property.
1462              * Arguments that don't match AjaxEngine properties are added as
1463              * request parameters.
1464              * @ignore
1465              */
1466             req.setupArguments = function(args) {
1467                 for (var i in args) {
1468                     if (args.hasOwnProperty(i)) {
1469                         if (typeof req[i] === 'undefined') {
1470                             req.parameters[i] = args[i];
1471                         } else {
1472                             req[i] = args[i];
1473                         }
1474                     }
1475                 }
1476             };
1477 
1478             /**
1479              * This function does final encoding of parameters, determines the request method
1480              * (GET or POST) and sends the request using the specified url.
1481              * @ignore
1482              */
1483             req.sendRequest = function() {
1484                 if (req.xmlReq !== null) {
1485                     // if there is already a request on the queue waiting to be processed..
1486                     // just queue this request
1487                     if (!req.que.isEmpty()) {
1488                         if (!req.fromQueue) {
1489                             req.que.enqueue(req);
1490                             return;
1491                         }
1492                     }
1493                     // If the queue is empty, queue up this request and send
1494                     if (!req.fromQueue) {
1495                         req.que.enqueue(req);
1496                     }
1497                     // Some logic to get the real request URL
1498                     if (req.generateUniqueUrl && req.method == "GET") {
1499                         req.parameters["AjaxRequestUniqueId"] = new Date().getTime() + "" + req.requestIndex;
1500                     }
1501                     var content = null; // For POST requests, to hold query string
1502                     for (var i in req.parameters) {
1503                         if (req.parameters.hasOwnProperty(i)) {
1504                             if (req.queryString.length > 0) {
1505                                 req.queryString += "&";
1506                             }
1507                             req.queryString += encodeURIComponent(i) + "=" + encodeURIComponent(req.parameters[i]);
1508                         }
1509                     }
1510                     if (req.method === "GET") {
1511                         if (req.queryString.length > 0) {
1512                             req.url += ((req.url.indexOf("?") > -1) ? "&" : "?") + req.queryString;
1513                         }
1514                     }
1515                     req.xmlReq.open(req.method, req.url, req.async);
1516                     // note that we are including the charset=UTF-8 as part of the content type (even
1517                     // if encodeURIComponent encodes as UTF-8), because with some
1518                     // browsers it will not be set in the request.  Some server implementations need to 
1519                     // determine the character encoding from the request header content type.
1520                     if (req.method === "POST") {
1521                         if (typeof req.xmlReq.setRequestHeader !== 'undefined') {
1522                             req.xmlReq.setRequestHeader('Faces-Request', 'partial/ajax');
1523                             req.xmlReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
1524                         }
1525                         content = req.queryString;
1526                     }
1527                     // note that async == false is not a supported feature.  We may change it in ways
1528                     // that break existing programs at any time, with no warning.
1529                     if(!req.async) {
1530                         req.xmlReq.onreadystatechange = null; // no need for readystate change listening
1531                     }
1532                     sendEvent(req.xmlReq, req.context, "begin");
1533                     req.xmlReq.send(content);
1534                     if(!req.async){
1535                         req.onComplete();
1536                 }
1537                 }
1538             };
1539 
1540             return req;
1541         };
1542 
1543         /**
1544          * Error handling callback.
1545          * Assumes that the request has completed.
1546          * @ignore
1547          */
1548         var sendError = function sendError(request, context, status, description, serverErrorName, serverErrorMessage) {
1549 
1550             // Possible errornames:
1551             // httpError
1552             // emptyResponse
1553             // serverError
1554             // malformedXML
1555 
1556             var sent = false;
1557             var data = {};  // data payload for function
1558             data.type = "error";
1559             data.status = status;
1560             data.source = context.sourceid;
1561             data.responseCode = request.status;
1562             data.responseXML = request.responseXML;
1563             data.responseText = request.responseText;
1564 
1565             // ensure data source is the dom element and not the ID
1566             // per 14.4.1 of the 2.0 specification.
1567             if (typeof data.source === 'string') {
1568                 data.source = document.getElementById(data.source);
1569             }
1570 
1571             if (description) {
1572                 data.description = description;
1573             } else if (status == "httpError") {
1574                 if (data.responseCode === 0) {
1575                     data.description = "The Http Transport returned a 0 status code.  This is usually the result of mixing ajax and full requests.  This is usually undesired, for both performance and data integrity reasons.";
1576                 } else {
1577                     data.description = "There was an error communicating with the server, status: " + data.responseCode;
1578                 }
1579             } else if (status == "serverError") {
1580                 data.description = serverErrorMessage;
1581             } else if (status == "emptyResponse") {
1582                 data.description = "An empty response was received from the server.  Check server error logs.";
1583             } else if (status == "malformedXML") {
1584                 if (getParseErrorText(data.responseXML) !== PARSED_OK) {
1585                     data.description = getParseErrorText(data.responseXML);
1586                 } else {
1587                     data.description = "An invalid XML response was received from the server.";
1588                 }
1589             }
1590 
1591             if (status == "serverError") {
1592                 data.errorName = serverErrorName;
1593                 data.errorMessage = serverErrorMessage;
1594             }
1595 
1596             // If we have a registered callback, send the error to it.
1597             if (context.onerror) {
1598                 context.onerror.call(null, data);
1599                 sent = true;
1600             }
1601 
1602             for (var i in errorListeners) {
1603                 if (errorListeners.hasOwnProperty(i)) {
1604                     errorListeners[i].call(null, data);
1605                     sent = true;
1606                 }
1607             }
1608 
1609             if (!sent && jsf.getProjectStage() === "Development") {
1610                 if (status == "serverError") {
1611                     alert("serverError: " + serverErrorName + " " + serverErrorMessage);
1612                 } else {
1613                     alert(status + ": " + data.description);
1614                 }
1615             }
1616         };
1617 
1618         /**
1619          * Event handling callback.
1620          * Request is assumed to have completed, except in the case of event = 'begin'.
1621          * @ignore
1622          */
1623         var sendEvent = function sendEvent(request, context, status) {
1624 
1625             var data = {};
1626             data.type = "event";
1627             data.status = status;
1628             data.source = context.sourceid;
1629             // ensure data source is the dom element and not the ID
1630             // per 14.4.1 of the 2.0 specification.
1631             if (typeof data.source === 'string') {
1632                 data.source = document.getElementById(data.source);
1633             }
1634             if (status !== 'begin') {
1635                 data.responseCode = request.status;
1636                 data.responseXML = request.responseXML;
1637                 data.responseText = request.responseText;
1638             }
1639 
1640             if (context.onevent) {
1641                 context.onevent.call(null, data);
1642             }
1643 
1644             for (var i in eventListeners) {
1645                 if (eventListeners.hasOwnProperty(i)) {
1646                     eventListeners[i].call(null, data);
1647                 }
1648             }
1649         };
1650 
1651         // Use module pattern to return the functions we actually expose
1652         return {
1653             /**
1654              * Register a callback for error handling.
1655              * <p><b>Usage:</b></p>
1656              * <pre><code>
1657              * jsf.ajax.addOnError(handleError);
1658              * ...
1659              * var handleError = function handleError(data) {
1660              * ...
1661              * }
1662              * </pre></code>
1663              * <p><b>Implementation Requirements:</b></p>
1664              * This function must accept a reference to an existing JavaScript function.
1665              * The JavaScript function reference must be added to a list of callbacks, making it possible
1666              * to register more than one callback by invoking <code>jsf.ajax.addOnError</code>
1667              * more than once.  This function must throw an error if the <code>callback</code>
1668              * argument is not a function.
1669              *
1670              * @member jsf.ajax
1671              * @param callback a reference to a function to call on an error
1672              */
1673             addOnError: function addOnError(callback) {
1674                 if (typeof callback === 'function') {
1675                     errorListeners[errorListeners.length] = callback;
1676                 } else {
1677                     throw new Error("jsf.ajax.addOnError:  Added a callback that was not a function.");
1678                 }
1679             },
1680             /**
1681              * Register a callback for event handling.
1682              * <p><b>Usage:</b></p>
1683              * <pre><code>
1684              * jsf.ajax.addOnEvent(statusUpdate);
1685              * ...
1686              * var statusUpdate = function statusUpdate(data) {
1687              * ...
1688              * }
1689              * </pre></code>
1690              * <p><b>Implementation Requirements:</b></p>
1691              * This function must accept a reference to an existing JavaScript function.
1692              * The JavaScript function reference must be added to a list of callbacks, making it possible
1693              * to register more than one callback by invoking <code>jsf.ajax.addOnEvent</code>
1694              * more than once.  This function must throw an error if the <code>callback</code>
1695              * argument is not a function.
1696              *
1697              * @member jsf.ajax
1698              * @param callback a reference to a function to call on an event
1699              */
1700             addOnEvent: function addOnEvent(callback) {
1701                 if (typeof callback === 'function') {
1702                     eventListeners[eventListeners.length] = callback;
1703                 } else {
1704                     throw new Error("jsf.ajax.addOnEvent: Added a callback that was not a function");
1705                 }
1706             },
1707             /**
1708 
1709              * <p><span class="changed_modified_2_2">Send</span> an
1710              * asynchronous Ajax req uest to the server.
1711 
1712              * <p><b>Usage:</b></p>
1713              * <pre><code>
1714              * Example showing all optional arguments:
1715              *
1716              * <commandButton id="button1" value="submit"
1717              *     onclick="jsf.ajax.request(this,event,
1718              *       {execute:'button1',render:'status',onevent: handleEvent,onerror: handleError});return false;"/>
1719              * </commandButton/>
1720              * </pre></code>
1721              * <p><b>Implementation Requirements:</b></p>
1722              * This function must:
1723              * <ul>
1724              * <li>Be used within the context of a <code>form</code>.</li>
1725              * <li>Capture the element that triggered this Ajax request
1726              * (from the <code>source</code> argument, also known as the
1727              * <code>source</code> element.</li>
1728              * <li>If the <code>source</code> element is <code>null</code> or
1729              * <code>undefined</code> throw an error.</li>
1730              * <li>If the <code>source</code> argument is not a <code>string</code> or
1731              * DOM element object, throw an error.</li>
1732              * <li>If the <code>source</code> argument is a <code>string</code>, find the
1733              * DOM element for that <code>string</code> identifier.
1734              * <li>If the DOM element could not be determined, throw an error.</li>
1735              * <li>If the <code>onerror</code> and <code>onevent</code> arguments are set,
1736              * they must be functions, or throw an error.
1737              * <li>Determine the <code>source</code> element's <code>form</code>
1738              * element.</li>
1739              * <li>Get the <code>form</code> view state by calling
1740              * {@link jsf.getViewState} passing the
1741              * <code>form</code> element as the argument.</li>
1742              * <li>Collect post data arguments for the Ajax request.
1743              * <ul>
1744              * <li>The following name/value pairs are required post data arguments:
1745              * <table border="1">
1746              * <tr>
1747              * <th>name</th>
1748              * <th>value</th>
1749              * </tr>
1750              * <tr>
1751              * <td><code>javax.faces.ViewState</code></td>
1752              * <td><code>Contents of javax.faces.ViewState hidden field.  This is included when
1753              * {@link jsf.getViewState} is used.</code></td>
1754              * </tr>
1755              * <tr>
1756              * <td><code>javax.faces.partial.ajax</code></td>
1757              * <td><code>true</code></td>
1758              * </tr>
1759              * <tr>
1760              * <td><code>javax.faces.source</code></td>
1761              * <td><code>The identifier of the element that triggered this request.</code></td>
1762              * </tr>
1763              * <tr class="changed_added_2_2">
1764              * <td><code>javax.faces.ClientWindow</code></td>
1765 
1766              * <td><code>Call jsf.getClientWindow(), passing the current
1767              * form.  If the return is non-null, it must be set as the
1768              * value of this name/value pair, otherwise, a name/value
1769              * pair for client window must not be sent.</code></td>
1770 
1771              * </tr>
1772              * </table>
1773              * </li>
1774              * </ul>
1775              * </li>
1776              * <li>Collect optional post data arguments for the Ajax request.
1777              * <ul>
1778              * <li>Determine additional arguments (if any) from the <code>options</code>
1779              * argument. If <code>options.execute</code> exists:
1780              * <ul>
1781              * <li>If the keyword <code>@none</code> is present, do not create and send
1782              * the post data argument <code>javax.faces.partial.execute</code>.</li>
1783              * <li>If the keyword <code>@all</code> is present, create the post data argument with
1784              * the name <code>javax.faces.partial.execute</code> and the value <code>@all</code>.</li>
1785              * <li>Otherwise, there are specific identifiers that need to be sent.  Create the post
1786              * data argument with the name <code>javax.faces.partial.execute</code> and the value as a
1787              * space delimited <code>string</code> of client identifiers.</li>
1788              * </ul>
1789              * </li>
1790              * <li>If <code>options.execute</code> does not exist, create the post data argument with the
1791              * name <code>javax.faces.partial.execute</code> and the value as the identifier of the
1792              * element that caused this request.</li>
1793              * <li>If <code>options.render</code> exists:
1794              * <ul>
1795              * <li>If the keyword <code>@none</code> is present, do not create and send
1796              * the post data argument <code>javax.faces.partial.render</code>.</li>
1797              * <li>If the keyword <code>@all</code> is present, create the post data argument with
1798              * the name <code>javax.faces.partial.render</code> and the value <code>@all</code>.</li>
1799              * <li>Otherwise, there are specific identifiers that need to be sent.  Create the post
1800              * data argument with the name <code>javax.faces.partial.render</code> and the value as a
1801              * space delimited <code>string</code> of client identifiers.</li>
1802              * </ul>
1803              * <li>If <code>options.render</code> does not exist do not create and send the
1804              * post data argument <code>javax.faces.partial.render</code>.</li>
1805 
1806              * <li class="changed_added_2_2">If
1807              * <code>options.delay</code> exists let it be the value
1808              * <em>delay</em>, for this discussion.  If
1809              * <code>options.delay</code> does not exist, or is the
1810              * literal string <code>'none'</code>, without the quotes,
1811              * no delay is used.  If less than <em>delay</em>
1812              * milliseconds elapses between calls to <em>request()</em>
1813              * only the most recent one is sent and all other requests
1814              * are discarded.</li>
1815 
1816 
1817              * <li class="changed_added_2_2">If
1818              * <code>options.resetInput</code> exists and its value is
1819              * <code>true</code> ensure a post data argument with the
1820              * name <code>javax.faces.partial.resetInput</code> and the
1821              * value <code>true</code> is sent in addition to the other
1822              * post data arguments.</li>
1823 
1824 
1825              * <li>Determine additional arguments (if any) from the <code>event</code>
1826              * argument.  The following name/value pairs may be used from the
1827              * <code>event</code> object:
1828              * <ul>
1829              * <li><code>target</code> - the ID of the element that triggered the event.</li>
1830              * <li><code>captured</code> - the ID of the element that captured the event.</li>
1831              * <li><code>type</code> - the type of event (ex: onkeypress)</li>
1832              * <li><code>alt</code> - <code>true</code> if ALT key was pressed.</li>
1833              * <li><code>ctrl</code> - <code>true</code> if CTRL key was pressed.</li>
1834              * <li><code>shift</code> - <code>true</code> if SHIFT key was pressed. </li>
1835              * <li><code>meta</code> - <code>true</code> if META key was pressed. </li>
1836              * <li><code>right</code> - <code>true</code> if right mouse button
1837              * was pressed. </li>
1838              * <li><code>left</code> - <code>true</code> if left mouse button
1839              * was pressed. </li>
1840              * <li><code>keycode</code> - the key code.
1841              * </ul>
1842              * </li>
1843              * </ul>
1844              * </li>
1845              * <li>Encode the set of post data arguments.</li>
1846              * <li>Join the encoded view state with the encoded set of post data arguments
1847              * to form the <code>query string</code> that will be sent to the server.</li>
1848              * <li>Create a request <code>context</code> object and set the properties:
1849              * <ul><li><code>source</code> (the source DOM element for this request)</li>
1850              * <li><code>onerror</code> (the error handler for this request)</li>
1851              * <li><code>onevent</code> (the event handler for this request)</li></ul>
1852              * The request context will be used during error/event handling.</li>
1853              * <li>Send a <code>begin</code> event following the procedure as outlined
1854              * in the Chapter 13 "Sending Events" section of the spec prose document <a
1855              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
1856              *  overview summary</a></li>
1857              * <li>Set the request header with the name: <code>Faces-Request</code> and the
1858              * value: <code>partial/ajax</code>.</li>
1859              * <li>Determine the <code>posting URL</code> as follows: If the hidden field
1860              * <code>javax.faces.encodedURL</code> is present in the submitting form, use its
1861              * value as the <code>posting URL</code>.  Otherwise, use the <code>action</code>
1862              * property of the <code>form</code> element as the <code>URL</code>.</li>
1863 
1864              * <li> 
1865 
1866              * <p><span class="changed_modified_2_2">Determine whether
1867              * or not any of the components in
1868              * <code>options.execute</code> (or its effective
1869              * equivalent) is a file upload component.  If not, send the
1870              * request as an <code>asynchronous POST</code> using the
1871              * <code>posting URL</code> that was determined in the
1872              * previous step.</span><p>
1873 
1874              * <div class="changed_added_2_2">
1875 
1876              * <p>Otherwise, verify that the <code>enctype</code>
1877              * property of the effective form is
1878              * <code>multipart/form-data</code> and throw an error if it
1879              * is not.  Send the request using a multi-part capable
1880              * transport layer, such as a hidden inline frame.  Note
1881              * that using a hidden inline frame does
1882              * <strong>not</strong> use <code>XMLHttpRequest</code>, but
1883              * the request must be sent with all the parameters that a
1884              * JSF <code>XMLHttpRequest</code> would have been sent
1885              * with.  In this way, the server side processing of the
1886              * request will be identical whether or the request is
1887              * multipart or not.</p>
1888 
1889              * <p>The <code>begin</code>, <code>complete</code>, and
1890              * <code>success</code> events must be emulated when using
1891              * the multipart transport.  This allows any listeners to
1892              * behave uniformly regardless of the multipart or
1893              * <code>XMLHttpRequest</code> nature of the transport.
1894 
1895              * </div>
1896 
1897 </li>
1898              * </ul>
1899              * Form serialization should occur just before the request is sent to minimize 
1900              * the amount of time between the creation of the serialized form data and the 
1901              * sending of the serialized form data (in the case of long requests in the queue).
1902              * Before the request is sent it must be put into a queue to ensure requests
1903              * are sent in the same order as when they were initiated.  The request callback function
1904              * must examine the queue and determine the next request to be sent.  The behavior of the
1905              * request callback function must be as follows:
1906              * <ul>
1907              * <li>If the request completed successfully invoke {@link jsf.ajax.response}
1908              * passing the <code>request</code> object.</li>
1909              * <li>If the request did not complete successfully, notify the client.</li>
1910              * <li>Regardless of the outcome of the request (success or error) every request in the
1911              * queue must be handled.  Examine the status of each request in the queue starting from
1912              * the request that has been in the queue the longest.  If the status of the request is
1913              * <code>complete</code> (readyState 4), dequeue the request (remove it from the queue).
1914              * If the request has not been sent (readyState 0), send the request.  Requests that are
1915              * taken off the queue and sent should not be put back on the queue.</li>
1916              * </ul>
1917              *
1918              * </p>
1919              *
1920              * @param source The DOM element that triggered this Ajax request, or an id string of the
1921              * element to use as the triggering element.
1922              * @param event The DOM event that triggered this Ajax request.  The
1923              * <code>event</code> argument is optional.
1924              * @param options The set of available options that can be sent as
1925              * request parameters to control client and/or server side
1926              * request processing. Acceptable name/value pair options are:
1927              * <table border="1">
1928              * <tr>
1929              * <th>name</th>
1930              * <th>value</th>
1931              * </tr>
1932              * <tr>
1933              * <td><code>execute</code></td>
1934              * <td><code>space seperated list of client identifiers</code></td>
1935              * </tr>
1936              * <tr>
1937              * <td><code>render</code></td>
1938              * <td><code>space seperated list of client identifiers</code></td>
1939              * </tr>
1940              * <tr>
1941              * <td><code>onevent</code></td>
1942              * <td><code>function to callback for event</code></td>
1943              * </tr>
1944              * <tr>
1945              * <td><code>onerror</code></td>
1946              * <td><code>function to callback for error</code></td>
1947              * </tr>
1948              * <tr>
1949              * <td><code>params</code></td>
1950              * <td><code>object containing parameters to include in the request</code></td>
1951              * </tr>
1952 
1953              * <tr class="changed_added_2_2">
1954 
1955              * <td><code>delay</code></td>
1956 
1957              * <td>If less than <em>delay</em> milliseconds elapses
1958              * between calls to <em>request()</em> only the most recent
1959              * one is sent and all other requests are discarded. If the
1960              * value of <em>delay</em> is the literal string
1961              * <code>'none'</code> without the quotes, or no delay is
1962              * specified, no delay is used. </td>
1963 
1964              * </tr>
1965 
1966              * </table>
1967              * The <code>options</code> argument is optional.
1968              * @member jsf.ajax
1969              * @function jsf.ajax.request
1970 
1971              * @throws Error if first required argument
1972              * <code>element</code> is not specified, or if one or more
1973              * of the components in the <code>options.execute</code>
1974              * list is a file upload component, but the form's enctype
1975              * is not set to <code>multipart/form-data</code>
1976              */
1977 
1978             request: function request(source, event, options) {
1979 
1980                 var element, form;   //  Element variables
1981                 var all, none;
1982 
1983                 if (typeof source === 'undefined' || source === null) {
1984                     throw new Error("jsf.ajax.request: source not set");
1985                 }
1986                 if(delayHandler) {
1987                     clearTimeout(delayHandler);
1988                     delayHandler = null;
1989                 }
1990 
1991                 // set up the element based on source
1992                 if (typeof source === 'string') {
1993                     element = document.getElementById(source);
1994                 } else if (typeof source === 'object') {
1995                     element = source;
1996                 } else {
1997                     throw new Error("jsf.request: source must be object or string");
1998                 }
1999                 // attempt to handle case of name unset
2000                 // this might be true in a badly written composite component
2001                 if (!element.name) {
2002                     element.name = element.id;
2003                 }
2004 
2005                 if (typeof(options) === 'undefined' || options === null) {
2006                     options = {};
2007                 }
2008 
2009                 // Error handler for this request
2010                 var onerror = false;
2011 
2012                 if (options.onerror && typeof options.onerror === 'function') {
2013                     onerror = options.onerror;
2014                 } else if (options.onerror && typeof options.onerror !== 'function') {
2015                     throw new Error("jsf.ajax.request: Added an onerror callback that was not a function");
2016                 }
2017 
2018                 // Event handler for this request
2019                 var onevent = false;
2020 
2021                 if (options.onevent && typeof options.onevent === 'function') {
2022                     onevent = options.onevent;
2023                 } else if (options.onevent && typeof options.onevent !== 'function') {
2024                     throw new Error("jsf.ajax.request: Added an onevent callback that was not a function");
2025                 }
2026 
2027                 form = getForm(element);
2028                 if (!form) {
2029                     throw new Error("jsf.ajax.request: Method must be called within a form");
2030                 }
2031                 var viewState = jsf.getViewState(form);
2032 
2033                 // Set up additional arguments to be used in the request..
2034                 // Make sure "javax.faces.source" is set up.
2035                 // If there were "execute" ids specified, make sure we
2036                 // include the identifier of the source element in the
2037                 // "execute" list.  If there were no "execute" ids
2038                 // specified, determine the default.
2039 
2040                 var args = {};
2041 
2042                 args["javax.faces.source"] = element.id;
2043 
2044                 if (event && !!event.type) {
2045                     args["javax.faces.partial.event"] = event.type;
2046                 }
2047 
2048                 // If we have 'execute' identifiers:
2049                 // Handle any keywords that may be present.
2050                 // If @none present anywhere, do not send the
2051                 // "javax.faces.partial.execute" parameter.
2052                 // The 'execute' and 'render' lists must be space
2053                 // delimited.
2054 
2055                 if (options.execute) {
2056                     none = options.execute.search(/@none/);
2057                     if (none < 0) {
2058                         all = options.execute.search(/@all/);
2059                         if (all < 0) {
2060                             options.execute = options.execute.replace("@this", element.id);
2061                             options.execute = options.execute.replace("@form", form.id);
2062                             var temp = options.execute.split(' ');
2063                             if (!isInArray(temp, element.name)) {
2064                                 options.execute = element.name + " " + options.execute;
2065                             }
2066                         } else {
2067                             options.execute = "@all";
2068                         }
2069                         args["javax.faces.partial.execute"] = options.execute;
2070                     }
2071                 } else {
2072                     options.execute = element.name + " " + element.id;
2073                     args["javax.faces.partial.execute"] = options.execute;
2074                 }
2075 
2076                 if (options.render) {
2077                     none = options.render.search(/@none/);
2078                     if (none < 0) {
2079                         all = options.render.search(/@all/);
2080                         if (all < 0) {
2081                             options.render = options.render.replace("@this", element.id);
2082                             options.render = options.render.replace("@form", form.id);
2083                         } else {
2084                             options.render = "@all";
2085                         }
2086                         args["javax.faces.partial.render"] = options.render;
2087                     }
2088                 }
2089                 var explicitlyDoNotDelay = ((typeof options.delay == 'undefined') || (typeof options.delay == 'string') &&
2090                                             (options.delay.toLowerCase() == 'none'));
2091                 var delayValue;
2092                 if (typeof options.delay == 'number') {
2093                     delayValue = options.delay;
2094                 } else if (!explicitlyDoNotDelay) {
2095                     throw new Error('invalid value for delay option: ' + options.delay);
2096                 }
2097 
2098                 // remove non-passthrough options
2099                 delete options.execute;
2100                 delete options.render;
2101                 delete options.onerror;
2102                 delete options.onevent;
2103                 delete options.delay;
2104 
2105                 // copy all other options to args
2106                 for (var property in options) {
2107                     if (options.hasOwnProperty(property)) {
2108                         args[property] = options[property];
2109                     }
2110                 }
2111 
2112                 args["javax.faces.partial.ajax"] = "true";
2113                 args["method"] = "POST";
2114 
2115                 // Determine the posting url
2116 
2117                 var encodedUrlField = form.elements["javax.faces.encodedURL"];
2118                 if (typeof encodedUrlField == 'undefined') {
2119                     args["url"] = form.action;
2120                 } else {
2121                     args["url"] = encodedUrlField.value;
2122                 }
2123                 var sendRequest = function() {
2124                     var ajaxEngine = new AjaxEngine();
2125                     ajaxEngine.setupArguments(args);
2126                     ajaxEngine.queryString = viewState;
2127                     ajaxEngine.context.onevent = onevent;
2128                     ajaxEngine.context.onerror = onerror;
2129                     ajaxEngine.context.sourceid = element.id;
2130                     ajaxEngine.context.formid = form.id;
2131                     ajaxEngine.context.render = args["javax.faces.partial.render"];
2132                     ajaxEngine.sendRequest();
2133 
2134                     // null out element variables to protect against IE memory leak
2135                     element = null;
2136                     form = null;
2137                     sendRequest = null;
2138                 };
2139 
2140                 if (explicitlyDoNotDelay) {
2141                     sendRequest();
2142                 } else {
2143                     delayHandler = setTimeout(sendRequest, delayValue);
2144                 }
2145 
2146             },
2147             /**
2148              * <p><span class="changed_modified_2_2">Receive</span> an Ajax response 
2149              * from the server.
2150              * <p><b>Usage:</b></p>
2151              * <pre><code>
2152              * jsf.ajax.response(request, context);
2153              * </pre></code>
2154              * <p><b>Implementation Requirements:</b></p>
2155              * This function must evaluate the markup returned in the
2156              * <code>request.responseXML</code> object and perform the following action:
2157              * <ul>
2158              * <p>If there is no XML response returned, signal an <code>emptyResponse</code>
2159              * error. If the XML response does not follow the format as outlined
2160              * in Appendix A of the spec prose document <a
2161              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
2162              *  overview summary</a> signal a <code>malformedError</code> error.  Refer to
2163              * section "Signaling Errors" in Chapter 13 of the spec prose document <a
2164              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
2165              *  overview summary</a>.</p>
2166              * <p>If the response was successfully processed, send a <code>success</code>
2167              * event as outlined in Chapter 13 "Sending Events" section of the spec prose
2168              * document <a
2169              * href="../../javadocs/overview-summary.html#prose_document">linked in the
2170              * overview summary</a>.</p>
2171              * <p><i>Update Element Processing</i></p>
2172              * The <code>update</code> element is used to update a single DOM element.  The
2173              * "id" attribute of the <code>update</code> element refers to the DOM element that
2174              * will be updated.  The contents of the <code>CDATA</code> section is the data that 
2175              * will be used when updating the contents of the DOM element as specified by the
2176              * <code><update></code> element identifier.
2177              * <li>If an <code>update</code> element is found in the response
2178              * with the identifier <code>javax.faces.ViewRoot</code>:
2179              * <pre><code><update id="javax.faces.ViewRoot">
2180              *    <![CDATA[...]]>
2181              * </update></code></pre>
2182              * Update the entire DOM replacing the appropriate <code>head</code> and/or
2183              * <code>body</code> sections with the content from the response.</li>
2184 
2185              * <li class="changed_modified_2_2">If an
2186              * <code>update</code> element is found in the response with
2187              * an identifier containing
2188              * <code>javax.faces.ViewState</code>:
2189 
2190              * <pre><code><update id="<VIEW_ROOT_CONTAINER_CLIENT_ID><SEP>javax.faces.ViewState<SEP><UNIQUE_PER_VIEW_NUMBER>">
2191              *    <![CDATA[...]]>
2192              * </update></code></pre>
2193 
2194              * locate and update the submitting form's
2195              * <code>javax.faces.ViewState</code> value with the
2196              * <code>CDATA</code> contents from the response.
2197              * <SEP>: is the currently configured
2198              * <code>UINamingContainer.getSeparatorChar()</code>.
2199              * <VIEW_ROOT_CONTAINER_CLIENT_ID> is the return from
2200              * <code>UIViewRoot.getContainerClientId()</code> on the
2201              * view from whence this state originated.
2202              * <UNIQUE_PER_VIEW_NUMBER> is a number that must be
2203              * unique within this view, but must not be included in the
2204              * view state.  This requirement is simply to satisfy XML
2205              * correctness in parity with what is done in the
2206              * corresponding non-partial JSF view.  Locate and update
2207              * the <code>javax.faces.ViewState</code> value for all
2208              * forms specified in the <code>render</code> target
2209              * list.</li>
2210 
2211              * <li class="changed_added_2_2">If an
2212              * <code>update</code> element is found in the response with
2213              * an identifier containing
2214              * <code>javax.faces.ClientWindow</code>:
2215 
2216              * <pre><code><update id="<VIEW_ROOT_CONTAINER_CLIENT_ID><SEP>javax.faces.ClientWindow<SEP><UNIQUE_PER_VIEW_NUMBER>">
2217              *    <![CDATA[...]]>
2218              * </update></code></pre>
2219 
2220              * locate and update the submitting form's
2221              * <code>javax.faces.ClientWindow</code> value with the
2222              * <code>CDATA</code> contents from the response.
2223              * <SEP>: is the currently configured
2224              * <code>UINamingContainer.getSeparatorChar()</code>.
2225              * <VIEW_ROOT_CONTAINER_CLIENT_ID> is the return from
2226              * <code>UIViewRoot.getContainerClientId()</code> on the
2227              * view from whence this state originated.             
2228              * <UNIQUE_PER_VIEW_NUMBER> is a number that must be
2229              * unique within this view, but must not be included in the
2230              * view state.  This requirement is simply to satisfy XML
2231              * correctness in parity with what is done in the
2232              * corresponding non-partial JSF view.  Locate and update
2233              * the <code>javax.faces.ClientWindow</code> value for all
2234              * forms specified in the <code>render</code> target
2235              * list.</li>
2236 
2237 
2238              * <li>If an <code>update</code> element is found in the response with the identifier
2239              * <code>javax.faces.ViewHead</code>:
2240              * <pre><code><update id="javax.faces.ViewHead">
2241              *    <![CDATA[...]]>
2242              * </update></code></pre>
2243              * update the document's <code>head</code> section with the <code>CDATA</code>
2244              * contents from the response.</li>
2245              * <li>If an <code>update</code> element is found in the response with the identifier
2246              * <code>javax.faces.ViewBody</code>:
2247              * <pre><code><update id="javax.faces.ViewBody">
2248              *    <![CDATA[...]]>
2249              * </update></code></pre>
2250              * update the document's <code>body</code> section with the <code>CDATA</code>
2251              * contents from the response.</li>
2252              * <li>For any other <code><update></code> element:
2253              * <pre><code><update id="update id">
2254              *    <![CDATA[...]]>
2255              * </update></code></pre>
2256              * Find the DOM element with the identifier that matches the
2257              * <code><update></code> element identifier, and replace its contents with
2258              * the <code><update></code> element's <code>CDATA</code> contents.</li>
2259              * </li>
2260              * <p><i>Insert Element Processing</i></p>
2261              * <li>If an <code><insert></code> element is found in the response with the
2262              * attribute <code>before</code>:
2263              * <pre><code><insert id="insert id" before="before id">
2264              *    <![CDATA[...]]>
2265              * </insert></code></pre>
2266              * <ul>
2267              * <li>Extract this <code><insert></code> element's <code>CDATA</code> contents
2268              * from the response.</li>
2269              * <li>Find the DOM element whose identifier matches <code>before id</code> and insert
2270              * the <code><insert></code> element's <code>CDATA</code> content before
2271              * the DOM element in the document.</li>
2272              * </ul>
2273              * </li>
2274              * <li>If an <code><insert></code> element is found in the response with the
2275              * attribute <code>after</code>:
2276              * <pre><code><insert id="insert id" after="after id">
2277              *    <![CDATA[...]]>
2278              * </insert></code></pre>
2279              * <ul>
2280              * <li>Extract this <code><insert></code> element's <code>CDATA</code> contents
2281              * from the response.</li>
2282              * <li>Find the DOM element whose identifier matches <code>after id</code> and insert
2283              * the <code><insert></code> element's <code>CDATA</code> content after
2284              * the DOM element in the document.</li>
2285              * </ul>
2286              * </li>
2287              * <p><i>Delete Element Processing</i></p>
2288              * <li>If a <code><delete></code> element is found in the response:
2289              * <pre><code><delete id="delete id"/></code></pre>
2290              * Find the DOM element whose identifier matches <code>delete id</code> and remove it
2291              * from the DOM.</li>
2292              * <p><i>Element Attribute Update Processing</i></p>
2293              * <li>If an <code><attributes></code> element is found in the response:
2294              * <pre><code><attributes id="id of element with attribute">
2295              *    <attribute name="attribute name" value="attribute value">
2296              *    ...
2297              * </attributes></code></pre>
2298              * <ul>
2299              * <li>Find the DOM element that matches the <code><attributes></code> identifier.</li>
2300              * <li>For each nested <code><attribute></code> element in <code><attribute></code>,
2301              * update the DOM element attribute value (whose name matches <code>attribute name</code>),
2302              * with <code>attribute value</code>.</li>
2303              * </ul>
2304              * </li>
2305              * <p><i>JavaScript Processing</i></p>
2306              * <li>If an <code><eval></code> element is found in the response:
2307              * <pre><code><eval>
2308              *    <![CDATA[...JavaScript...]]>
2309              * </eval></code></pre>
2310              * <ul>
2311              * <li>Extract this <code><eval></code> element's <code>CDATA</code> contents
2312              * from the response and execute it as if it were JavaScript code.</li>
2313              * </ul>
2314              * </li>
2315              * <p><i>Redirect Processing</i></p>
2316              * <li>If a <code><redirect></code> element is found in the response:
2317              * <pre><code><redirect url="redirect url"/></code></pre>
2318              * Cause a redirect to the url <code>redirect url</code>.</li>
2319              * <p><i>Error Processing</i></p>
2320              * <li>If an <code><error></code> element is found in the response:
2321              * <pre><code><error>
2322              *    <error-name>..fully qualified class name string...<error-name>
2323              *    <error-message><![CDATA[...]]><error-message>
2324              * </error></code></pre>
2325              * Extract this <code><error></code> element's <code>error-name</code> contents
2326              * and the <code>error-message</code> contents. Signal a <code>serverError</code> passing
2327              * the <code>errorName</code> and <code>errorMessage</code>.  Refer to
2328              * section "Signaling Errors" in Chapter 13 of the spec prose document <a
2329              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
2330              *  overview summary</a>.</li>
2331              * <p><i>Extensions</i></p>
2332              * <li>The <code><extensions></code> element provides a way for framework
2333              * implementations to provide their own information.</li>
2334              * <p><li>The implementation must check if <script> elements in the response can
2335              * be automatically run, as some browsers support this feature and some do not.  
2336              * If they can not be run, then scripts should be extracted from the response and
2337              * run separately.</li></p> 
2338              * </ul>
2339              *
2340              * </p>
2341              *
2342              * @param request The <code>XMLHttpRequest</code> instance that
2343              * contains the status code and response message from the server.
2344              *
2345              * @param context An object containing the request context, including the following properties:
2346              * the source element, per call onerror callback function, and per call onevent callback function.
2347              *
2348              * @throws  Error if request contains no data
2349              *
2350              * @function jsf.ajax.response
2351              */
2352             response: function response(request, context) {
2353                 if (!request) {
2354                     throw new Error("jsf.ajax.response: Request parameter is unset");
2355                 }
2356 
2357                 // ensure context source is the dom element and not the ID
2358                 // per 14.4.1 of the 2.0 specification.  We're doing it here
2359                 // *before* any errors or events are propagated becasue the
2360                 // DOM element may be removed after the update has been processed.
2361                 if (typeof context.sourceid === 'string') {
2362                     context.sourceid = document.getElementById(context.sourceid);
2363                 }
2364 
2365                 var xml = request.responseXML;
2366                 if (xml === null) {
2367                     sendError(request, context, "emptyResponse");
2368                     return;
2369                 }
2370 
2371                 if (getParseErrorText(xml) !== PARSED_OK) {
2372                     sendError(request, context, "malformedXML");
2373                     return;
2374                 }
2375 
2376                 var partialResponse = xml.getElementsByTagName("partial-response")[0];
2377                 var partialResponseId = partialResponse.getAttribute("id");
2378                 var responseType = partialResponse.firstChild;
2379 
2380                 if (responseType.nodeName === "error") { // it's an error
2381                     var errorName = responseType.firstChild.firstChild.nodeValue;
2382                     var errorMessage = responseType.firstChild.nextSibling.firstChild.nodeValue;
2383                     sendError(request, context, "serverError", null, errorName, errorMessage);
2384                     sendEvent(request, context, "success");
2385                     return;
2386                 }
2387 
2388 
2389                 if (responseType.nodeName === "redirect") {
2390                     window.location = responseType.getAttribute("url");
2391                     return;
2392                 }
2393 
2394 
2395                 if (responseType.nodeName !== "changes") {
2396                     sendError(request, context, "malformedXML", "Top level node must be one of: changes, redirect, error, received: " + responseType.nodeName + " instead.");
2397                     return;
2398                 }
2399 
2400 
2401                 var changes = responseType.childNodes;
2402 
2403                 try {
2404                     for (var i = 0; i < changes.length; i++) {
2405                         switch (changes[i].nodeName) {
2406                             case "update":
2407                                 doUpdate(changes[i], context, partialResponseId);
2408                                 break;
2409                             case "delete":
2410                                 doDelete(changes[i]);
2411                                 break;
2412                             case "insert":
2413                                 doInsert(changes[i]);
2414                                 break;
2415                             case "attributes":
2416                                 doAttributes(changes[i]);
2417                                 break;
2418                             case "eval":
2419                                 doEval(changes[i]);
2420                                 break;
2421                             case "extension":
2422                                 // no action
2423                                 break;
2424                             default:
2425                                 sendError(request, context, "malformedXML", "Changes allowed are: update, delete, insert, attributes, eval, extension.  Received " + changes[i].nodeName + " instead.");
2426                                 return;
2427                         }
2428                     }
2429                 } catch (ex) {
2430                     sendError(request, context, "malformedXML", ex.message);
2431                     return;
2432                 }
2433                 sendEvent(request, context, "success");
2434 
2435             }
2436         };
2437     }();
2438 
2439     /**
2440      *
2441      * <p>Return the value of <code>Application.getProjectStage()</code> for
2442      * the currently running application instance.  Calling this method must
2443      * not cause any network transaction to happen to the server.</p>
2444      * <p><b>Usage:</b></p>
2445      * <pre><code>
2446      * var stage = jsf.getProjectStage();
2447      * if (stage === ProjectStage.Development) {
2448      *  ...
2449      * } else if stage === ProjectStage.Production) {
2450      *  ...
2451      * }
2452      * </code></pre>
2453      *
2454      * @returns String <code>String</code> representing the current state of the
2455      * running application in a typical product development lifecycle.  Refer
2456      * to <code>javax.faces.application.Application.getProjectStage</code> and
2457      * <code>javax.faces.application.ProjectStage</code>.
2458      * @function jsf.getProjectStage
2459      */
2460     jsf.getProjectStage = function() {
2461         // First, return cached value if available
2462         if (typeof mojarra !== 'undefined' && typeof mojarra.projectStageCache !== 'undefined') {
2463             return mojarra.projectStageCache;
2464         }
2465         var scripts = document.getElementsByTagName("script"); // nodelist of scripts
2466         var script; // jsf.js script
2467         var s = 0; // incremental variable for for loop
2468         var stage; // temp value for stage
2469         var match; // temp value for match
2470         while (s < scripts.length) {
2471             if (typeof scripts[s].src === 'string' && scripts[s].src.match('\/javax\.faces\.resource\/jsf\.js\?.*ln=javax\.faces')) {
2472                 script = scripts[s].src;
2473                 break;
2474             }
2475             s++;
2476         }
2477         if (typeof script == "string") {
2478             match = script.match("stage=(.*)");
2479             if (match) {
2480                 stage = match[1];
2481             }
2482         }
2483         if (typeof stage === 'undefined' || !stage) {
2484             stage = "Production";
2485         }
2486 
2487         mojarra = mojarra || {};
2488         mojarra.projectStageCache = stage;
2489 
2490         return mojarra.projectStageCache;
2491     };
2492 
2493 
2494     /**
2495      * <p>Collect and encode state for input controls associated
2496      * with the specified <code>form</code> element.  This will include
2497      * all input controls of type <code>hidden</code>.</p>
2498      * <p><b>Usage:</b></p>
2499      * <pre><code>
2500      * var state = jsf.getViewState(form);
2501      * </pre></code>
2502      *
2503      * @param form The <code>form</code> element whose contained
2504      * <code>input</code> controls will be collected and encoded.
2505      * Only successful controls will be collected and encoded in
2506      * accordance with: <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2">
2507      * Section 17.13.2 of the HTML Specification</a>.
2508      *
2509      * @returns String The encoded state for the specified form's input controls.
2510      * @function jsf.getViewState
2511      */
2512     jsf.getViewState = function(form) {
2513         if (!form) {
2514             throw new Error("jsf.getViewState:  form must be set");
2515         }
2516         var els = form.elements;
2517         var len = els.length;
2518         // create an array which we'll use to hold all the intermediate strings
2519         // this bypasses a problem in IE when repeatedly concatenating very
2520         // large strings - we'll perform the concatenation once at the end
2521         var qString = [];
2522         var addField = function(name, value) {
2523             var tmpStr = "";
2524             if (qString.length > 0) {
2525                 tmpStr = "&";
2526             }
2527             tmpStr += encodeURIComponent(name) + "=" + encodeURIComponent(value);
2528             qString.push(tmpStr);
2529         };
2530         for (var i = 0; i < len; i++) {
2531             var el = els[i];
2532             if (!el.disabled) {
2533                 switch (el.type) {
2534                     case 'submit':
2535                     case 'reset':
2536                     case 'button':
2537                     case 'image':
2538                     case 'file':
2539                         break;
2540                     case 'select-one':
2541                         if (el.selectedIndex >= 0) {
2542                             addField(el.name, el.options[el.selectedIndex].value);
2543                         }
2544                         break;
2545                     case 'select-multiple':
2546                         for (var j = 0; j < el.options.length; j++) {
2547                             if (el.options[j].selected) {
2548                                 addField(el.name, el.options[j].value);
2549                             }
2550                         }
2551                         break;
2552                     case 'checkbox':
2553                     case 'radio':
2554                         if (el.checked) {
2555                             addField(el.name, el.value || 'on');
2556                         }
2557                         break;
2558                     default:
2559                         // this is for any input incl.  text', 'password', 'hidden', 'textarea'
2560                         addField(el.name, el.value);
2561                         break;
2562                 }
2563             }
2564         }
2565         // concatenate the array
2566         return qString.join("");
2567     };
2568 
2569     /**
2570      * <p class="changed_added_2_2">Return the windowId of the window
2571      * in which the argument form is rendered.</p>
2572 
2573      * @param {optional String|DomNode} node. Determine the nature of
2574      * the argument.  If not present, search for the windowId within
2575      * <code>document.forms</code>.  If present and the value is a
2576      * string, assume the string is a DOM id and get the element with
2577      * that id and start the search from there.  If present and the
2578      * value is a DOM element, start the search from there.
2579 
2580      * @returns String The windowId of the current window, or null 
2581      *  if the windowId cannot be determined.
2582 
2583      * @throws an error if more than one unique WindowId is found.
2584 
2585      * @function jsf.getViewState
2586      */
2587     jsf.getClientWindow = function(node) {
2588         var FORM = "form";
2589         var WIN_ID = "javax.faces.ClientWindow";
2590 
2591         var fetchWindowIdFromForms = function (forms) {
2592             var result_idx = {};
2593             var result;
2594             var foundCnt = 0;
2595             for (var cnt = forms.length - 1; cnt >= 0; cnt--) {
2596                 var UDEF = 'undefined';
2597                 var currentForm = forms[cnt];
2598                 var windowId = currentForm[WIN_ID] && currentForm[WIN_ID].value;
2599                 if (UDEF != typeof windowId) {
2600                     if (foundCnt > 0 && UDEF == typeof result_idx[windowId]) throw Error("Multiple different windowIds found in document");
2601                     result = windowId;
2602                     result_idx[windowId] = true;
2603                     foundCnt++;
2604                 }
2605             }
2606             return result;
2607         }
2608 
2609         var getChildForms = function (currentElement) {
2610             //Special condition no element we return document forms
2611             //as search parameter, ideal would be to
2612             //have the viewroot here but the frameworks
2613             //can deal with that themselves by using
2614             //the viewroot as currentElement
2615             if (!currentElement) {
2616                 return document.forms;
2617             }
2618             
2619             var targetArr = [];
2620             if (!currentElement.tagName) return [];
2621             else if (currentElement.tagName.toLowerCase() == FORM) {
2622                 targetArr.push(currentElement);
2623                 return targetArr;
2624             }
2625             
2626             //if query selectors are supported we can take
2627             //a non recursive shortcut
2628             if (currentElement.querySelectorAll) {
2629                 return currentElement.querySelectorAll(FORM);
2630             }
2631             
2632             //old recursive way, due to flakeyness of querySelectorAll
2633             for (var cnt = currentElement.childNodes.length - 1; cnt >= 0; cnt--) {
2634                 var currentChild = currentElement.childNodes[cnt];
2635                 targetArr = targetArr.concat(getChildForms(currentChild, FORM));
2636             }
2637             return targetArr;
2638         }
2639         
2640         var fetchWindowIdFromURL = function () {
2641             var href = window.location.href;
2642             var windowId = "windowId";
2643             var regex = new RegExp("[\\?&]" + windowId + "=([^&#\\;]*)");
2644             var results = regex.exec(href);
2645             //initial trial over the url and a regexp
2646             if (results != null) return results[1];
2647             return null;
2648         }
2649         
2650         //byId ($)
2651         var finalNode = (node && (typeof node == "string" || node instanceof String)) ?
2652             document.getElementById(node) : (node || null);
2653         
2654         var forms = getChildForms(finalNode);
2655         var result = fetchWindowIdFromForms(forms);
2656         return (null != result) ? result : fetchWindowIdFromURL();
2657         
2658 
2659     };
2660 
2661 
2662     /**
2663      * The namespace for JavaServer Faces JavaScript utilities.
2664      * @name jsf.util
2665      * @namespace
2666      */
2667     jsf.util = {};
2668 
2669     /**
2670      * <p>A varargs function that invokes an arbitrary number of scripts.
2671      * If any script in the chain returns false, the chain is short-circuited
2672      * and subsequent scripts are not invoked.  Any number of scripts may
2673      * specified after the <code>event</code> argument.</p>
2674      *
2675      * @param source The DOM element that triggered this Ajax request, or an
2676      * id string of the element to use as the triggering element.
2677      * @param event The DOM event that triggered this Ajax request.  The
2678      * <code>event</code> argument is optional.
2679      *
2680      * @returns boolean <code>false</code> if any scripts in the chain return <code>false</code>,
2681      *  otherwise returns <code>true</code>
2682      * 
2683      * @function jsf.util.chain
2684      */
2685     jsf.util.chain = function(source, event) {
2686 
2687         if (arguments.length < 3) {
2688             return true;
2689         }
2690 
2691         // RELEASE_PENDING rogerk - shouldn't this be getElementById instead of null
2692         var thisArg = (typeof source === 'object') ? source : null;
2693 
2694         // Call back any scripts that were passed in
2695         for (var i = 2; i < arguments.length; i++) {
2696 
2697             var f = new Function("event", arguments[i]);
2698             var returnValue = f.call(thisArg, event);
2699 
2700             if (returnValue === false) {
2701                 return false;
2702             }
2703         }
2704         return true;
2705         
2706     };
2707 
2708     /**
2709      * <p class="changed_added_2_2">The result of calling
2710      * <code>UINamingContainer.getNamingContainerSeparatorChar().</code></p>
2711      */
2712     jsf.separatorchar = '#{facesContext.namingContainerSeparatorChar}';
2713 
2714     /**
2715      * <p>An integer specifying the specification version that this file implements.
2716      * It's format is: rightmost two digits, bug release number, next two digits,
2717      * minor release number, leftmost digits, major release number.
2718      * This number may only be incremented by a new release of the specification.</p>
2719      */
2720     jsf.specversion = 22000;
2721 
2722     /**
2723      * <p>An integer specifying the implementation version that this file implements.
2724      * It's a monotonically increasing number, reset with every increment of
2725      * <code>jsf.specversion</code>
2726      * This number is implementation dependent.</p>
2727      */
2728     jsf.implversion = 3;
2729 
2730 
2731 } //end if version detection block
2732