1 /*
  2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3  *
  4  * Copyright 1997-2013 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 {
773                     //setting property to '' seems to be the only cross-browser method for removing an attribute
774                     //avoid setting 'value' property to '' for checkbox and radio input elements because then the
775                     //'value' is used instead of the 'checked' property when the form is serialized by the browser
776                     if (attributeName == "value" && (target.type != 'checkbox' && target.type != 'radio')) {
777                          target[propertyName] = '';
778                     }
779                     target.removeAttribute(attributeName);
780                 }
781             }
782 
783             var booleanPropertyNames = isInputElement ? inputElementBooleanProperties : [];
784             for (var jIndex = 0, jLength = booleanPropertyNames.length; jIndex < jLength; jIndex++) {
785                 var booleanPropertyName = booleanPropertyNames[jIndex];
786                 var newBooleanValue = source[booleanPropertyName];
787                 var oldBooleanValue = target[booleanPropertyName];
788                 if (oldBooleanValue != newBooleanValue) {
789                     target[booleanPropertyName] = newBooleanValue;
790                 }
791             }
792 
793             //'style' attribute special case
794             if (sourceAttributeDetector('style')) {
795                 var newStyle;
796                 var oldStyle;
797                 if (isIE()) {
798                     newStyle = source.style.cssText;
799                     oldStyle = target.style.cssText;
800                     if (newStyle != oldStyle) {
801                         target.style.cssText = newStyle;
802                     }
803                 } else {
804                     newStyle = source.getAttribute('style');
805                     oldStyle = target.getAttribute('style');
806                     if (newStyle != oldStyle) {
807                         target.setAttribute('style', newStyle);
808                     }
809                 }
810             } else if (targetAttributeDetector('style')){
811                 target.removeAttribute('style');
812             }
813 
814             // Special case for 'dir' attribute
815             if (!isIE() && source.dir != target.dir) {
816                 if (sourceAttributeDetector('dir')) {
817                     target.dir = source.dir;
818                 } else if (targetAttributeDetector('dir')) {
819                     target.dir = '';
820                 }
821             }
822 
823             for (var lIndex = 0, lLength = listenerNames.length; lIndex < lLength; lIndex++) {
824                 var name = listenerNames[lIndex];
825                 target[name] = source[name] ? source[name] : null;
826                 if (source[name]) {
827                     source[name] = null;
828                 }
829             }
830 
831             //clone HTML5 data-* attributes
832             try{
833                 var targetDataset = target.dataset;
834                 var sourceDataset = source.dataset;
835                 if (targetDataset || sourceDataset) {
836                     //cleanup the dataset
837                     for (var tp in targetDataset) {
838                         delete targetDataset[tp];
839                     }
840                     //copy dataset's properties
841                     for (var sp in sourceDataset) {
842                         targetDataset[sp] = sourceDataset[sp];
843                     }
844                 }
845             } catch (ex) {
846                 //most probably dataset properties are not supported
847             }
848         };
849 
850         /**
851          * Replace an element from one document into another
852          * @param newElement new element to put in document
853          * @param origElement original element to replace
854          * @ignore
855          */
856         var elementReplace = function elementReplace(newElement, origElement) {
857             copyChildNodes(newElement, origElement);
858             // sadly, we have to reparse all over again
859             // to reregister the event handlers and styles
860             // PENDING do some performance tests on large pages
861             origElement.innerHTML = origElement.innerHTML;
862 
863             try {
864                 cloneAttributes(origElement, newElement);
865             } catch (ex) {
866                 // if in dev mode, report an error, else try to limp onward
867                 if (jsf.getProjectStage() == "Development") {
868                     throw new Error("Error updating attributes");
869                 }
870             }
871             deleteNode(newElement);
872 
873         };
874 
875         /**
876          * Create a new document, then select the body element within it
877          * @param docStr Stringified version of document to create
878          * @return element the body element
879          * @ignore
880          */
881         var getBodyElement = function getBodyElement(docStr) {
882 
883             var doc;  // intermediate document we'll create
884             var body; // Body element to return
885 
886             if (typeof DOMParser !== "undefined") {  // FF, S, Chrome
887                 doc = (new DOMParser()).parseFromString(docStr, "text/xml");
888             } else if (typeof ActiveXObject !== "undefined") { // IE
889                 doc = new ActiveXObject("MSXML2.DOMDocument");
890                 doc.loadXML(docStr);
891             } else {
892                 throw new Error("You don't seem to be running a supported browser");
893             }
894 
895             if (getParseErrorText(doc) !== PARSED_OK) {
896                 throw new Error(getParseErrorText(doc));
897             }
898 
899             body = doc.getElementsByTagName("body")[0];
900 
901             if (!body) {
902                 throw new Error("Can't find body tag in returned document.");
903             }
904 
905             return body;
906         };
907 
908         /**
909          * Find view state field for a given form.
910          * @param form
911          * @ignore
912          */
913         var getViewStateElement = function getViewStateElement(form) {
914             var viewStateElement = form['javax.faces.ViewState'];
915 
916             if (viewStateElement) {
917                 return viewStateElement;
918             } else {
919                 var formElements = form.elements;
920                 for (var i = 0, length = formElements.length; i < length; i++) {
921                     var formElement = formElements[i];
922                     if (formElement.name == 'javax.faces.ViewState') {
923                         return formElement;
924                     }
925                 }
926             }
927 
928             return undefined;
929         };
930 
931         /**
932          * Do update.
933          * @param element element to update
934          * @param context context of request
935          * @ignore
936          */
937         var doUpdate = function doUpdate(element, context, partialResponseId) {
938             var id, content, markup, state, windowId;
939             var stateForm, windowIdForm;
940             var scripts = []; // temp holding value for array of script nodes
941 
942             id = element.getAttribute('id');
943             var viewStateRegex = new RegExp("javax.faces.ViewState" +
944                                             jsf.separatorchar + ".*$");
945             var windowIdRegex = new RegExp("^.*" + jsf.separatorchar + 
946                                            "javax.faces.ClientWindow" +
947                                             jsf.separatorchar + ".*$");
948             if (id.match(viewStateRegex)) {
949 
950                 state = element.firstChild;
951 
952                 // Now set the view state from the server into the DOM
953                 // but only for the form that submitted the request.
954 
955                 stateForm = document.getElementById(context.formid);
956                 if (!stateForm || !stateForm.elements) {
957                     // if the form went away for some reason, or it lacks elements 
958                     // we're going to just return silently.
959                     return;
960                 }
961                 var field = getViewStateElement(stateForm);
962                 if (typeof field == 'undefined') {
963                     field = document.createElement("input");
964                     field.type = "hidden";
965                     field.name = "javax.faces.ViewState";
966                     stateForm.appendChild(field);
967                 }
968                 field.value = state.nodeValue;
969 
970                 // Now set the view state from the server into the DOM
971                 // for any form that is a render target.
972 
973                 if (typeof context.render !== 'undefined' && context.render !== null) {
974                     var temp = context.render.split(' ');
975                     for (var i = 0; i < temp.length; i++) {
976                         if (temp.hasOwnProperty(i)) {
977                             // See if the element is a form and
978                             // the form is not the one that caused the submission..
979                             var f = document.forms[temp[i]];
980                             if (typeof f !== 'undefined' && f !== null && f.id !== context.formid) {
981                                 field = getViewStateElement(f);
982                                 if (typeof field === 'undefined') {
983                                     field = document.createElement("input");
984                                     field.type = "hidden";
985                                     field.name = "javax.faces.ViewState";
986                                     f.appendChild(field);
987                                 }
988                                 field.value = state.nodeValue;
989                             }
990                         }
991                     }
992                 }
993                 return;
994             } else if (id.match(windowIdRegex)) {
995 
996                 windowId = element.firstChild;
997 
998                 // Now set the windowId from the server into the DOM
999                 // but only for the form that submitted the request.
1000 
1001                 windowIdForm = document.getElementById(context.formid);
1002                 if (!windowIdForm || !windowIdForm.elements) {
1003                     // if the form went away for some reason, or it lacks elements 
1004                     // we're going to just return silently.
1005                     return;
1006                 }
1007                 var field = windowIdForm.elements["javax.faces.ClientWindow"];
1008                 if (typeof field == 'undefined') {
1009                     field = document.createElement("input");
1010                     field.type = "hidden";
1011                     field.name = "javax.faces.ClientWindow";
1012                     windowIdForm.appendChild(field);
1013                 }
1014                 field.value = windowId.nodeValue;
1015 
1016                 // Now set the windowId from the server into the DOM
1017                 // for any form that is a render target.
1018 
1019                 if (typeof context.render !== 'undefined' && context.render !== null) {
1020                     var temp = context.render.split(' ');
1021                     for (var i = 0; i < temp.length; i++) {
1022                         if (temp.hasOwnProperty(i)) {
1023                             // See if the element is a form and
1024                             // the form is not the one that caused the submission..
1025                             var f = document.forms[temp[i]];
1026                             if (typeof f !== 'undefined' && f !== null && f.id !== context.formid) {
1027                                 field = f.elements["javax.faces.ClientWindow"];
1028                                 if (typeof field === 'undefined') {
1029                                     field = document.createElement("input");
1030                                     field.type = "hidden";
1031                                     field.name = "javax.faces.ClientWindow";
1032                                     f.appendChild(field);
1033                                 }
1034                                 field.value = windowId.nodeValue;
1035                             }
1036                         }
1037                     }
1038                 }
1039                 return;
1040             }
1041 
1042             // join the CDATA sections in the markup
1043             markup = '';
1044             for (var j = 0; j < element.childNodes.length; j++) {
1045                 content = element.childNodes[j];
1046                 markup += content.nodeValue;
1047             }
1048 
1049             var src = markup;
1050 
1051             // If our special render all markup is present..
1052             if (id === "javax.faces.ViewRoot" || id === "javax.faces.ViewBody") {
1053                 var bodyStartEx = new RegExp("< *body[^>]*>", "gi");
1054                 var bodyEndEx = new RegExp("< */ *body[^>]*>", "gi");
1055                 var newsrc;
1056 
1057                 var docBody = document.getElementsByTagName("body")[0];
1058                 var bodyStart = bodyStartEx.exec(src);
1059 
1060                 if (bodyStart !== null) { // replace body tag
1061                     // First, try with XML manipulation
1062                     try {
1063                         // Get scripts from text
1064                         scripts = stripScripts(src);
1065                         // Remove scripts from text
1066                         newsrc = src.replace(/<script[^>]*type="text\/javascript"*>([\S\s]*?)<\/script>/igm, "");
1067                         elementReplace(getBodyElement(newsrc), docBody);
1068                         runScripts(scripts);
1069                     } catch (e) {
1070                         // OK, replacing the body didn't work with XML - fall back to quirks mode insert
1071                         var srcBody, bodyEnd;
1072                         // if src contains </body>
1073                         bodyEnd = bodyEndEx.exec(src);
1074                         if (bodyEnd !== null) {
1075                             srcBody = src.substring(bodyStartEx.lastIndex,
1076                                     bodyEnd.index);
1077                         } else { // can't find the </body> tag, punt
1078                             srcBody = src.substring(bodyStartEx.lastIndex);
1079                         }
1080                         // replace body contents with innerHTML - note, script handling happens within function
1081                         elementReplaceStr(docBody, "body", srcBody);
1082 
1083                     }
1084 
1085                 } else {  // replace body contents with innerHTML - note, script handling happens within function
1086                     elementReplaceStr(docBody, "body", src);
1087                 }
1088             } else if (id === "javax.faces.ViewHead") {
1089                 throw new Error("javax.faces.ViewHead not supported - browsers cannot reliably replace the head's contents");
1090             } else {
1091                 var d = $(id);
1092                 if (!d) {
1093                     throw new Error("During update: " + id + " not found");
1094                 }
1095                 var parent = d.parentNode;
1096                 // Trim space padding before assigning to innerHTML
1097                 var html = src.replace(/^\s+/g, '').replace(/\s+$/g, '');
1098                 var parserElement = document.createElement('div');
1099                 var tag = d.nodeName.toLowerCase();
1100                 var tableElements = ['td', 'th', 'tr', 'tbody', 'thead', 'tfoot'];
1101                 var isInTable = false;
1102                 for (var tei = 0, tel = tableElements.length; tei < tel; tei++) {
1103                     if (tableElements[tei] == tag) {
1104                         isInTable = true;
1105                         break;
1106                     }
1107                 }
1108                 if (isInTable) {
1109 
1110                     if (isAutoExec()) {
1111                         // Create html
1112                         parserElement.innerHTML = '<table>' + html + '</table>';
1113                     } else {
1114                         // Get the scripts from the text
1115                         scripts = stripScripts(html);
1116                         // Remove scripts from text
1117                         html = html.replace(/<script[^>]*type="text\/javascript"*>([\S\s]*?)<\/script>/igm,"");
1118                         parserElement.innerHTML = '<table>' + html + '</table>';
1119                     }
1120                     var newElement = parserElement.firstChild;
1121                     //some browsers will also create intermediary elements such as table>tbody>tr>td
1122                     while ((null !== newElement) && (id !== newElement.id)) {
1123                         newElement = newElement.firstChild;
1124                     }
1125                     parent.replaceChild(newElement, d);
1126                     runScripts(scripts);
1127                 } else if (d.nodeName.toLowerCase() === 'input') {
1128                     // special case handling for 'input' elements
1129                     // in order to not lose focus when updating,
1130                     // input elements need to be added in place.
1131                     parserElement = document.createElement('div');
1132                     parserElement.innerHTML = html;
1133                     newElement = parserElement.firstChild;
1134 
1135                     cloneAttributes(d, newElement);
1136                     deleteNode(parserElement);
1137                 } else if (html.length > 0) {
1138                     if (isAutoExec()) {
1139                         // Create html
1140                         parserElement.innerHTML = html;
1141                     } else {
1142                         // Get the scripts from the text
1143                         scripts = stripScripts(html);
1144                         // Remove scripts from text
1145                         html = html.replace(/<script[^>]*type="text\/javascript"*>([\S\s]*?)<\/script>/igm,"");
1146                         parserElement.innerHTML = html;
1147                     }
1148                     replaceNode(parserElement.firstChild, d);
1149                     deleteNode(parserElement);
1150                     runScripts(scripts);
1151                 }
1152             }
1153         };
1154 
1155         /**
1156          * Delete a node specified by the element.
1157          * @param element
1158          * @ignore
1159          */
1160         var doDelete = function doDelete(element) {
1161             var id = element.getAttribute('id');
1162             var target = $(id);
1163             deleteNode(target);
1164         };
1165 
1166         /**
1167          * Insert a node specified by the element.
1168          * @param element
1169          * @ignore
1170          */
1171         var doInsert = function doInsert(element) {
1172             var tablePattern = new RegExp("<\\s*(td|th|tr|tbody|thead|tfoot)", "i");
1173             var scripts = [];
1174             var target = $(element.firstChild.getAttribute('id'));
1175             var parent = target.parentNode;
1176             var html = element.firstChild.firstChild.nodeValue;
1177             var isInTable = tablePattern.test(html);
1178 
1179             if (!isAutoExec())  {
1180                 // Get the scripts from the text
1181                 scripts = stripScripts(html);
1182                 // Remove scripts from text
1183                 html = html.replace(/<script[^>]*type="text\/javascript"*>([\S\s]*?)<\/script>/igm,"");
1184             }
1185             var tempElement = document.createElement('div');
1186             var newElement = null;
1187             if (isInTable)  {
1188                 tempElement.innerHTML = '<table>' + html + '</table>';
1189                 newElement = tempElement.firstChild;
1190                 //some browsers will also create intermediary elements such as table>tbody>tr>td
1191                 //test for presence of id on the new element since we do not have it directly
1192                 while ((null !== newElement) && ("" == newElement.id)) {
1193                     newElement = newElement.firstChild;
1194                 }
1195             } else {
1196                 tempElement.innerHTML = html;
1197                 newElement = tempElement.firstChild;
1198             }
1199 
1200             if (element.firstChild.nodeName === 'after') {
1201                 // Get the next in the list, to insert before
1202                 target = target.nextSibling;
1203             }  // otherwise, this is a 'before' element
1204             if (!!tempElement.innerHTML) { // check if only scripts were inserted - if so, do nothing here
1205                 parent.insertBefore(newElement, target);
1206             }
1207             runScripts(scripts);
1208             deleteNode(tempElement);
1209         };
1210 
1211         /**
1212          * Modify attributes of given element id.
1213          * @param element
1214          * @ignore
1215          */
1216         var doAttributes = function doAttributes(element) {
1217 
1218             // Get id of element we'll act against
1219             var id = element.getAttribute('id');
1220 
1221             var target = $(id);
1222 
1223             if (!target) {
1224                 throw new Error("The specified id: " + id + " was not found in the page.");
1225             }
1226 
1227             // There can be multiple attributes modified.  Loop through the list.
1228             var nodes = element.childNodes;
1229             for (var i = 0; i < nodes.length; i++) {
1230                 var name = nodes[i].getAttribute('name');
1231                 var value = nodes[i].getAttribute('value');
1232                 if (!isIE()) {
1233                     if (name === 'value') {
1234                         target.value = value;
1235                     } else if (name === 'disabled') {
1236                         target.disabled = value;
1237                     } else {
1238                         target.setAttribute(name, value);
1239                     }
1240                 } else { // if it's IE, then quite a bit more work is required
1241                     if (name === 'class') {
1242                         name = 'className';
1243                         target.setAttribute(name, value, 0);
1244                     } else if (name === "for") {
1245                         name = 'htmlFor';
1246                         target.setAttribute(name, value, 0);
1247                     } else if (name === 'style') {
1248                         target.style.setAttribute('cssText', value, 0);
1249                     } else if (name.substring(0, 2) === 'on') {
1250                         var fn = function(value) {
1251                             return function() {
1252                                 window.execScript(value);
1253                             };
1254                         }(value);
1255                         target.setAttribute(name, fn, 0);
1256                     } else if (name === 'dir') {
1257                         if (jsf.getProjectStage() == 'Development') {
1258                             throw new Error("Cannot set 'dir' attribute in IE");
1259                         }
1260                     } else {
1261                         target.setAttribute(name, value, 0);
1262                     }
1263                 }
1264             }
1265         };
1266 
1267         /**
1268          * Eval the CDATA of the element.
1269          * @param element to eval
1270          * @ignore
1271          */
1272         var doEval = function doEval(element) {
1273             var evalText = element.firstChild.nodeValue;
1274             globalEval(evalText);
1275         };
1276 
1277         /**
1278          * Ajax Request Queue
1279          * @ignore
1280          */
1281         var Queue = new function Queue() {
1282 
1283             // Create the internal queue
1284             var queue = [];
1285 
1286 
1287             // the amount of space at the front of the queue, initialised to zero
1288             var queueSpace = 0;
1289 
1290             /** Returns the size of this Queue. The size of a Queue is equal to the number
1291              * of elements that have been enqueued minus the number of elements that have
1292              * been dequeued.
1293              * @ignore
1294              */
1295             this.getSize = function getSize() {
1296                 return queue.length - queueSpace;
1297             };
1298 
1299             /** Returns true if this Queue is empty, and false otherwise. A Queue is empty
1300              * if the number of elements that have been enqueued equals the number of
1301              * elements that have been dequeued.
1302              * @ignore
1303              */
1304             this.isEmpty = function isEmpty() {
1305                 return (queue.length === 0);
1306             };
1307 
1308             /** Enqueues the specified element in this Queue.
1309              *
1310              * @param element - the element to enqueue
1311              * @ignore
1312              */
1313             this.enqueue = function enqueue(element) {
1314                 // Queue the request
1315                 queue.push(element);
1316             };
1317 
1318 
1319             /** Dequeues an element from this Queue. The oldest element in this Queue is
1320              * removed and returned. If this Queue is empty then undefined is returned.
1321              *
1322              * @returns Object The element that was removed from the queue.
1323              * @ignore
1324              */
1325             this.dequeue = function dequeue() {
1326                 // initialise the element to return to be undefined
1327                 var element = undefined;
1328 
1329                 // check whether the queue is empty
1330                 if (queue.length) {
1331                     // fetch the oldest element in the queue
1332                     element = queue[queueSpace];
1333 
1334                     // update the amount of space and check whether a shift should occur
1335                     if (++queueSpace * 2 >= queue.length) {
1336                         // set the queue equal to the non-empty portion of the queue
1337                         queue = queue.slice(queueSpace);
1338                         // reset the amount of space at the front of the queue
1339                         queueSpace = 0;
1340                     }
1341                 }
1342                 // return the removed element
1343                 try {
1344                     return element;
1345                 } finally {
1346                     element = null; // IE 6 leak prevention
1347                 }
1348             };
1349 
1350             /** Returns the oldest element in this Queue. If this Queue is empty then
1351              * undefined is returned. This function returns the same value as the dequeue
1352              * function, but does not remove the returned element from this Queue.
1353              * @ignore
1354              */
1355             this.getOldestElement = function getOldestElement() {
1356                 // initialise the element to return to be undefined
1357                 var element = undefined;
1358 
1359                 // if the queue is not element then fetch the oldest element in the queue
1360                 if (queue.length) {
1361                     element = queue[queueSpace];
1362                 }
1363                 // return the oldest element
1364                 try {
1365                     return element;
1366                 } finally {
1367                     element = null; //IE 6 leak prevention
1368                 }
1369             };
1370         }();
1371 
1372 
1373         /**
1374          * AjaxEngine handles Ajax implementation details.
1375          * @ignore
1376          */
1377         var AjaxEngine = function AjaxEngine() {
1378 
1379             var req = {};                  // Request Object
1380             req.url = null;                // Request URL
1381             req.context = {};              // Context of request and response
1382             req.context.sourceid = null;   // Source of this request
1383             req.context.onerror = null;    // Error handler for request
1384             req.context.onevent = null;    // Event handler for request
1385             req.context.formid = null;     // Form that's the context for this request
1386             req.xmlReq = null;             // XMLHttpRequest Object
1387             req.async = true;              // Default - Asynchronous
1388             req.parameters = {};           // Parameters For GET or POST
1389             req.queryString = null;        // Encoded Data For GET or POST
1390             req.method = null;             // GET or POST
1391             req.status = null;             // Response Status Code From Server
1392             req.fromQueue = false;         // Indicates if the request was taken off the queue
1393             // before being sent.  This prevents the request from
1394             // entering the queue redundantly.
1395 
1396             req.que = Queue;
1397 
1398             // Get an XMLHttpRequest Handle
1399             req.xmlReq = getTransport();
1400             if (req.xmlReq === null) {
1401                 return null;
1402             }
1403 
1404             function noop() {}
1405             
1406             // Set up request/response state callbacks
1407             /**
1408              * @ignore
1409              */
1410             req.xmlReq.onreadystatechange = function() {
1411                 if (req.xmlReq.readyState === 4) {
1412                     req.onComplete();
1413                     // next two lines prevent closure/ciruclar reference leaks
1414                     // of XHR instances in IE
1415                     req.xmlReq.onreadystatechange = noop;
1416                     req.xmlReq = null;
1417                 }
1418             };
1419 
1420             /**
1421              * This function is called when the request/response interaction
1422              * is complete.  If the return status code is successfull,
1423              * dequeue all requests from the queue that have completed.  If a
1424              * request has been found on the queue that has not been sent,
1425              * send the request.
1426              * @ignore
1427              */
1428             req.onComplete = function onComplete() {
1429                 if (req.xmlReq.status && (req.xmlReq.status >= 200 && req.xmlReq.status < 300)) {
1430                     sendEvent(req.xmlReq, req.context, "complete");
1431                     jsf.ajax.response(req.xmlReq, req.context);
1432                 } else {
1433                     sendEvent(req.xmlReq, req.context, "complete");
1434                     sendError(req.xmlReq, req.context, "httpError");
1435                 }
1436 
1437                 // Regardless of whether the request completed successfully (or not),
1438                 // dequeue requests that have been completed (readyState 4) and send
1439                 // requests that ready to be sent (readyState 0).
1440 
1441                 var nextReq = req.que.getOldestElement();
1442                 if (nextReq === null || typeof nextReq === 'undefined') {
1443                     return;
1444                 }
1445                 while ((typeof nextReq.xmlReq !== 'undefined' && nextReq.xmlReq !== null) &&
1446                        nextReq.xmlReq.readyState === 4) {
1447                     req.que.dequeue();
1448                     nextReq = req.que.getOldestElement();
1449                     if (nextReq === null || typeof nextReq === 'undefined') {
1450                         break;
1451                     }
1452                 }
1453                 if (nextReq === null || typeof nextReq === 'undefined') {
1454                     return;
1455                 }
1456                 if ((typeof nextReq.xmlReq !== 'undefined' && nextReq.xmlReq !== null) &&
1457                     nextReq.xmlReq.readyState === 0) {
1458                     nextReq.fromQueue = true;
1459                     nextReq.sendRequest();
1460                 }
1461             };
1462 
1463             /**
1464              * Utility method that accepts additional arguments for the AjaxEngine.
1465              * If an argument is passed in that matches an AjaxEngine property, the
1466              * argument value becomes the value of the AjaxEngine property.
1467              * Arguments that don't match AjaxEngine properties are added as
1468              * request parameters.
1469              * @ignore
1470              */
1471             req.setupArguments = function(args) {
1472                 for (var i in args) {
1473                     if (args.hasOwnProperty(i)) {
1474                         if (typeof req[i] === 'undefined') {
1475                             req.parameters[i] = args[i];
1476                         } else {
1477                             req[i] = args[i];
1478                         }
1479                     }
1480                 }
1481             };
1482 
1483             /**
1484              * This function does final encoding of parameters, determines the request method
1485              * (GET or POST) and sends the request using the specified url.
1486              * @ignore
1487              */
1488             req.sendRequest = function() {
1489                 if (req.xmlReq !== null) {
1490                     // if there is already a request on the queue waiting to be processed..
1491                     // just queue this request
1492                     if (!req.que.isEmpty()) {
1493                         if (!req.fromQueue) {
1494                             req.que.enqueue(req);
1495                             return;
1496                         }
1497                     }
1498                     // If the queue is empty, queue up this request and send
1499                     if (!req.fromQueue) {
1500                         req.que.enqueue(req);
1501                     }
1502                     // Some logic to get the real request URL
1503                     if (req.generateUniqueUrl && req.method == "GET") {
1504                         req.parameters["AjaxRequestUniqueId"] = new Date().getTime() + "" + req.requestIndex;
1505                     }
1506                     var content = null; // For POST requests, to hold query string
1507                     for (var i in req.parameters) {
1508                         if (req.parameters.hasOwnProperty(i)) {
1509                             if (req.queryString.length > 0) {
1510                                 req.queryString += "&";
1511                             }
1512                             req.queryString += encodeURIComponent(i) + "=" + encodeURIComponent(req.parameters[i]);
1513                         }
1514                     }
1515                     if (req.method === "GET") {
1516                         if (req.queryString.length > 0) {
1517                             req.url += ((req.url.indexOf("?") > -1) ? "&" : "?") + req.queryString;
1518                         }
1519                     }
1520                     req.xmlReq.open(req.method, req.url, req.async);
1521                     // note that we are including the charset=UTF-8 as part of the content type (even
1522                     // if encodeURIComponent encodes as UTF-8), because with some
1523                     // browsers it will not be set in the request.  Some server implementations need to 
1524                     // determine the character encoding from the request header content type.
1525                     if (req.method === "POST") {
1526                         if (typeof req.xmlReq.setRequestHeader !== 'undefined') {
1527                             req.xmlReq.setRequestHeader('Faces-Request', 'partial/ajax');
1528                             req.xmlReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
1529                         }
1530                         content = req.queryString;
1531                     }
1532                     // note that async == false is not a supported feature.  We may change it in ways
1533                     // that break existing programs at any time, with no warning.
1534                     if(!req.async) {
1535                         req.xmlReq.onreadystatechange = null; // no need for readystate change listening
1536                     }
1537                     sendEvent(req.xmlReq, req.context, "begin");
1538                     req.xmlReq.send(content);
1539                     if(!req.async){
1540                         req.onComplete();
1541                 }
1542                 }
1543             };
1544 
1545             return req;
1546         };
1547 
1548         /**
1549          * Error handling callback.
1550          * Assumes that the request has completed.
1551          * @ignore
1552          */
1553         var sendError = function sendError(request, context, status, description, serverErrorName, serverErrorMessage) {
1554 
1555             // Possible errornames:
1556             // httpError
1557             // emptyResponse
1558             // serverError
1559             // malformedXML
1560 
1561             var sent = false;
1562             var data = {};  // data payload for function
1563             data.type = "error";
1564             data.status = status;
1565             data.source = context.sourceid;
1566             data.responseCode = request.status;
1567             data.responseXML = request.responseXML;
1568             data.responseText = request.responseText;
1569 
1570             // ensure data source is the dom element and not the ID
1571             // per 14.4.1 of the 2.0 specification.
1572             if (typeof data.source === 'string') {
1573                 data.source = document.getElementById(data.source);
1574             }
1575 
1576             if (description) {
1577                 data.description = description;
1578             } else if (status == "httpError") {
1579                 if (data.responseCode === 0) {
1580                     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.";
1581                 } else {
1582                     data.description = "There was an error communicating with the server, status: " + data.responseCode;
1583                 }
1584             } else if (status == "serverError") {
1585                 data.description = serverErrorMessage;
1586             } else if (status == "emptyResponse") {
1587                 data.description = "An empty response was received from the server.  Check server error logs.";
1588             } else if (status == "malformedXML") {
1589                 if (getParseErrorText(data.responseXML) !== PARSED_OK) {
1590                     data.description = getParseErrorText(data.responseXML);
1591                 } else {
1592                     data.description = "An invalid XML response was received from the server.";
1593                 }
1594             }
1595 
1596             if (status == "serverError") {
1597                 data.errorName = serverErrorName;
1598                 data.errorMessage = serverErrorMessage;
1599             }
1600 
1601             // If we have a registered callback, send the error to it.
1602             if (context.onerror) {
1603                 context.onerror.call(null, data);
1604                 sent = true;
1605             }
1606 
1607             for (var i in errorListeners) {
1608                 if (errorListeners.hasOwnProperty(i)) {
1609                     errorListeners[i].call(null, data);
1610                     sent = true;
1611                 }
1612             }
1613 
1614             if (!sent && jsf.getProjectStage() === "Development") {
1615                 if (status == "serverError") {
1616                     alert("serverError: " + serverErrorName + " " + serverErrorMessage);
1617                 } else {
1618                     alert(status + ": " + data.description);
1619                 }
1620             }
1621         };
1622 
1623         /**
1624          * Event handling callback.
1625          * Request is assumed to have completed, except in the case of event = 'begin'.
1626          * @ignore
1627          */
1628         var sendEvent = function sendEvent(request, context, status) {
1629 
1630             var data = {};
1631             data.type = "event";
1632             data.status = status;
1633             data.source = context.sourceid;
1634             // ensure data source is the dom element and not the ID
1635             // per 14.4.1 of the 2.0 specification.
1636             if (typeof data.source === 'string') {
1637                 data.source = document.getElementById(data.source);
1638             }
1639             if (status !== 'begin') {
1640                 data.responseCode = request.status;
1641                 data.responseXML = request.responseXML;
1642                 data.responseText = request.responseText;
1643             }
1644 
1645             if (context.onevent) {
1646                 context.onevent.call(null, data);
1647             }
1648 
1649             for (var i in eventListeners) {
1650                 if (eventListeners.hasOwnProperty(i)) {
1651                     eventListeners[i].call(null, data);
1652                 }
1653             }
1654         };
1655 
1656         // Use module pattern to return the functions we actually expose
1657         return {
1658             /**
1659              * Register a callback for error handling.
1660              * <p><b>Usage:</b></p>
1661              * <pre><code>
1662              * jsf.ajax.addOnError(handleError);
1663              * ...
1664              * var handleError = function handleError(data) {
1665              * ...
1666              * }
1667              * </pre></code>
1668              * <p><b>Implementation Requirements:</b></p>
1669              * This function must accept a reference to an existing JavaScript function.
1670              * The JavaScript function reference must be added to a list of callbacks, making it possible
1671              * to register more than one callback by invoking <code>jsf.ajax.addOnError</code>
1672              * more than once.  This function must throw an error if the <code>callback</code>
1673              * argument is not a function.
1674              *
1675              * @member jsf.ajax
1676              * @param callback a reference to a function to call on an error
1677              */
1678             addOnError: function addOnError(callback) {
1679                 if (typeof callback === 'function') {
1680                     errorListeners[errorListeners.length] = callback;
1681                 } else {
1682                     throw new Error("jsf.ajax.addOnError:  Added a callback that was not a function.");
1683                 }
1684             },
1685             /**
1686              * Register a callback for event handling.
1687              * <p><b>Usage:</b></p>
1688              * <pre><code>
1689              * jsf.ajax.addOnEvent(statusUpdate);
1690              * ...
1691              * var statusUpdate = function statusUpdate(data) {
1692              * ...
1693              * }
1694              * </pre></code>
1695              * <p><b>Implementation Requirements:</b></p>
1696              * This function must accept a reference to an existing JavaScript function.
1697              * The JavaScript function reference must be added to a list of callbacks, making it possible
1698              * to register more than one callback by invoking <code>jsf.ajax.addOnEvent</code>
1699              * more than once.  This function must throw an error if the <code>callback</code>
1700              * argument is not a function.
1701              *
1702              * @member jsf.ajax
1703              * @param callback a reference to a function to call on an event
1704              */
1705             addOnEvent: function addOnEvent(callback) {
1706                 if (typeof callback === 'function') {
1707                     eventListeners[eventListeners.length] = callback;
1708                 } else {
1709                     throw new Error("jsf.ajax.addOnEvent: Added a callback that was not a function");
1710                 }
1711             },
1712             /**
1713 
1714              * <p><span class="changed_modified_2_2">Send</span> an
1715              * asynchronous Ajax req uest to the server.
1716 
1717              * <p><b>Usage:</b></p>
1718              * <pre><code>
1719              * Example showing all optional arguments:
1720              *
1721              * <commandButton id="button1" value="submit"
1722              *     onclick="jsf.ajax.request(this,event,
1723              *       {execute:'button1',render:'status',onevent: handleEvent,onerror: handleError});return false;"/>
1724              * </commandButton/>
1725              * </pre></code>
1726              * <p><b>Implementation Requirements:</b></p>
1727              * This function must:
1728              * <ul>
1729              * <li>Be used within the context of a <code>form</code>.</li>
1730              * <li>Capture the element that triggered this Ajax request
1731              * (from the <code>source</code> argument, also known as the
1732              * <code>source</code> element.</li>
1733              * <li>If the <code>source</code> element is <code>null</code> or
1734              * <code>undefined</code> throw an error.</li>
1735              * <li>If the <code>source</code> argument is not a <code>string</code> or
1736              * DOM element object, throw an error.</li>
1737              * <li>If the <code>source</code> argument is a <code>string</code>, find the
1738              * DOM element for that <code>string</code> identifier.
1739              * <li>If the DOM element could not be determined, throw an error.</li>
1740              * <li>If the <code>onerror</code> and <code>onevent</code> arguments are set,
1741              * they must be functions, or throw an error.
1742              * <li>Determine the <code>source</code> element's <code>form</code>
1743              * element.</li>
1744              * <li>Get the <code>form</code> view state by calling
1745              * {@link jsf.getViewState} passing the
1746              * <code>form</code> element as the argument.</li>
1747              * <li>Collect post data arguments for the Ajax request.
1748              * <ul>
1749              * <li>The following name/value pairs are required post data arguments:
1750              * <table border="1">
1751              * <tr>
1752              * <th>name</th>
1753              * <th>value</th>
1754              * </tr>
1755              * <tr>
1756              * <td><code>javax.faces.ViewState</code></td>
1757              * <td><code>Contents of javax.faces.ViewState hidden field.  This is included when
1758              * {@link jsf.getViewState} is used.</code></td>
1759              * </tr>
1760              * <tr>
1761              * <td><code>javax.faces.partial.ajax</code></td>
1762              * <td><code>true</code></td>
1763              * </tr>
1764              * <tr>
1765              * <td><code>javax.faces.source</code></td>
1766              * <td><code>The identifier of the element that triggered this request.</code></td>
1767              * </tr>
1768              * <tr class="changed_added_2_2">
1769              * <td><code>javax.faces.ClientWindow</code></td>
1770 
1771              * <td><code>Call jsf.getClientWindow(), passing the current
1772              * form.  If the return is non-null, it must be set as the
1773              * value of this name/value pair, otherwise, a name/value
1774              * pair for client window must not be sent.</code></td>
1775 
1776              * </tr>
1777              * </table>
1778              * </li>
1779              * </ul>
1780              * </li>
1781              * <li>Collect optional post data arguments for the Ajax request.
1782              * <ul>
1783              * <li>Determine additional arguments (if any) from the <code>options</code>
1784              * argument. If <code>options.execute</code> exists:
1785              * <ul>
1786              * <li>If the keyword <code>@none</code> is present, do not create and send
1787              * the post data argument <code>javax.faces.partial.execute</code>.</li>
1788              * <li>If the keyword <code>@all</code> is present, create the post data argument with
1789              * the name <code>javax.faces.partial.execute</code> and the value <code>@all</code>.</li>
1790              * <li>Otherwise, there are specific identifiers that need to be sent.  Create the post
1791              * data argument with the name <code>javax.faces.partial.execute</code> and the value as a
1792              * space delimited <code>string</code> of client identifiers.</li>
1793              * </ul>
1794              * </li>
1795              * <li>If <code>options.execute</code> does not exist, create the post data argument with the
1796              * name <code>javax.faces.partial.execute</code> and the value as the identifier of the
1797              * element that caused this request.</li>
1798              * <li>If <code>options.render</code> exists:
1799              * <ul>
1800              * <li>If the keyword <code>@none</code> is present, do not create and send
1801              * the post data argument <code>javax.faces.partial.render</code>.</li>
1802              * <li>If the keyword <code>@all</code> is present, create the post data argument with
1803              * the name <code>javax.faces.partial.render</code> and the value <code>@all</code>.</li>
1804              * <li>Otherwise, there are specific identifiers that need to be sent.  Create the post
1805              * data argument with the name <code>javax.faces.partial.render</code> and the value as a
1806              * space delimited <code>string</code> of client identifiers.</li>
1807              * </ul>
1808              * <li>If <code>options.render</code> does not exist do not create and send the
1809              * post data argument <code>javax.faces.partial.render</code>.</li>
1810 
1811              * <li class="changed_added_2_2">If
1812              * <code>options.delay</code> exists let it be the value
1813              * <em>delay</em>, for this discussion.  If
1814              * <code>options.delay</code> does not exist, or is the
1815              * literal string <code>'none'</code>, without the quotes,
1816              * no delay is used.  If less than <em>delay</em>
1817              * milliseconds elapses between calls to <em>request()</em>
1818              * only the most recent one is sent and all other requests
1819              * are discarded.</li>
1820 
1821 
1822              * <li class="changed_added_2_2">If
1823              * <code>options.resetValues</code> exists and its value is
1824              * <code>true</code>, ensure a post data argument with the
1825              * name <code>javax.faces.partial.resetValues</code> and the
1826              * value <code>true</code> is sent in addition to the other
1827              * post data arguments.  This will cause
1828              * <code>UIViewRoot.resetValues()</code> to be called,
1829              * passing the value of the "render" attribute.  Note: do
1830              * not use any of the <code>@</code> keywords such as
1831              * <code>@form</code> or <code>@this</code> with this option
1832              * because <code>UIViewRoot.resetValues()</code> does not
1833              * descend into the children of the listed components.</li>
1834 
1835 
1836              * <li>Determine additional arguments (if any) from the <code>event</code>
1837              * argument.  The following name/value pairs may be used from the
1838              * <code>event</code> object:
1839              * <ul>
1840              * <li><code>target</code> - the ID of the element that triggered the event.</li>
1841              * <li><code>captured</code> - the ID of the element that captured the event.</li>
1842              * <li><code>type</code> - the type of event (ex: onkeypress)</li>
1843              * <li><code>alt</code> - <code>true</code> if ALT key was pressed.</li>
1844              * <li><code>ctrl</code> - <code>true</code> if CTRL key was pressed.</li>
1845              * <li><code>shift</code> - <code>true</code> if SHIFT key was pressed. </li>
1846              * <li><code>meta</code> - <code>true</code> if META key was pressed. </li>
1847              * <li><code>right</code> - <code>true</code> if right mouse button
1848              * was pressed. </li>
1849              * <li><code>left</code> - <code>true</code> if left mouse button
1850              * was pressed. </li>
1851              * <li><code>keycode</code> - the key code.
1852              * </ul>
1853              * </li>
1854              * </ul>
1855              * </li>
1856              * <li>Encode the set of post data arguments.</li>
1857              * <li>Join the encoded view state with the encoded set of post data arguments
1858              * to form the <code>query string</code> that will be sent to the server.</li>
1859              * <li>Create a request <code>context</code> object and set the properties:
1860              * <ul><li><code>source</code> (the source DOM element for this request)</li>
1861              * <li><code>onerror</code> (the error handler for this request)</li>
1862              * <li><code>onevent</code> (the event handler for this request)</li></ul>
1863              * The request context will be used during error/event handling.</li>
1864              * <li>Send a <code>begin</code> event following the procedure as outlined
1865              * in the Chapter 13 "Sending Events" section of the spec prose document <a
1866              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
1867              *  overview summary</a></li>
1868              * <li>Set the request header with the name: <code>Faces-Request</code> and the
1869              * value: <code>partial/ajax</code>.</li>
1870              * <li>Determine the <code>posting URL</code> as follows: If the hidden field
1871              * <code>javax.faces.encodedURL</code> is present in the submitting form, use its
1872              * value as the <code>posting URL</code>.  Otherwise, use the <code>action</code>
1873              * property of the <code>form</code> element as the <code>URL</code>.</li>
1874 
1875              * <li> 
1876 
1877              * <p><span class="changed_modified_2_2">Determine whether
1878              * or not any of the components in
1879              * <code>options.execute</code> (or its effective
1880              * equivalent) is a file upload component.  If not, send the
1881              * request as an <code>asynchronous POST</code> using the
1882              * <code>posting URL</code> that was determined in the
1883              * previous step.</span><p>
1884 
1885              * <div class="changed_added_2_2">
1886 
1887              * <p>Otherwise, verify that the <code>enctype</code>
1888              * property of the effective form is
1889              * <code>multipart/form-data</code> and throw an error if it
1890              * is not.  Send the request using a multi-part capable
1891              * transport layer, such as a hidden inline frame.  Note
1892              * that using a hidden inline frame does
1893              * <strong>not</strong> use <code>XMLHttpRequest</code>, but
1894              * the request must be sent with all the parameters that a
1895              * JSF <code>XMLHttpRequest</code> would have been sent
1896              * with.  In this way, the server side processing of the
1897              * request will be identical whether or the request is
1898              * multipart or not.</p>
1899 
1900              * <p>The <code>begin</code>, <code>complete</code>, and
1901              * <code>success</code> events must be emulated when using
1902              * the multipart transport.  This allows any listeners to
1903              * behave uniformly regardless of the multipart or
1904              * <code>XMLHttpRequest</code> nature of the transport.
1905 
1906              * </div>
1907 
1908 </li>
1909              * </ul>
1910              * Form serialization should occur just before the request is sent to minimize 
1911              * the amount of time between the creation of the serialized form data and the 
1912              * sending of the serialized form data (in the case of long requests in the queue).
1913              * Before the request is sent it must be put into a queue to ensure requests
1914              * are sent in the same order as when they were initiated.  The request callback function
1915              * must examine the queue and determine the next request to be sent.  The behavior of the
1916              * request callback function must be as follows:
1917              * <ul>
1918              * <li>If the request completed successfully invoke {@link jsf.ajax.response}
1919              * passing the <code>request</code> object.</li>
1920              * <li>If the request did not complete successfully, notify the client.</li>
1921              * <li>Regardless of the outcome of the request (success or error) every request in the
1922              * queue must be handled.  Examine the status of each request in the queue starting from
1923              * the request that has been in the queue the longest.  If the status of the request is
1924              * <code>complete</code> (readyState 4), dequeue the request (remove it from the queue).
1925              * If the request has not been sent (readyState 0), send the request.  Requests that are
1926              * taken off the queue and sent should not be put back on the queue.</li>
1927              * </ul>
1928              *
1929              * </p>
1930              *
1931              * @param source The DOM element that triggered this Ajax request, or an id string of the
1932              * element to use as the triggering element.
1933              * @param event The DOM event that triggered this Ajax request.  The
1934              * <code>event</code> argument is optional.
1935              * @param options The set of available options that can be sent as
1936              * request parameters to control client and/or server side
1937              * request processing. Acceptable name/value pair options are:
1938              * <table border="1">
1939              * <tr>
1940              * <th>name</th>
1941              * <th>value</th>
1942              * </tr>
1943              * <tr>
1944              * <td><code>execute</code></td>
1945              * <td><code>space seperated list of client identifiers</code></td>
1946              * </tr>
1947              * <tr>
1948              * <td><code>render</code></td>
1949              * <td><code>space seperated list of client identifiers</code></td>
1950              * </tr>
1951              * <tr>
1952              * <td><code>onevent</code></td>
1953              * <td><code>function to callback for event</code></td>
1954              * </tr>
1955              * <tr>
1956              * <td><code>onerror</code></td>
1957              * <td><code>function to callback for error</code></td>
1958              * </tr>
1959              * <tr>
1960              * <td><code>params</code></td>
1961              * <td><code>object containing parameters to include in the request</code></td>
1962              * </tr>
1963 
1964              * <tr class="changed_added_2_2">
1965 
1966              * <td><code>delay</code></td>
1967 
1968              * <td>If less than <em>delay</em> milliseconds elapses
1969              * between calls to <em>request()</em> only the most recent
1970              * one is sent and all other requests are discarded. If the
1971              * value of <em>delay</em> is the literal string
1972              * <code>'none'</code> without the quotes, or no delay is
1973              * specified, no delay is used. </td>
1974 
1975              * </tr>
1976 
1977              * <tr class="changed_added_2_2">
1978 
1979              * <td><code>resetValues</code></td>
1980 
1981              * <td>If true, ensure a post data argument with the name
1982              * javax.faces.partial.resetValues and the value true is
1983              * sent in addition to the other post data arguments. This
1984              * will cause UIViewRoot.resetValues() to be called, passing
1985              * the value of the "render" attribute. Note: do not use any
1986              * of the @ keywords such as @form or @this with this option
1987              * because UIViewRoot.resetValues() does not descend into
1988              * the children of the listed components.</td>
1989 
1990              * </tr>
1991 
1992 
1993              * </table>
1994              * The <code>options</code> argument is optional.
1995              * @member jsf.ajax
1996              * @function jsf.ajax.request
1997 
1998              * @throws Error if first required argument
1999              * <code>element</code> is not specified, or if one or more
2000              * of the components in the <code>options.execute</code>
2001              * list is a file upload component, but the form's enctype
2002              * is not set to <code>multipart/form-data</code>
2003              */
2004 
2005             request: function request(source, event, options) {
2006 
2007                 var element, form;   //  Element variables
2008                 var all, none;
2009 
2010                 if (typeof source === 'undefined' || source === null) {
2011                     throw new Error("jsf.ajax.request: source not set");
2012                 }
2013                 if(delayHandler) {
2014                     clearTimeout(delayHandler);
2015                     delayHandler = null;
2016                 }
2017 
2018                 // set up the element based on source
2019                 if (typeof source === 'string') {
2020                     element = document.getElementById(source);
2021                 } else if (typeof source === 'object') {
2022                     element = source;
2023                 } else {
2024                     throw new Error("jsf.request: source must be object or string");
2025                 }
2026                 // attempt to handle case of name unset
2027                 // this might be true in a badly written composite component
2028                 if (!element.name) {
2029                     element.name = element.id;
2030                 }
2031 
2032                 if (typeof(options) === 'undefined' || options === null) {
2033                     options = {};
2034                 }
2035 
2036                 // Error handler for this request
2037                 var onerror = false;
2038 
2039                 if (options.onerror && typeof options.onerror === 'function') {
2040                     onerror = options.onerror;
2041                 } else if (options.onerror && typeof options.onerror !== 'function') {
2042                     throw new Error("jsf.ajax.request: Added an onerror callback that was not a function");
2043                 }
2044 
2045                 // Event handler for this request
2046                 var onevent = false;
2047 
2048                 if (options.onevent && typeof options.onevent === 'function') {
2049                     onevent = options.onevent;
2050                 } else if (options.onevent && typeof options.onevent !== 'function') {
2051                     throw new Error("jsf.ajax.request: Added an onevent callback that was not a function");
2052                 }
2053 
2054                 form = getForm(element);
2055                 if (!form) {
2056                     throw new Error("jsf.ajax.request: Method must be called within a form");
2057                 }
2058                 var viewState = jsf.getViewState(form);
2059 
2060                 // Set up additional arguments to be used in the request..
2061                 // Make sure "javax.faces.source" is set up.
2062                 // If there were "execute" ids specified, make sure we
2063                 // include the identifier of the source element in the
2064                 // "execute" list.  If there were no "execute" ids
2065                 // specified, determine the default.
2066 
2067                 var args = {};
2068 
2069                 args["javax.faces.source"] = element.id;
2070 
2071                 if (event && !!event.type) {
2072                     args["javax.faces.partial.event"] = event.type;
2073                 }
2074 
2075                 if ("resetValues" in options) {
2076                     args["javax.faces.partial.resetValues"] = options.resetValues;
2077                 }
2078 
2079                 // If we have 'execute' identifiers:
2080                 // Handle any keywords that may be present.
2081                 // If @none present anywhere, do not send the
2082                 // "javax.faces.partial.execute" parameter.
2083                 // The 'execute' and 'render' lists must be space
2084                 // delimited.
2085 
2086                 if (options.execute) {
2087                     none = options.execute.search(/@none/);
2088                     if (none < 0) {
2089                         all = options.execute.search(/@all/);
2090                         if (all < 0) {
2091                             options.execute = options.execute.replace("@this", element.id);
2092                             options.execute = options.execute.replace("@form", form.id);
2093                             var temp = options.execute.split(' ');
2094                             if (!isInArray(temp, element.name)) {
2095                                 options.execute = element.name + " " + options.execute;
2096                             }
2097                         } else {
2098                             options.execute = "@all";
2099                         }
2100                         args["javax.faces.partial.execute"] = options.execute;
2101                     }
2102                 } else {
2103                     options.execute = element.name + " " + element.id;
2104                     args["javax.faces.partial.execute"] = options.execute;
2105                 }
2106 
2107                 if (options.render) {
2108                     none = options.render.search(/@none/);
2109                     if (none < 0) {
2110                         all = options.render.search(/@all/);
2111                         if (all < 0) {
2112                             options.render = options.render.replace("@this", element.id);
2113                             options.render = options.render.replace("@form", form.id);
2114                         } else {
2115                             options.render = "@all";
2116                         }
2117                         args["javax.faces.partial.render"] = options.render;
2118                     }
2119                 }
2120                 var explicitlyDoNotDelay = ((typeof options.delay == 'undefined') || (typeof options.delay == 'string') &&
2121                                             (options.delay.toLowerCase() == 'none'));
2122                 var delayValue;
2123                 if (typeof options.delay == 'number') {
2124                     delayValue = options.delay;
2125                 } else if (!explicitlyDoNotDelay) {
2126                     throw new Error('invalid value for delay option: ' + options.delay);
2127                 }
2128 
2129                 // remove non-passthrough options
2130                 delete options.execute;
2131                 delete options.render;
2132                 delete options.onerror;
2133                 delete options.onevent;
2134                 delete options.delay;
2135 
2136                 // copy all other options to args
2137                 for (var property in options) {
2138                     if (options.hasOwnProperty(property)) {
2139                         args[property] = options[property];
2140                     }
2141                 }
2142 
2143                 args["javax.faces.partial.ajax"] = "true";
2144                 args["method"] = "POST";
2145 
2146                 // Determine the posting url
2147 
2148                 var encodedUrlField = form.elements["javax.faces.encodedURL"];
2149                 if (typeof encodedUrlField == 'undefined') {
2150                     args["url"] = form.action;
2151                 } else {
2152                     args["url"] = encodedUrlField.value;
2153                 }
2154                 var sendRequest = function() {
2155                     var ajaxEngine = new AjaxEngine();
2156                     ajaxEngine.setupArguments(args);
2157                     ajaxEngine.queryString = viewState;
2158                     ajaxEngine.context.onevent = onevent;
2159                     ajaxEngine.context.onerror = onerror;
2160                     ajaxEngine.context.sourceid = element.id;
2161                     ajaxEngine.context.formid = form.id;
2162                     ajaxEngine.context.render = args["javax.faces.partial.render"];
2163                     ajaxEngine.sendRequest();
2164 
2165                     // null out element variables to protect against IE memory leak
2166                     element = null;
2167                     form = null;
2168                     sendRequest = null;
2169                 };
2170 
2171                 if (explicitlyDoNotDelay) {
2172                     sendRequest();
2173                 } else {
2174                     delayHandler = setTimeout(sendRequest, delayValue);
2175                 }
2176 
2177             },
2178             /**
2179              * <p><span class="changed_modified_2_2">Receive</span> an Ajax response 
2180              * from the server.
2181              * <p><b>Usage:</b></p>
2182              * <pre><code>
2183              * jsf.ajax.response(request, context);
2184              * </pre></code>
2185              * <p><b>Implementation Requirements:</b></p>
2186              * This function must evaluate the markup returned in the
2187              * <code>request.responseXML</code> object and perform the following action:
2188              * <ul>
2189              * <p>If there is no XML response returned, signal an <code>emptyResponse</code>
2190              * error. If the XML response does not follow the format as outlined
2191              * in Appendix A of the spec prose document <a
2192              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
2193              *  overview summary</a> signal a <code>malformedError</code> error.  Refer to
2194              * section "Signaling Errors" in Chapter 13 of the spec prose document <a
2195              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
2196              *  overview summary</a>.</p>
2197              * <p>If the response was successfully processed, send a <code>success</code>
2198              * event as outlined in Chapter 13 "Sending Events" section of the spec prose
2199              * document <a
2200              * href="../../javadocs/overview-summary.html#prose_document">linked in the
2201              * overview summary</a>.</p>
2202              * <p><i>Update Element Processing</i></p>
2203              * The <code>update</code> element is used to update a single DOM element.  The
2204              * "id" attribute of the <code>update</code> element refers to the DOM element that
2205              * will be updated.  The contents of the <code>CDATA</code> section is the data that 
2206              * will be used when updating the contents of the DOM element as specified by the
2207              * <code><update></code> element identifier.
2208              * <li>If an <code>update</code> element is found in the response
2209              * with the identifier <code>javax.faces.ViewRoot</code>:
2210              * <pre><code><update id="javax.faces.ViewRoot">
2211              *    <![CDATA[...]]>
2212              * </update></code></pre>
2213              * Update the entire DOM replacing the appropriate <code>head</code> and/or
2214              * <code>body</code> sections with the content from the response.</li>
2215 
2216              * <li class="changed_modified_2_2">If an
2217              * <code>update</code> element is found in the response with
2218              * an identifier containing
2219              * <code>javax.faces.ViewState</code>:
2220 
2221              * <pre><code><update id="<VIEW_ROOT_CONTAINER_CLIENT_ID><SEP>javax.faces.ViewState<SEP><UNIQUE_PER_VIEW_NUMBER>">
2222              *    <![CDATA[...]]>
2223              * </update></code></pre>
2224 
2225              * locate and update the submitting form's
2226              * <code>javax.faces.ViewState</code> value with the
2227              * <code>CDATA</code> contents from the response.
2228              * <SEP>: is the currently configured
2229              * <code>UINamingContainer.getSeparatorChar()</code>.
2230              * <VIEW_ROOT_CONTAINER_CLIENT_ID> is the return from
2231              * <code>UIViewRoot.getContainerClientId()</code> on the
2232              * view from whence this state originated.
2233              * <UNIQUE_PER_VIEW_NUMBER> is a number that must be
2234              * unique within this view, but must not be included in the
2235              * view state.  This requirement is simply to satisfy XML
2236              * correctness in parity with what is done in the
2237              * corresponding non-partial JSF view.  Locate and update
2238              * the <code>javax.faces.ViewState</code> value for all
2239              * forms specified in the <code>render</code> target
2240              * list.</li>
2241 
2242              * <li class="changed_added_2_2">If an
2243              * <code>update</code> element is found in the response with
2244              * an identifier containing
2245              * <code>javax.faces.ClientWindow</code>:
2246 
2247              * <pre><code><update id="<VIEW_ROOT_CONTAINER_CLIENT_ID><SEP>javax.faces.ClientWindow<SEP><UNIQUE_PER_VIEW_NUMBER>">
2248              *    <![CDATA[...]]>
2249              * </update></code></pre>
2250 
2251              * locate and update the submitting form's
2252              * <code>javax.faces.ClientWindow</code> value with the
2253              * <code>CDATA</code> contents from the response.
2254              * <SEP>: is the currently configured
2255              * <code>UINamingContainer.getSeparatorChar()</code>.
2256              * <VIEW_ROOT_CONTAINER_CLIENT_ID> is the return from
2257              * <code>UIViewRoot.getContainerClientId()</code> on the
2258              * view from whence this state originated.             
2259              * <UNIQUE_PER_VIEW_NUMBER> is a number that must be
2260              * unique within this view, but must not be included in the
2261              * view state.  This requirement is simply to satisfy XML
2262              * correctness in parity with what is done in the
2263              * corresponding non-partial JSF view.  Locate and update
2264              * the <code>javax.faces.ClientWindow</code> value for all
2265              * forms specified in the <code>render</code> target
2266              * list.</li>
2267 
2268 
2269              * <li>If an <code>update</code> element is found in the response with the identifier
2270              * <code>javax.faces.ViewHead</code>:
2271              * <pre><code><update id="javax.faces.ViewHead">
2272              *    <![CDATA[...]]>
2273              * </update></code></pre>
2274              * update the document's <code>head</code> section with the <code>CDATA</code>
2275              * contents from the response.</li>
2276              * <li>If an <code>update</code> element is found in the response with the identifier
2277              * <code>javax.faces.ViewBody</code>:
2278              * <pre><code><update id="javax.faces.ViewBody">
2279              *    <![CDATA[...]]>
2280              * </update></code></pre>
2281              * update the document's <code>body</code> section with the <code>CDATA</code>
2282              * contents from the response.</li>
2283              * <li>For any other <code><update></code> element:
2284              * <pre><code><update id="update id">
2285              *    <![CDATA[...]]>
2286              * </update></code></pre>
2287              * Find the DOM element with the identifier that matches the
2288              * <code><update></code> element identifier, and replace its contents with
2289              * the <code><update></code> element's <code>CDATA</code> contents.</li>
2290              * </li>
2291              * <p><i>Insert Element Processing</i></p>
2292              * <li>If an <code><insert></code> element is found in the response with the
2293              * attribute <code>before</code>:
2294              * <pre><code><insert id="insert id" before="before id">
2295              *    <![CDATA[...]]>
2296              * </insert></code></pre>
2297              * <ul>
2298              * <li>Extract this <code><insert></code> element's <code>CDATA</code> contents
2299              * from the response.</li>
2300              * <li>Find the DOM element whose identifier matches <code>before id</code> and insert
2301              * the <code><insert></code> element's <code>CDATA</code> content before
2302              * the DOM element in the document.</li>
2303              * </ul>
2304              * </li>
2305              * <li>If an <code><insert></code> element is found in the response with the
2306              * attribute <code>after</code>:
2307              * <pre><code><insert id="insert id" after="after id">
2308              *    <![CDATA[...]]>
2309              * </insert></code></pre>
2310              * <ul>
2311              * <li>Extract this <code><insert></code> element's <code>CDATA</code> contents
2312              * from the response.</li>
2313              * <li>Find the DOM element whose identifier matches <code>after id</code> and insert
2314              * the <code><insert></code> element's <code>CDATA</code> content after
2315              * the DOM element in the document.</li>
2316              * </ul>
2317              * </li>
2318              * <p><i>Delete Element Processing</i></p>
2319              * <li>If a <code><delete></code> element is found in the response:
2320              * <pre><code><delete id="delete id"/></code></pre>
2321              * Find the DOM element whose identifier matches <code>delete id</code> and remove it
2322              * from the DOM.</li>
2323              * <p><i>Element Attribute Update Processing</i></p>
2324              * <li>If an <code><attributes></code> element is found in the response:
2325              * <pre><code><attributes id="id of element with attribute">
2326              *    <attribute name="attribute name" value="attribute value">
2327              *    ...
2328              * </attributes></code></pre>
2329              * <ul>
2330              * <li>Find the DOM element that matches the <code><attributes></code> identifier.</li>
2331              * <li>For each nested <code><attribute></code> element in <code><attribute></code>,
2332              * update the DOM element attribute value (whose name matches <code>attribute name</code>),
2333              * with <code>attribute value</code>.</li>
2334              * </ul>
2335              * </li>
2336              * <p><i>JavaScript Processing</i></p>
2337              * <li>If an <code><eval></code> element is found in the response:
2338              * <pre><code><eval>
2339              *    <![CDATA[...JavaScript...]]>
2340              * </eval></code></pre>
2341              * <ul>
2342              * <li>Extract this <code><eval></code> element's <code>CDATA</code> contents
2343              * from the response and execute it as if it were JavaScript code.</li>
2344              * </ul>
2345              * </li>
2346              * <p><i>Redirect Processing</i></p>
2347              * <li>If a <code><redirect></code> element is found in the response:
2348              * <pre><code><redirect url="redirect url"/></code></pre>
2349              * Cause a redirect to the url <code>redirect url</code>.</li>
2350              * <p><i>Error Processing</i></p>
2351              * <li>If an <code><error></code> element is found in the response:
2352              * <pre><code><error>
2353              *    <error-name>..fully qualified class name string...<error-name>
2354              *    <error-message><![CDATA[...]]><error-message>
2355              * </error></code></pre>
2356              * Extract this <code><error></code> element's <code>error-name</code> contents
2357              * and the <code>error-message</code> contents. Signal a <code>serverError</code> passing
2358              * the <code>errorName</code> and <code>errorMessage</code>.  Refer to
2359              * section "Signaling Errors" in Chapter 13 of the spec prose document <a
2360              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
2361              *  overview summary</a>.</li>
2362              * <p><i>Extensions</i></p>
2363              * <li>The <code><extensions></code> element provides a way for framework
2364              * implementations to provide their own information.</li>
2365              * <p><li>The implementation must check if <script> elements in the response can
2366              * be automatically run, as some browsers support this feature and some do not.  
2367              * If they can not be run, then scripts should be extracted from the response and
2368              * run separately.</li></p> 
2369              * </ul>
2370              *
2371              * </p>
2372              *
2373              * @param request The <code>XMLHttpRequest</code> instance that
2374              * contains the status code and response message from the server.
2375              *
2376              * @param context An object containing the request context, including the following properties:
2377              * the source element, per call onerror callback function, and per call onevent callback function.
2378              *
2379              * @throws  Error if request contains no data
2380              *
2381              * @function jsf.ajax.response
2382              */
2383             response: function response(request, context) {
2384                 if (!request) {
2385                     throw new Error("jsf.ajax.response: Request parameter is unset");
2386                 }
2387 
2388                 // ensure context source is the dom element and not the ID
2389                 // per 14.4.1 of the 2.0 specification.  We're doing it here
2390                 // *before* any errors or events are propagated becasue the
2391                 // DOM element may be removed after the update has been processed.
2392                 if (typeof context.sourceid === 'string') {
2393                     context.sourceid = document.getElementById(context.sourceid);
2394                 }
2395 
2396                 var xml = request.responseXML;
2397                 if (xml === null) {
2398                     sendError(request, context, "emptyResponse");
2399                     return;
2400                 }
2401 
2402                 if (getParseErrorText(xml) !== PARSED_OK) {
2403                     sendError(request, context, "malformedXML");
2404                     return;
2405                 }
2406 
2407                 var partialResponse = xml.getElementsByTagName("partial-response")[0];
2408                 var partialResponseId = partialResponse.getAttribute("id");
2409                 var responseType = partialResponse.firstChild;
2410 
2411                 if (responseType.nodeName === "error") { // it's an error
2412                     var errorName = responseType.firstChild.firstChild.nodeValue;
2413                     var errorMessage = responseType.firstChild.nextSibling.firstChild.nodeValue;
2414                     sendError(request, context, "serverError", null, errorName, errorMessage);
2415                     sendEvent(request, context, "success");
2416                     return;
2417                 }
2418 
2419 
2420                 if (responseType.nodeName === "redirect") {
2421                     window.location = responseType.getAttribute("url");
2422                     return;
2423                 }
2424 
2425 
2426                 if (responseType.nodeName !== "changes") {
2427                     sendError(request, context, "malformedXML", "Top level node must be one of: changes, redirect, error, received: " + responseType.nodeName + " instead.");
2428                     return;
2429                 }
2430 
2431 
2432                 var changes = responseType.childNodes;
2433 
2434                 try {
2435                     for (var i = 0; i < changes.length; i++) {
2436                         switch (changes[i].nodeName) {
2437                             case "update":
2438                                 doUpdate(changes[i], context, partialResponseId);
2439                                 break;
2440                             case "delete":
2441                                 doDelete(changes[i]);
2442                                 break;
2443                             case "insert":
2444                                 doInsert(changes[i]);
2445                                 break;
2446                             case "attributes":
2447                                 doAttributes(changes[i]);
2448                                 break;
2449                             case "eval":
2450                                 doEval(changes[i]);
2451                                 break;
2452                             case "extension":
2453                                 // no action
2454                                 break;
2455                             default:
2456                                 sendError(request, context, "malformedXML", "Changes allowed are: update, delete, insert, attributes, eval, extension.  Received " + changes[i].nodeName + " instead.");
2457                                 return;
2458                         }
2459                     }
2460                 } catch (ex) {
2461                     sendError(request, context, "malformedXML", ex.message);
2462                     return;
2463                 }
2464                 sendEvent(request, context, "success");
2465 
2466             }
2467         };
2468     }();
2469 
2470     /**
2471      *
2472      * <p>Return the value of <code>Application.getProjectStage()</code> for
2473      * the currently running application instance.  Calling this method must
2474      * not cause any network transaction to happen to the server.</p>
2475      * <p><b>Usage:</b></p>
2476      * <pre><code>
2477      * var stage = jsf.getProjectStage();
2478      * if (stage === ProjectStage.Development) {
2479      *  ...
2480      * } else if stage === ProjectStage.Production) {
2481      *  ...
2482      * }
2483      * </code></pre>
2484      *
2485      * @returns String <code>String</code> representing the current state of the
2486      * running application in a typical product development lifecycle.  Refer
2487      * to <code>javax.faces.application.Application.getProjectStage</code> and
2488      * <code>javax.faces.application.ProjectStage</code>.
2489      * @function jsf.getProjectStage
2490      */
2491     jsf.getProjectStage = function() {
2492         // First, return cached value if available
2493         if (typeof mojarra !== 'undefined' && typeof mojarra.projectStageCache !== 'undefined') {
2494             return mojarra.projectStageCache;
2495         }
2496         var scripts = document.getElementsByTagName("script"); // nodelist of scripts
2497         var script; // jsf.js script
2498         var s = 0; // incremental variable for for loop
2499         var stage; // temp value for stage
2500         var match; // temp value for match
2501         while (s < scripts.length) {
2502             if (typeof scripts[s].src === 'string' && scripts[s].src.match('\/javax\.faces\.resource\/jsf\.js\?.*ln=javax\.faces')) {
2503                 script = scripts[s].src;
2504                 break;
2505             }
2506             s++;
2507         }
2508         if (typeof script == "string") {
2509             match = script.match("stage=(.*)");
2510             if (match) {
2511                 stage = match[1];
2512             }
2513         }
2514         if (typeof stage === 'undefined' || !stage) {
2515             stage = "Production";
2516         }
2517 
2518         mojarra = mojarra || {};
2519         mojarra.projectStageCache = stage;
2520 
2521         return mojarra.projectStageCache;
2522     };
2523 
2524 
2525     /**
2526      * <p>Collect and encode state for input controls associated
2527      * with the specified <code>form</code> element.  This will include
2528      * all input controls of type <code>hidden</code>.</p>
2529      * <p><b>Usage:</b></p>
2530      * <pre><code>
2531      * var state = jsf.getViewState(form);
2532      * </pre></code>
2533      *
2534      * @param form The <code>form</code> element whose contained
2535      * <code>input</code> controls will be collected and encoded.
2536      * Only successful controls will be collected and encoded in
2537      * accordance with: <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2">
2538      * Section 17.13.2 of the HTML Specification</a>.
2539      *
2540      * @returns String The encoded state for the specified form's input controls.
2541      * @function jsf.getViewState
2542      */
2543     jsf.getViewState = function(form) {
2544         if (!form) {
2545             throw new Error("jsf.getViewState:  form must be set");
2546         }
2547         var els = form.elements;
2548         var len = els.length;
2549         // create an array which we'll use to hold all the intermediate strings
2550         // this bypasses a problem in IE when repeatedly concatenating very
2551         // large strings - we'll perform the concatenation once at the end
2552         var qString = [];
2553         var addField = function(name, value) {
2554             var tmpStr = "";
2555             if (qString.length > 0) {
2556                 tmpStr = "&";
2557             }
2558             tmpStr += encodeURIComponent(name) + "=" + encodeURIComponent(value);
2559             qString.push(tmpStr);
2560         };
2561         for (var i = 0; i < len; i++) {
2562             var el = els[i];
2563             if (el.name === "") {
2564                 continue;
2565             }
2566             if (!el.disabled) {
2567                 switch (el.type) {
2568                     case 'submit':
2569                     case 'reset':
2570                     case 'image':
2571                     case 'file':
2572                         break;
2573                     case 'select-one':
2574                         if (el.selectedIndex >= 0) {
2575                             addField(el.name, el.options[el.selectedIndex].value);
2576                         }
2577                         break;
2578                     case 'select-multiple':
2579                         for (var j = 0; j < el.options.length; j++) {
2580                             if (el.options[j].selected) {
2581                                 addField(el.name, el.options[j].value);
2582                             }
2583                         }
2584                         break;
2585                     case 'checkbox':
2586                     case 'radio':
2587                         if (el.checked) {
2588                             addField(el.name, el.value || 'on');
2589                         }
2590                         break;
2591                     default:
2592                         // this is for any input incl.  text', 'password', 'hidden', 'textarea'
2593                         var nodeName = el.nodeName.toLowerCase();
2594                         if (nodeName === "input" || nodeName === "select" ||
2595                             nodeName === "button" || nodeName === "object" ||
2596                             nodeName === "textarea") { 
2597                             addField(el.name, el.value);
2598                         }
2599                         break;
2600                 }
2601             }
2602         }
2603         // concatenate the array
2604         return qString.join("");
2605     };
2606 
2607     /**
2608      * <p class="changed_added_2_2">Return the windowId of the window
2609      * in which the argument form is rendered.</p>
2610 
2611      * @param {optional String|DomNode} node. Determine the nature of
2612      * the argument.  If not present, search for the windowId within
2613      * <code>document.forms</code>.  If present and the value is a
2614      * string, assume the string is a DOM id and get the element with
2615      * that id and start the search from there.  If present and the
2616      * value is a DOM element, start the search from there.
2617 
2618      * @returns String The windowId of the current window, or null 
2619      *  if the windowId cannot be determined.
2620 
2621      * @throws an error if more than one unique WindowId is found.
2622 
2623      * @function jsf.getViewState
2624      */
2625     jsf.getClientWindow = function(node) {
2626         var FORM = "form";
2627         var WIN_ID = "javax.faces.ClientWindow";
2628 
2629         var fetchWindowIdFromForms = function (forms) {
2630             var result_idx = {};
2631             var result;
2632             var foundCnt = 0;
2633             for (var cnt = forms.length - 1; cnt >= 0; cnt--) {
2634                 var UDEF = 'undefined';
2635                 var currentForm = forms[cnt];
2636                 var windowId = currentForm[WIN_ID] && currentForm[WIN_ID].value;
2637                 if (UDEF != typeof windowId) {
2638                     if (foundCnt > 0 && UDEF == typeof result_idx[windowId]) throw Error("Multiple different windowIds found in document");
2639                     result = windowId;
2640                     result_idx[windowId] = true;
2641                     foundCnt++;
2642                 }
2643             }
2644             return result;
2645         }
2646 
2647         var getChildForms = function (currentElement) {
2648             //Special condition no element we return document forms
2649             //as search parameter, ideal would be to
2650             //have the viewroot here but the frameworks
2651             //can deal with that themselves by using
2652             //the viewroot as currentElement
2653             if (!currentElement) {
2654                 return document.forms;
2655             }
2656             
2657             var targetArr = [];
2658             if (!currentElement.tagName) return [];
2659             else if (currentElement.tagName.toLowerCase() == FORM) {
2660                 targetArr.push(currentElement);
2661                 return targetArr;
2662             }
2663             
2664             //if query selectors are supported we can take
2665             //a non recursive shortcut
2666             if (currentElement.querySelectorAll) {
2667                 return currentElement.querySelectorAll(FORM);
2668             }
2669             
2670             //old recursive way, due to flakeyness of querySelectorAll
2671             for (var cnt = currentElement.childNodes.length - 1; cnt >= 0; cnt--) {
2672                 var currentChild = currentElement.childNodes[cnt];
2673                 targetArr = targetArr.concat(getChildForms(currentChild, FORM));
2674             }
2675             return targetArr;
2676         }
2677         
2678         var fetchWindowIdFromURL = function () {
2679             var href = window.location.href;
2680             var windowId = "windowId";
2681             var regex = new RegExp("[\\?&]" + windowId + "=([^&#\\;]*)");
2682             var results = regex.exec(href);
2683             //initial trial over the url and a regexp
2684             if (results != null) return results[1];
2685             return null;
2686         }
2687         
2688         //byId ($)
2689         var finalNode = (node && (typeof node == "string" || node instanceof String)) ?
2690             document.getElementById(node) : (node || null);
2691         
2692         var forms = getChildForms(finalNode);
2693         var result = fetchWindowIdFromForms(forms);
2694         return (null != result) ? result : fetchWindowIdFromURL();
2695         
2696 
2697     };
2698 
2699 
2700     /**
2701      * The namespace for JavaServer Faces JavaScript utilities.
2702      * @name jsf.util
2703      * @namespace
2704      */
2705     jsf.util = {};
2706 
2707     /**
2708      * <p>A varargs function that invokes an arbitrary number of scripts.
2709      * If any script in the chain returns false, the chain is short-circuited
2710      * and subsequent scripts are not invoked.  Any number of scripts may
2711      * specified after the <code>event</code> argument.</p>
2712      *
2713      * @param source The DOM element that triggered this Ajax request, or an
2714      * id string of the element to use as the triggering element.
2715      * @param event The DOM event that triggered this Ajax request.  The
2716      * <code>event</code> argument is optional.
2717      *
2718      * @returns boolean <code>false</code> if any scripts in the chain return <code>false</code>,
2719      *  otherwise returns <code>true</code>
2720      * 
2721      * @function jsf.util.chain
2722      */
2723     jsf.util.chain = function(source, event) {
2724 
2725         if (arguments.length < 3) {
2726             return true;
2727         }
2728 
2729         // RELEASE_PENDING rogerk - shouldn't this be getElementById instead of null
2730         var thisArg = (typeof source === 'object') ? source : null;
2731 
2732         // Call back any scripts that were passed in
2733         for (var i = 2; i < arguments.length; i++) {
2734 
2735             var f = new Function("event", arguments[i]);
2736             var returnValue = f.call(thisArg, event);
2737 
2738             if (returnValue === false) {
2739                 return false;
2740             }
2741         }
2742         return true;
2743         
2744     };
2745 
2746     /**
2747      * <p class="changed_added_2_2">The result of calling
2748      * <code>UINamingContainer.getNamingContainerSeparatorChar().</code></p>
2749      */
2750     jsf.separatorchar = '#{facesContext.namingContainerSeparatorChar}';
2751 
2752     /**
2753      * <p>An integer specifying the specification version that this file implements.
2754      * It's format is: rightmost two digits, bug release number, next two digits,
2755      * minor release number, leftmost digits, major release number.
2756      * This number may only be incremented by a new release of the specification.</p>
2757      */
2758     jsf.specversion = 22000;
2759 
2760     /**
2761      * <p>An integer specifying the implementation version that this file implements.
2762      * It's a monotonically increasing number, reset with every increment of
2763      * <code>jsf.specversion</code>
2764      * This number is implementation dependent.</p>
2765      */
2766     jsf.implversion = 3;
2767 
2768 
2769 } //end if version detection block
2770