1 /*
  2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3  *
  4  * Copyright 1997-2008 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.0
 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.0", 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 > 2))) {
 73 
 74     /**
 75      * The top level global namespace for JavaServer Faces functionality.
 76      * @name jsf
 77      * @namespace
 78      */
 79     var jsf = {};
 80 
 81     /**
 82      * The namespace for Ajax functionality.
 83      * @name jsf.ajax
 84      * @namespace
 85      * @exec
 86      */
 87     jsf.ajax = function() {
 88 
 89         var eventListeners = [];
 90         var errorListeners = [];
 91 
 92         /**
 93          * Determine if the current browser is part of Microsoft's failed attempt at
 94          * standards modification.
 95          * @ignore
 96          */
 97         var isIE = function isIE() {
 98             if (typeof isIECache !== "undefined") {
 99                 return isIECache;
100             }
101             isIECache =
102                    document.all && window.ActiveXObject &&
103                    navigator.userAgent.toLowerCase().indexOf("msie") > -1 &&
104                    navigator.userAgent.toLowerCase().indexOf("opera") == -1;
105             return isIECache;
106         };
107         var isIECache;
108 
109         /**
110          * Determine if loading scripts into the page executes the script.
111          * This is instead of doing a complicated browser detection algorithm.  Some do, some don't.
112          * @returns {boolean} does including a script in the dom execute it?
113          * @ignore
114          */
115         var isAutoExecCache;
116         var isAutoExec = function isAutoExec() {
117             try {
118                 if (typeof isAutoExecCache !== "undefined") {
119                     return isAutoExecCache;
120                 }
121                 var autoExecTestString = "<script>var mojarra = mojarra || {};mojarra.autoExecTest = true;</script>";
122                 var tempElement = document.createElement('span');
123                 tempElement.innerHTML = autoExecTestString;
124                 var body = document.getElementsByTagName('body')[0];
125                 var tempNode = body.appendChild(tempElement);
126                 if (mojarra && mojarra.autoExecTest) {
127                     isAutoExecCache = true;
128                     delete mojarra.autoExecTest;
129                 } else {
130                     isAutoExecCache = false;
131                 }
132                 deleteNode(tempNode);
133                 return isAutoExecCache;
134             } catch (ex) {
135                 // OK, that didn't work, we'll have to make an assumption
136                 if (typeof isAutoExecCache === "undefined") {
137                     isAutoExecCache = false;
138                 }
139                 return isAutoExecCache;
140             }
141         };
142 
143         /**
144          * @ignore
145          */
146         var getTransport = function getTransport() {
147             var methods = [
148                 function() {
149                     return new XMLHttpRequest();
150                 },
151                 function() {
152                     return new ActiveXObject('Msxml2.XMLHTTP');
153                 },
154                 function() {
155                     return new ActiveXObject('Microsoft.XMLHTTP');
156                 }
157             ];
158 
159             var returnVal;
160             for (var i = 0, len = methods.length; i < len; i++) {
161                 try {
162                     returnVal = methods[i]();
163                 } catch(e) {
164                     continue;
165                 }
166                 return returnVal;
167             }
168             throw new Error('Could not create an XHR object.');
169         };
170 
171         /**
172          * Find instance of passed String via getElementById
173          * @ignore
174          */
175         var $ = function $() {
176             var results = [], element;
177             for (var i = 0; i < arguments.length; i++) {
178                 element = arguments[i];
179                 if (typeof element == 'string') {
180                     element = document.getElementById(element);
181                 }
182                 results.push(element);
183             }
184             return results.length > 1 ? results : results[0];
185         };
186 
187         /**
188          * Get the form element which encloses the supplied element.
189          * @param element - element to act against in search
190          * @returns form element representing enclosing form, or first form if none found.
191          * @ignore
192          */
193         var getForm = function getForm(element) {
194             if (element) {
195                 var form = $(element);
196                 while (form) {
197 
198                     if (form.nodeName && (form.nodeName.toLowerCase() == 'form')) {
199                         return form;
200                     }
201                     if (form.form) {
202                         return form.form;
203                     }
204                     if (form.parentNode) {
205                         form = form.parentNode;
206                     } else {
207                         form = null;
208                     }
209                 }
210                 return document.forms[0];
211             }
212             return null;
213         };
214 
215         /**
216          * Check if a value exists in an array
217          * @ignore
218          */
219         var isInArray = function isInArray(array, value) {
220             for (var i = 0; i < array.length; i++) {
221                 if (array[i] === value) {
222                     return true;
223                 }
224             }
225             return false;
226         };
227 
228 
229         /**
230          * Evaluate JavaScript code in a global context.
231          * @param src JavaScript code to evaluate
232          * @ignore
233          */
234         var globalEval = function globalEval(src) {
235             if (window.execScript) {
236                 window.execScript(src);
237                 return;
238             }
239             // We have to wrap the call in an anon function because of a firefox bug, where this is incorrectly set
240             // We need to explicitly call window.eval because of a Chrome peculiarity
241             var fn = function() {
242                 window.eval.call(window,src);
243             };
244             fn();
245         };
246 
247         /**
248          * Get all scripts from supplied string, return them as an array for later processing.
249          * @param str
250          * @returns {array} of script text
251          * @ignore
252          */
253         var stripScripts = function stripScripts(str) {
254             // Regex to find all scripts in a string
255             var findscripts = /<script[^>]*>([\S\s]*?)<\/script>/igm;
256             // Regex to find one script, to isolate it's content [2] and attributes [1]
257             var findscript = /<script([^>]*)>([\S\s]*?)<\/script>/im;
258             // Regex to remove leading cruft
259             var stripStart = /^\s*(<!--)*\s*(\/\/)*\s*(\/\*)*\s*(<!\[CDATA\[)*/;
260             // Regex to find src attribute
261             var findsrc = /src="([\S]*?)"/im;
262             var initialnodes = [];
263             var scripts = [];
264             initialnodes = str.match(findscripts);
265             while (!!initialnodes && initialnodes.length > 0) {
266                 var scriptStr = [];
267                 scriptStr = initialnodes.shift().match(findscript);
268                 var src = [];
269                 // check if src specified
270                 src = scriptStr[1].match(findsrc);
271                 var script;
272                 if ( !!src && src[1]) {
273                     // if this is a file, load it
274                     var url = src[1];
275                     // if this is another copy of jsf.js, don't load it
276                     // it's never necessary, and can make debugging difficult
277                     if (/\/javax.faces.resource\/jsf.js\?ln=javax\.faces/.test(url)) {
278                         script = false;
279                     } else {
280                         script = loadScript(url);
281                     }
282                 } else if (!!scriptStr && scriptStr[2]){
283                     // else get content of tag, without leading CDATA and such
284                     script = scriptStr[2].replace(stripStart,"");
285                 } else {
286                     script = false;
287                 }
288                 if (!!script) {
289                     scripts.push(script);
290                 }
291             }
292             return scripts;
293         };
294 
295         /**
296          * Load a script via a url, use synchronous XHR request.  This is liable to be slow,
297          * but it's probably the only correct way.
298          * @param url the url to load
299          * @ignore
300          */
301         var loadScript = function loadScript(url) {
302             var xhr = getTransport();
303             if (xhr === null) {
304                 return "";
305             }
306 
307             xhr.open("GET", url, false);
308             xhr.setRequestHeader("Content-Type", "application/x-javascript");
309             xhr.send(null);
310 
311             // PENDING graceful error handling
312             if (xhr.readyState == 4 && xhr.status == 200) {
313                     return xhr.responseText;
314             }
315 
316             return "";
317         };
318 
319         /**
320          * Run an array of scripts text
321          * @param scripts array of script nodes
322          * @ignore
323          */
324         var runScripts = function runScripts(scripts) {
325             if (!scripts || scripts.length === 0) {
326                 return;
327             }
328 
329             var head = document.getElementsByTagName('head')[0] || document.documentElement;
330             while (scripts.length) {
331                 // create script node
332                 var scriptNode = document.createElement('script');
333                 scriptNode.type = 'text/javascript';
334                 scriptNode.text = scripts.shift(); // add the code to the script node
335                 head.appendChild(scriptNode); // add it to the page
336                 head.removeChild(scriptNode); // then remove it
337             }
338         };
339 
340         /**
341          * Replace DOM element with a new tagname and supplied innerHTML
342          * @param element element to replace
343          * @param tempTagName new tag name to replace with
344          * @param src string new content for element
345          * @ignore
346          */
347         var elementReplaceStr = function elementReplaceStr(element, tempTagName, src) {
348             var parent = element.parentNode;
349             var temp = document.createElement(tempTagName);
350             if (element.id) {
351                 temp.id = element.id;
352             }
353 
354             // Creating a head element isn't allowed in IE, and faulty in most browsers,
355             // so it is not allowed
356             if (element.nodeName.toLowerCase() === "head") {
357                 throw new Error("Attempted to replace a head element - this is not allowed.");
358             } else {
359                 var scripts = [];
360                 if (isAutoExec()) {
361                     temp.innerHTML = src;
362                 } else {
363                     // Get scripts from text
364                     scripts = stripScripts(src);
365                     // Remove scripts from text
366                     src = src.replace(/<script[^>]*>([\S\s]*?)<\/script>/igm,"");
367                     temp.innerHTML = src;
368                 }
369             }
370 
371             replaceNode(temp, element);            
372             runScripts(scripts);
373 
374         };
375 
376         /**
377          * Get a string with the concatenated values of all string nodes under the given node
378          * @param  oNode the given DOM node
379          * @param  deep boolean - whether to recursively scan the children nodes of the given node for text as well. Default is <code>false</code>
380          * @ignore
381          * Note:  This code originally from Sarissa: http://dev.abiss.gr/sarissa
382          * It has been modified to fit into the overall codebase
383          */
384         var getText = function getText(oNode, deep) {
385             var Node = {ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4,
386                 ENTITY_REFERENCE_NODE: 5,  ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7,
387                 COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10,
388                 DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12};
389 
390             var s = "";
391             var nodes = oNode.childNodes;
392             for (var i = 0; i < nodes.length; i++) {
393                 var node = nodes[i];
394                 var nodeType = node.nodeType;
395                 if (nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE) {
396                     s += node.data;
397                 } else if (deep === true && (nodeType == Node.ELEMENT_NODE ||
398                                              nodeType == Node.DOCUMENT_NODE ||
399                                              nodeType == Node.DOCUMENT_FRAGMENT_NODE)) {
400                     s += getText(node, true);
401                 }
402             }
403             return s;
404         };
405 
406         var PARSED_OK = "Document contains no parsing errors";
407         var PARSED_EMPTY = "Document is empty";
408         var PARSED_UNKNOWN_ERROR = "Not well-formed or other error";
409         var getParseErrorText;
410         if (isIE()) {
411             /**
412              * Note: This code orginally from Sarissa: http://dev.abiss.gr/sarissa
413              * @ignore
414              */
415             getParseErrorText = function (oDoc) {
416                 var parseErrorText = PARSED_OK;
417                 if (oDoc && oDoc.parseError && oDoc.parseError.errorCode && oDoc.parseError.errorCode !== 0) {
418                     parseErrorText = "XML Parsing Error: " + oDoc.parseError.reason +
419                                      "\nLocation: " + oDoc.parseError.url +
420                                      "\nLine Number " + oDoc.parseError.line + ", Column " +
421                                      oDoc.parseError.linepos +
422                                      ":\n" + oDoc.parseError.srcText +
423                                      "\n";
424                     for (var i = 0; i < oDoc.parseError.linepos; i++) {
425                         parseErrorText += "-";
426                     }
427                     parseErrorText += "^\n";
428                 }
429                 else if (oDoc.documentElement === null) {
430                     parseErrorText = PARSED_EMPTY;
431                 }
432                 return parseErrorText;
433             };
434         } else { // (non-IE)
435 
436             /**
437              * <p>Returns a human readable description of the parsing error. Useful
438              * for debugging. Tip: append the returned error string in a <pre>
439              * element if you want to render it.</p>
440              * @param  oDoc The target DOM document
441              * @returns {String} The parsing error description of the target Document in
442              *          human readable form (preformated text)
443              * @ignore
444              * Note:  This code orginally from Sarissa: http://dev.abiss.gr/sarissa
445              */
446             getParseErrorText = function (oDoc) {
447                 var parseErrorText = PARSED_OK;
448                 if ((!oDoc) || (!oDoc.documentElement)) {
449                     parseErrorText = PARSED_EMPTY;
450                 } else if (oDoc.documentElement.tagName == "parsererror") {
451                     parseErrorText = oDoc.documentElement.firstChild.data;
452                     parseErrorText += "\n" + oDoc.documentElement.firstChild.nextSibling.firstChild.data;
453                 } else if (oDoc.getElementsByTagName("parsererror").length > 0) {
454                     var parsererror = oDoc.getElementsByTagName("parsererror")[0];
455                     parseErrorText = getText(parsererror, true) + "\n";
456                 } else if (oDoc.parseError && oDoc.parseError.errorCode !== 0) {
457                     parseErrorText = PARSED_UNKNOWN_ERROR;
458                 }
459                 return parseErrorText;
460             };
461         }
462 
463         if ((typeof(document.importNode) == "undefined") && isIE()) {
464             try {
465                 /**
466                  * Implementation of importNode for the context window document in IE.
467                  * If <code>oNode</code> is a TextNode, <code>bChildren</code> is ignored.
468                  * @param oNode the Node to import
469                  * @param bChildren whether to include the children of oNode
470                  * @returns the imported node for further use
471                  * @ignore
472                  * Note:  This code orginally from Sarissa: http://dev.abiss.gr/sarissa
473                  */
474                 document.importNode = function(oNode, bChildren) {
475                     var tmp;
476                     if (oNode.nodeName == '#text') {
477                         return document.createTextNode(oNode.data);
478                     }
479                     else {
480                         if (oNode.nodeName == "tbody" || oNode.nodeName == "tr") {
481                             tmp = document.createElement("table");
482                         }
483                         else if (oNode.nodeName == "td") {
484                             tmp = document.createElement("tr");
485                         }
486                         else if (oNode.nodeName == "option") {
487                             tmp = document.createElement("select");
488                         }
489                         else {
490                             tmp = document.createElement("div");
491                         }
492                         if (bChildren) {
493                             tmp.innerHTML = oNode.xml ? oNode.xml : oNode.outerHTML;
494                         } else {
495                             tmp.innerHTML = oNode.xml ? oNode.cloneNode(false).xml : oNode.cloneNode(false).outerHTML;
496                         }
497                         return tmp.getElementsByTagName("*")[0];
498                     }
499                 };
500             } catch(e) {
501             }
502         }
503         // Setup Node type constants for those browsers that don't have them (IE)
504         var Node = {ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4,
505             ENTITY_REFERENCE_NODE: 5,  ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7,
506             COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10,
507             DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12};
508 
509         // PENDING - add support for removing handlers added via DOM 2 methods
510         /**
511          * Delete all events attached to a node
512          * @param node
513          * @ignore
514          */
515         var clearEvents = function clearEvents(node) {
516             if (!node) {
517                 return;
518             }
519 
520             // don't do anything for text and comment nodes - unnecessary
521             if (node.nodeType == Node.TEXT_NODE || node.nodeType == Node.COMMENT_NODE) {
522                 return;
523             }
524 
525             var events = ['abort', 'blur', 'change', 'error', 'focus', 'load', 'reset', 'resize', 'scroll', 'select', 'submit', 'unload',
526             'keydown', 'keypress', 'keyup', 'click', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'dblclick' ];
527             try {
528                 for (var e in events) {
529                     if (events.hasOwnProperty(e)) {
530                         node[e] = null;
531                     }
532                 }
533             } catch (ex) {
534                 // it's OK if it fails, at least we tried
535             }
536         };
537 
538         /**
539          * Deletes node
540          * @param node
541          * @ignore
542          */
543         var deleteNode = function deleteNode(node) {
544             if (!node) {
545                 return;
546             }
547             if (!isIE()) {
548                 // nothing special required
549                 if (!node.parentNode) {
550                     // if there's no parent, there's nothing to do
551                     return;
552                 }
553                 node.parentNode.removeChild(node);
554                 return;
555             }
556             // The rest of this code is specialcasing for IE
557             if (node.nodeName.toLowerCase() === "body") {
558                 // special case for removing body under IE.
559                 deleteChildren(node);
560                 try {
561                     node.outerHTML = '';
562                 } catch (ex) {
563                     // fails under some circumstances, but not in RI
564                     // supplied responses.  If we've gotten here, it's
565                     // fairly safe to leave a lingering body tag rather than
566                     // fail outright
567                 }
568                 return;
569             }
570             var temp = node.ownerDocument.createElement('div');
571             var parent = node.parentNode;
572             temp.appendChild(parent.removeChild(node));
573             // Now clean up the temporary element
574             try {
575                 temp.outerHTML = ''; //prevent leak in IE
576             } catch (ex) {
577                 // at least we tried.  Fails in some circumstances,
578                 // but not in RI supplied responses.  Better to leave a lingering
579                 // temporary div than to fail outright.
580             }
581         };
582 
583         /**
584          * Deletes all children of a node
585          * @param node
586          * @ignore
587          */
588         var deleteChildren = function deleteChildren(node) {
589             if (!node) {
590                 return;
591             }
592             for (var x = node.childNodes.length - 1; x >= 0; x--) { //delete all of node's children
593                 var childNode = node.childNodes[x];
594                 deleteNode(childNode);
595             }
596         };
597 
598         /**
599          * <p> Copies the childNodes of nodeFrom to nodeTo</p>
600          *
601          * @param  nodeFrom the Node to copy the childNodes from
602          * @param  nodeTo the Node to copy the childNodes to
603          * @ignore
604          * Note:  This code originally from Sarissa:  http://dev.abiss.gr/sarissa
605          * It has been modified to fit into the overall codebase
606          */
607         var copyChildNodes = function copyChildNodes(nodeFrom, nodeTo) {
608 
609             if ((!nodeFrom) || (!nodeTo)) {
610                 throw "Both source and destination nodes must be provided";
611             }
612 
613             deleteChildren(nodeTo);
614             var nodes = nodeFrom.childNodes;
615             // if within the same doc, just move, else copy and delete
616             if (nodeFrom.ownerDocument == nodeTo.ownerDocument) {
617                 while (nodeFrom.firstChild) {
618                     nodeTo.appendChild(nodeFrom.firstChild);
619                 }
620             } else {
621                 var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument;
622                 var i;
623                 if (typeof(ownerDoc.importNode) != "undefined") {
624                     for (i = 0; i < nodes.length; i++) {
625                         nodeTo.appendChild(ownerDoc.importNode(nodes[i], true));
626                     }
627                 } else {
628                     for (i = 0; i < nodes.length; i++) {
629                         nodeTo.appendChild(nodes[i].cloneNode(true));
630                     }
631                 }
632             }
633         };
634 
635 
636         /**
637          * Replace one node with another.  Necessary for handling IE memory leak.
638          * @param node
639          * @param newNode
640          */
641         var replaceNode = function replaceNode(newNode, node) {
642                if(isIE()){
643                     node.parentNode.insertBefore(newNode, node);
644                     deleteNode(node);
645                } else {
646                     node.parentNode.replaceChild(newNode, node);
647                }
648         };
649 
650 
651         /**
652          * copy all attributes from one element to another - except id
653          * @param target element to copy attributes to
654          * @param source element to copy attributes from
655          * @ignore
656          */
657         var cloneAttributes = function cloneAttributes(target, source) {
658 
659             // enumerate core element attributes - without 'dir' as special case
660             var coreElementAttributes = ['className', 'title', 'lang', 'xml:lang'];
661 
662             // Enumerate additional input element attributes
663             var inputElementAttributes =
664                     [   'name', 'value', 'checked', 'disabled', 'readOnly',
665                         'size', 'maxLength', 'src', 'alt', 'useMap', 'isMap',
666                         'tabIndex', 'accessKey', 'accept', 'type'
667                     ];
668 
669             // Enumerate all the names of the event listeners
670             var listenerNames =
671                     [ 'onclick', 'ondblclick', 'onmousedown', 'onmousemove', 'onmouseout',
672                         'onmouseover', 'onmouseup', 'onkeydown', 'onkeypress', 'onkeyup',
673                         'onhelp', 'onblur', 'onfocus', 'onchange', 'onload', 'onunload', 'onabort',
674                         'onreset', 'onselect', 'onsubmit'
675                     ];
676 
677             var iIndex, iLength; // for loop variables
678             var attributeName; // name of the attribute to set
679             var newValue, oldValue; // attribute values in each element
680 
681             // First, copy over core attributes
682             for (iIndex = 0,iLength = coreElementAttributes.length; iIndex < iLength; iIndex++) {
683                 attributeName = coreElementAttributes[iIndex];
684                 newValue = source[attributeName];
685                 oldValue = target[attributeName];
686                 if (oldValue != newValue) {
687                     target[attributeName] = newValue;
688                 }
689             }
690 
691             // Next, if it's an input, copy those over
692             if (target.nodeName.toLowerCase() === 'input') {
693                 for (iIndex = 0,iLength = inputElementAttributes.length; iIndex < iLength; iIndex++) {
694                     attributeName = inputElementAttributes[iIndex];
695                     newValue = source[attributeName];
696                     oldValue = target[attributeName];
697                     if (oldValue != newValue) {
698                         target[attributeName] = newValue;
699                     }
700                 }
701             }
702             //'style' attribute special case
703             var newStyle = source.getAttribute('style');
704             var oldStyle = target.getAttribute('style');
705             if (newStyle != oldStyle) {
706                 if (isIE()) {
707                     target.style.setAttribute('cssText', newStyle, 0);
708                 } else {
709                     target.setAttribute('style',newStyle);
710                 }
711             }
712             for (var lIndex = 0, lLength = listenerNames.length; lIndex < lLength; lIndex++) {
713                 var name = listenerNames[lIndex];
714                 target[name] = source[name] ? source[name] : null;
715                 if (source[name]) {
716                     source[name] = null;
717                 }
718             }
719             // Special case for 'dir' attribute
720             if (!isIE() && source.dir != target.dir) {
721                 target.dir = source.dir ? source.dir : null;
722             }
723         };
724 
725         /**
726          * Replace an element from one document into another
727          * @param newElement new element to put in document
728          * @param origElement original element to replace
729          * @ignore
730          */
731         var elementReplace = function elementReplace(newElement, origElement) {
732             copyChildNodes(newElement, origElement);
733             // sadly, we have to reparse all over again
734             // to reregister the event handlers and styles
735             // PENDING do some performance tests on large pages
736             origElement.innerHTML = origElement.innerHTML;
737 
738             try {
739                 cloneAttributes(origElement, newElement);
740             } catch (ex) {
741                 // if in dev mode, report an error, else try to limp onward
742                 if (jsf.getProjectStage() == "Development") {
743                     throw new Error("Error updating attributes");
744                 }
745             }
746             deleteNode(newElement);
747 
748         };
749 
750         /**
751          * Create a new document, then select the body element within it
752          * @param docStr Stringified version of document to create
753          * @return element the body element
754          * @ignore
755          */
756         var getBodyElement = function getBodyElement(docStr) {
757 
758             var doc;  // intermediate document we'll create
759             var body; // Body element to return
760 
761             if (typeof DOMParser !== "undefined") {  // FF, S, Chrome
762                 doc = (new DOMParser()).parseFromString(docStr, "text/xml");
763             } else if (typeof ActiveXObject !== "undefined") { // IE
764                 doc = new ActiveXObject("MSXML2.DOMDocument");
765                 doc.loadXML(docStr);
766             } else {
767                 throw new Error("You don't seem to be running a supported browser");
768             }
769 
770             if (getParseErrorText(doc) !== PARSED_OK) {
771                 throw new Error(getParseErrorText(doc));
772             }
773 
774             body = doc.getElementsByTagName("body")[0];
775 
776             if (!body) {
777                 throw new Error("Can't find body tag in returned document.");
778             }
779 
780             return body;
781         };
782 
783         /**
784          * Do update.
785          * @param element element to update
786          * @param context context of request
787          * @ignore
788          */
789         var doUpdate = function doUpdate(element, context) {
790             var id, content, markup, state;
791             var stateForm;
792             var scripts = []; // temp holding value for array of script nodes
793 
794             id = element.getAttribute('id');
795             if (id === "javax.faces.ViewState") {
796 
797                 state = element.firstChild;
798 
799                 // Now set the view state from the server into the DOM
800                 // but only for the form that submitted the request.
801 
802                 stateForm = document.getElementById(context.formid);
803                 if (!stateForm || !stateForm.elements) {
804                     // if the form went away for some reason, or it lacks elements 
805                     // we're going to just return silently.
806                     return;
807                 }
808                 var field = stateForm.elements["javax.faces.ViewState"];
809                 if (typeof field == 'undefined') {
810                     field = document.createElement("input");
811                     field.type = "hidden";
812                     field.name = "javax.faces.ViewState";
813                     stateForm.appendChild(field);
814                 }
815                 field.value = state.nodeValue;
816 
817                 // Now set the view state from the server into the DOM
818                 // for any form that is a render target.
819 
820                 if (typeof context.render !== 'undefined' && context.render !== null) {
821                     var temp = context.render.split(' ');
822                     for (var i = 0; i < temp.length; i++) {
823                         if (temp.hasOwnProperty(i)) {
824                             // See if the element is a form and
825                             // the form is not the one that caused the submission..
826                             var f = document.forms[temp[i]];
827                             if (typeof f !== 'undefined' && f !== null && f.id !== context.formid) {
828                                 field = f.elements["javax.faces.ViewState"];
829                                 if (typeof field === 'undefined') {
830                                     field = document.createElement("input");
831                                     field.type = "hidden";
832                                     field.name = "javax.faces.ViewState";
833                                     f.appendChild(field);
834                                 }
835                                 field.value = state.nodeValue;
836                             }
837                         }
838                     }
839                 }
840                 return;
841             }
842 
843             // join the CDATA sections in the markup
844             markup = '';
845             for (var j = 0; j < element.childNodes.length; j++) {
846                 content = element.childNodes[j];
847                 markup += content.nodeValue;
848             }
849 
850             var src = markup;
851 
852             // If our special render all markup is present..
853             if (id === "javax.faces.ViewRoot" || id === "javax.faces.ViewBody") {
854                 var bodyStartEx = new RegExp("< *body[^>]*>", "gi");
855                 var bodyEndEx = new RegExp("< */ *body[^>]*>", "gi");
856                 var newsrc;
857 
858                 var docBody = document.getElementsByTagName("body")[0];
859                 var bodyStart = bodyStartEx.exec(src);
860 
861                 if (bodyStart !== null) { // replace body tag
862                     // First, try with XML manipulation
863                     try {
864                         // Get scripts from text
865                         scripts = stripScripts(src);
866                         // Remove scripts from text
867                         newsrc = src.replace(/<script[^>]*>([\S\s]*?)<\/script>/igm, "");
868                         elementReplace(getBodyElement(newsrc), docBody);
869                         runScripts(scripts);
870                     } catch (e) {
871                         // OK, replacing the body didn't work with XML - fall back to quirks mode insert
872                         var srcBody, bodyEnd;
873                         // if src contains </body>
874                         bodyEnd = bodyEndEx.exec(src);
875                         if (bodyEnd !== null) {
876                             srcBody = src.substring(bodyStartEx.lastIndex,
877                                     bodyEnd.index);
878                         } else { // can't find the </body> tag, punt
879                             srcBody = src.substring(bodyStartEx.lastIndex);
880                         }
881                         // replace body contents with innerHTML - note, script handling happens within function
882                         elementReplaceStr(docBody, "body", srcBody);
883 
884                     }
885 
886                 } else {  // replace body contents with innerHTML - note, script handling happens within function
887                     elementReplaceStr(docBody, "body", src);
888                 }
889             } else if (id === "javax.faces.ViewHead") {
890                 throw new Error("javax.faces.ViewHead not supported - browsers cannot reliably replace the head's contents");
891             } else {
892                 var d = $(id);
893                 if (!d) {
894                     throw new Error("During update: " + id + " not found");
895                 }
896                 var parent = d.parentNode;
897                 // Trim space padding before assigning to innerHTML
898                 var html = src.replace(/^\s+/g, '').replace(/\s+$/g, '');
899                 var parserElement = document.createElement('div');
900                 var tag = d.nodeName.toLowerCase();
901                 var tableElements = ['td', 'th', 'tr', 'tbody', 'thead', 'tfoot'];
902                 var isInTable = false;
903                 for (var tei = 0, tel = tableElements.length; tei < tel; tei++) {
904                     if (tableElements[tei] == tag) {
905                         isInTable = true;
906                         break;
907                     }
908                 }
909                 if (isInTable) {
910 
911                     if (isAutoExec()) {
912                         // Create html
913                         parserElement.innerHTML = '<table>' + html + '</table>';
914                     } else {
915                         // Get the scripts from the text
916                         scripts = stripScripts(html);
917                         // Remove scripts from text
918                         html = html.replace(/<script[^>]*>([\S\s]*?)<\/script>/igm,"");
919                         parserElement.innerHTML = '<table>' + html + '</table>';
920                     }
921                     var newElement = parserElement.firstChild;
922                     //some browsers will also create intermediary elements such as table>tbody>tr>td
923                     while ((null !== newElement) && (id !== newElement.id)) {
924                         newElement = newElement.firstChild;
925                     }
926                     parent.replaceChild(newElement, d);
927                     runScripts(scripts);
928                 } else if (d.nodeName.toLowerCase() === 'input') {
929                     // special case handling for 'input' elements
930                     // in order to not lose focus when updating,
931                     // input elements need to be added in place.
932                     parserElement = document.createElement('div');
933                     parserElement.innerHTML = html;
934                     newElement = parserElement.firstChild;
935 
936                     cloneAttributes(d, newElement);
937                     deleteNode(parserElement);
938                 } else if (html.length > 0) {
939                     if (isAutoExec()) {
940                         // Create html
941                         parserElement.innerHTML = html;
942                     } else {
943                         // Get the scripts from the text
944                         scripts = stripScripts(html);
945                         // Remove scripts from text
946                         html = html.replace(/<script[^>]*>([\S\s]*?)<\/script>/igm,"");
947                         parserElement.innerHTML = html;
948                     }
949                     replaceNode(parserElement.firstChild, d);
950                     deleteNode(parserElement);
951                     runScripts(scripts);
952                 }
953             }
954         };
955 
956         /**
957          * Delete a node specified by the element.
958          * @param element
959          * @ignore
960          */
961         var doDelete = function doDelete(element) {
962             var id = element.getAttribute('id');
963             var target = $(id);
964             deleteNode(target);
965         };
966 
967         /**
968          * Insert a node specified by the element.
969          * @param element
970          * @ignore
971          */
972         var doInsert = function doInsert(element) {
973             var scripts = [];
974             var target = $(element.firstChild.getAttribute('id'));
975             var parent = target.parentNode;
976             var tempElement = document.createElement('span');
977             var html = element.firstChild.firstChild.nodeValue;
978 
979             if (isAutoExec()) {
980                 // Create html
981                 tempElement.innerHTML = html;
982             } else {
983                 // Get the scripts from the text
984                 scripts = stripScripts(html);
985                 // Remove scripts from text
986                 html = html.replace(/<script[^>]*>([\S\s]*?)<\/script>/igm,"");
987                 tempElement.innerHTML = html;
988             }
989             if (element.firstChild.nodeName === 'after') {
990                 // Get the next in the list, to insert before
991                 target = target.nextSibling;
992             }  // otherwise, this is a 'before' element
993             if (!!tempElement.innerHTML) { // check if only scripts were inserted - if so, do nothing here
994                 parent.insertBefore(tempElement.firstChild, target);
995             }
996             runScripts(scripts);
997             deleteNode(tempElement);
998         };
999 
1000         /**
1001          * Modify attributes of given element id.
1002          * @param element
1003          * @ignore
1004          */
1005         var doAttributes = function doAttributes(element) {
1006 
1007             // Get id of element we'll act against
1008             var id = element.getAttribute('id');
1009 
1010             var target = $(id);
1011 
1012             if (!target) {
1013                 throw new Error("The specified id: " + id + " was not found in the page.");
1014             }
1015 
1016             // There can be multiple attributes modified.  Loop through the list.
1017             var nodes = element.childNodes;
1018             for (var i = 0; i < nodes.length; i++) {
1019                 var name = nodes[i].getAttribute('name');
1020                 var value = nodes[i].getAttribute('value');
1021                 if (!isIE()) {
1022                     target.setAttribute(name, value);
1023                 } else { // if it's IE, then quite a bit more work is required
1024                     if (name === 'class') {
1025                         name = 'className';
1026                         target.setAttribute(name, value, 0);
1027                     } else if (name === "for") {
1028                         name = 'htmlFor';
1029                         target.setAttribute(name, value, 0);
1030                     } else if (name === 'style') {
1031                         target.style.setAttribute('cssText', value, 0);
1032                     } else if (name.substring(0, 2) === 'on') {
1033                         var fn = function(value) {
1034                             return function() {
1035                                 window.execScript(value);
1036                             };
1037                         }(value);
1038                         target.setAttribute(name, fn, 0);
1039                     } else if (name === 'dir') {
1040                         if (jsf.getProjectStage() == 'Development') {
1041                             throw new Error("Cannot set 'dir' attribute in IE");
1042                         }
1043                     } else {
1044                         target.setAttribute(name, value, 0);
1045                     }
1046                 }
1047             }
1048         };
1049 
1050         /**
1051          * Eval the CDATA of the element.
1052          * @param element to eval
1053          * @ignore
1054          */
1055         var doEval = function doEval(element) {
1056             var evalText = element.firstChild.nodeValue;
1057             globalEval(evalText);
1058         };
1059 
1060         /**
1061          * Ajax Request Queue
1062          * @ignore
1063          */
1064         var Queue = new function Queue() {
1065 
1066             // Create the internal queue
1067             var queue = [];
1068 
1069 
1070             // the amount of space at the front of the queue, initialised to zero
1071             var queueSpace = 0;
1072 
1073             /** Returns the size of this Queue. The size of a Queue is equal to the number
1074              * of elements that have been enqueued minus the number of elements that have
1075              * been dequeued.
1076              * @ignore
1077              */
1078             this.getSize = function getSize() {
1079                 return queue.length - queueSpace;
1080             };
1081 
1082             /** Returns true if this Queue is empty, and false otherwise. A Queue is empty
1083              * if the number of elements that have been enqueued equals the number of
1084              * elements that have been dequeued.
1085              * @ignore
1086              */
1087             this.isEmpty = function isEmpty() {
1088                 return (queue.length === 0);
1089             };
1090 
1091             /** Enqueues the specified element in this Queue.
1092              *
1093              * @param element - the element to enqueue
1094              * @ignore
1095              */
1096             this.enqueue = function enqueue(element) {
1097                 // Queue the request
1098                 queue.push(element);
1099             };
1100 
1101 
1102             /** Dequeues an element from this Queue. The oldest element in this Queue is
1103              * removed and returned. If this Queue is empty then undefined is returned.
1104              *
1105              * @returns Object The element that was removed from the queue.
1106              * @ignore
1107              */
1108             this.dequeue = function dequeue() {
1109                 // initialise the element to return to be undefined
1110                 var element = undefined;
1111 
1112                 // check whether the queue is empty
1113                 if (queue.length) {
1114                     // fetch the oldest element in the queue
1115                     element = queue[queueSpace];
1116 
1117                     // update the amount of space and check whether a shift should occur
1118                     if (++queueSpace * 2 >= queue.length) {
1119                         // set the queue equal to the non-empty portion of the queue
1120                         queue = queue.slice(queueSpace);
1121                         // reset the amount of space at the front of the queue
1122                         queueSpace = 0;
1123                     }
1124                 }
1125                 // return the removed element
1126                 try {
1127                     return element;
1128                 } finally {
1129                     element = null; // IE 6 leak prevention
1130                 }
1131             };
1132 
1133             /** Returns the oldest element in this Queue. If this Queue is empty then
1134              * undefined is returned. This function returns the same value as the dequeue
1135              * function, but does not remove the returned element from this Queue.
1136              * @ignore
1137              */
1138             this.getOldestElement = function getOldestElement() {
1139                 // initialise the element to return to be undefined
1140                 var element = undefined;
1141 
1142                 // if the queue is not element then fetch the oldest element in the queue
1143                 if (queue.length) {
1144                     element = queue[queueSpace];
1145                 }
1146                 // return the oldest element
1147                 try {
1148                     return element;
1149                 } finally {
1150                     element = null; //IE 6 leak prevention
1151                 }
1152             };
1153         }();
1154 
1155 
1156         /**
1157          * AjaxEngine handles Ajax implementation details.
1158          * @ignore
1159          */
1160         var AjaxEngine = function AjaxEngine() {
1161 
1162             var req = {};                  // Request Object
1163             req.url = null;                // Request URL
1164             req.context = {};              // Context of request and response
1165             req.context.sourceid = null;   // Source of this request
1166             req.context.onerror = null;    // Error handler for request
1167             req.context.onevent = null;    // Event handler for request
1168             req.context.formid = null;     // Form that's the context for this request
1169             req.xmlReq = null;             // XMLHttpRequest Object
1170             req.async = true;              // Default - Asynchronous
1171             req.parameters = {};           // Parameters For GET or POST
1172             req.queryString = null;        // Encoded Data For GET or POST
1173             req.method = null;             // GET or POST
1174             req.status = null;             // Response Status Code From Server
1175             req.fromQueue = false;         // Indicates if the request was taken off the queue
1176             // before being sent.  This prevents the request from
1177             // entering the queue redundantly.
1178 
1179             req.que = Queue;
1180 
1181             // Get an XMLHttpRequest Handle
1182             req.xmlReq = getTransport();
1183             if (req.xmlReq === null) {
1184                 return null;
1185             }
1186 
1187             // Set up request/response state callbacks
1188             /**
1189              * @ignore
1190              */
1191             req.xmlReq.onreadystatechange = function() {
1192                 if (req.xmlReq.readyState === 4) {
1193                     req.onComplete();
1194                 }
1195             };
1196 
1197             /**
1198              * This function is called when the request/response interaction
1199              * is complete.  If the return status code is successfull,
1200              * dequeue all requests from the queue that have completed.  If a
1201              * request has been found on the queue that has not been sent,
1202              * send the request.
1203              * @ignore
1204              */
1205             req.onComplete = function onComplete() {
1206                 if (req.xmlReq.status && (req.xmlReq.status >= 200 && req.xmlReq.status < 300)) {
1207                     sendEvent(req.xmlReq, req.context, "complete");
1208                     jsf.ajax.response(req.xmlReq, req.context);
1209                 } else {
1210                     sendEvent(req.xmlReq, req.context, "complete");
1211                     sendError(req.xmlReq, req.context, "httpError");
1212                 }
1213 
1214                 // Regardless of whether the request completed successfully (or not),
1215                 // dequeue requests that have been completed (readyState 4) and send
1216                 // requests that ready to be sent (readyState 0).
1217 
1218                 var nextReq = req.que.getOldestElement();
1219                 if (nextReq === null || typeof nextReq === 'undefined') {
1220                     return;
1221                 }
1222                 while ((typeof nextReq.xmlReq !== 'undefined' && nextReq.xmlReq !== null) &&
1223                        nextReq.xmlReq.readyState === 4) {
1224                     req.que.dequeue();
1225                     nextReq = req.que.getOldestElement();
1226                     if (nextReq === null || typeof nextReq === 'undefined') {
1227                         break;
1228                     }
1229                 }
1230                 if (nextReq === null || typeof nextReq === 'undefined') {
1231                     return;
1232                 }
1233                 if ((typeof nextReq.xmlReq !== 'undefined' && nextReq.xmlReq !== null) &&
1234                     nextReq.xmlReq.readyState === 0) {
1235                     nextReq.fromQueue = true;
1236                     nextReq.sendRequest();
1237                 }
1238             };
1239 
1240             /**
1241              * Utility method that accepts additional arguments for the AjaxEngine.
1242              * If an argument is passed in that matches an AjaxEngine property, the
1243              * argument value becomes the value of the AjaxEngine property.
1244              * Arguments that don't match AjaxEngine properties are added as
1245              * request parameters.
1246              * @ignore
1247              */
1248             req.setupArguments = function(args) {
1249                 for (var i in args) {
1250                     if (args.hasOwnProperty(i)) {
1251                         if (typeof req[i] === 'undefined') {
1252                             req.parameters[i] = args[i];
1253                         } else {
1254                             req[i] = args[i];
1255                         }
1256                     }
1257                 }
1258             };
1259 
1260             /**
1261              * This function does final encoding of parameters, determines the request method
1262              * (GET or POST) and sends the request using the specified url.
1263              * @ignore
1264              */
1265             req.sendRequest = function() {
1266                 if (req.xmlReq !== null) {
1267                     // if there is already a request on the queue waiting to be processed..
1268                     // just queue this request
1269                     if (!req.que.isEmpty()) {
1270                         if (!req.fromQueue) {
1271                             req.que.enqueue(req);
1272                             return;
1273                         }
1274                     }
1275                     // If the queue is empty, queue up this request and send
1276                     if (!req.fromQueue) {
1277                         req.que.enqueue(req);
1278                     }
1279                     // Some logic to get the real request URL
1280                     if (req.generateUniqueUrl && req.method == "GET") {
1281                         req.parameters["AjaxRequestUniqueId"] = new Date().getTime() + "" + req.requestIndex;
1282                     }
1283                     var content = null; // For POST requests, to hold query string
1284                     for (var i in req.parameters) {
1285                         if (req.parameters.hasOwnProperty(i)) {
1286                             if (req.queryString.length > 0) {
1287                                 req.queryString += "&";
1288                             }
1289                             req.queryString += encodeURIComponent(i) + "=" + encodeURIComponent(req.parameters[i]);
1290                         }
1291                     }
1292                     if (req.method === "GET") {
1293                         if (req.queryString.length > 0) {
1294                             req.url += ((req.url.indexOf("?") > -1) ? "&" : "?") + req.queryString;
1295                         }
1296                     }
1297                     req.xmlReq.open(req.method, req.url, req.async);
1298                     if (req.method === "POST") {
1299                         if (typeof req.xmlReq.setRequestHeader !== 'undefined') {
1300                             req.xmlReq.setRequestHeader('Faces-Request', 'partial/ajax');
1301                             req.xmlReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
1302                         }
1303                         content = req.queryString;
1304                     }
1305                     // note that async == false is not a supported feature.  We may change it in ways
1306                     // that break existing programs at any time, with no warning.
1307                     if(!req.async) {
1308                         req.xmlReq.onreadystatechange = null; // no need for readystate change listening
1309                     }
1310                     sendEvent(req.xmlReq, req.context, "begin");
1311                     req.xmlReq.send(content);
1312                     if(!req.async){
1313                         req.onComplete();
1314                 }
1315                 }
1316             };
1317 
1318             return req;
1319         };
1320 
1321         /**
1322          * Error handling callback.
1323          * Assumes that the request has completed.
1324          * @ignore
1325          */
1326         var sendError = function sendError(request, context, status, description, serverErrorName, serverErrorMessage) {
1327 
1328             // Possible errornames:
1329             // httpError
1330             // emptyResponse
1331             // serverError
1332             // malformedXML
1333 
1334             var sent = false;
1335             var data = {};  // data payload for function
1336             data.type = "error";
1337             data.status = status;
1338             data.source = document.getElementById(context.sourceid);
1339             data.responseCode = request.status;
1340             data.responseXML = request.responseXML;
1341             data.responseText = request.responseText;
1342 
1343             if (description) {
1344                 data.description = description;
1345             } else if (status == "httpError") {
1346                 if (data.responseCode === 0) {
1347                     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.";
1348                 } else {
1349                     data.description = "There was an error communicating with the server, status: " + data.responseCode;
1350                 }
1351             } else if (status == "serverError") {
1352                 data.description = serverErrorMessage;
1353             } else if (status == "emptyResponse") {
1354                 data.description = "An empty response was received from the server.  Check server error logs.";
1355             } else if (status == "malformedXML") {
1356                 if (getParseErrorText(data.responseXML) !== PARSED_OK) {
1357                     data.description = getParseErrorText(data.responseXML);
1358                 } else {
1359                     data.description = "An invalid XML response was received from the server.";
1360                 }
1361             }
1362 
1363             if (status == "serverError") {
1364                 data.errorName = serverErrorName;
1365                 data.errorMessage = serverErrorMessage;
1366             }
1367 
1368             // If we have a registered callback, send the error to it.
1369             if (context.onerror) {
1370                 context.onerror.call(null, data);
1371                 sent = true;
1372             }
1373 
1374             for (var i in errorListeners) {
1375                 if (errorListeners.hasOwnProperty(i)) {
1376                     errorListeners[i].call(null, data);
1377                     sent = true;
1378                 }
1379             }
1380 
1381             if (!sent && jsf.getProjectStage() === "Development") {
1382                 if (status == "serverError") {
1383                     alert("serverError: " + serverErrorName + " " + serverErrorMessage);
1384                 } else {
1385                     alert(status + ": " + data.description);
1386                 }
1387             }
1388         };
1389 
1390         /**
1391          * Event handling callback.
1392          * Request is assumed to have completed, except in the case of event = 'begin'.
1393          * @ignore
1394          */
1395         var sendEvent = function sendEvent(request, context, status) {
1396 
1397             var data = {};
1398             data.type = "event";
1399             data.status = status;
1400             data.source = document.getElementById(context.sourceid);
1401             if (status !== 'begin') {
1402                 data.responseCode = request.status;
1403                 data.responseXML = request.responseXML;
1404                 data.responseText = request.responseText;
1405             }
1406 
1407             if (context.onevent) {
1408                 context.onevent.call(null, data);
1409             }
1410 
1411             for (var i in eventListeners) {
1412                 if (eventListeners.hasOwnProperty(i)) {
1413                     eventListeners[i].call(null, data);
1414                 }
1415             }
1416         };
1417 
1418         // Use module pattern to return the functions we actually expose
1419         return {
1420             /**
1421              * Register a callback for error handling.
1422              * <p><b>Usage:</b></p>
1423              * <pre><code>
1424              * jsf.ajax.addOnError(handleError);
1425              * ...
1426              * var handleError = function handleError(data) {
1427              * ...
1428              * }
1429              * </pre></code>
1430              * <p><b>Implementation Requirements:</b></p>
1431              * This function must accept a reference to an existing JavaScript function.
1432              * The JavaScript function reference must be added to a list of callbacks, making it possible
1433              * to register more than one callback by invoking <code>jsf.ajax.addOnError</code>
1434              * more than once.  This function must throw an error if the <code>callback</code>
1435              * argument is not a function.
1436              *
1437              * @member jsf.ajax
1438              * @param callback a reference to a function to call on an error
1439              */
1440             addOnError: function addOnError(callback) {
1441                 if (typeof callback === 'function') {
1442                     errorListeners[errorListeners.length] = callback;
1443                 } else {
1444                     throw new Error("jsf.ajax.addOnError:  Added a callback that was not a function.");
1445                 }
1446             },
1447             /**
1448              * Register a callback for event handling.
1449              * <p><b>Usage:</b></p>
1450              * <pre><code>
1451              * jsf.ajax.addOnEvent(statusUpdate);
1452              * ...
1453              * var statusUpdate = function statusUpdate(data) {
1454              * ...
1455              * }
1456              * </pre></code>
1457              * <p><b>Implementation Requirements:</b></p>
1458              * This function must accept a reference to an existing JavaScript function.
1459              * The JavaScript function reference must be added to a list of callbacks, making it possible
1460              * to register more than one callback by invoking <code>jsf.ajax.addOnEvent</code>
1461              * more than once.  This function must throw an error if the <code>callback</code>
1462              * argument is not a function.
1463              *
1464              * @member jsf.ajax
1465              * @param callback a reference to a function to call on an event
1466              */
1467             addOnEvent: function addOnEvent(callback) {
1468                 if (typeof callback === 'function') {
1469                     eventListeners[eventListeners.length] = callback;
1470                 } else {
1471                     throw new Error("jsf.ajax.addOnEvent: Added a callback that was not a function");
1472                 }
1473             },
1474             /**
1475              * <p>Send an asynchronous Ajax request to the server.
1476              * <p><b>Usage:</b></p>
1477              * <pre><code>
1478              * Example showing all optional arguments:
1479              *
1480              * <commandButton id="button1" value="submit"
1481              *     onclick="jsf.ajax.request(this,event,
1482              *       {execute:'button1',render:'status',onevent: handleEvent,onerror: handleError});return false;"/>
1483              * </commandButton/>
1484              * </pre></code>
1485              * <p><b>Implementation Requirements:</b></p>
1486              * This function must:
1487              * <ul>
1488              * <li>Be used within the context of a <code>form</code>.</li>
1489              * <li>Capture the element that triggered this Ajax request
1490              * (from the <code>source</code> argument, also known as the
1491              * <code>source</code> element.</li>
1492              * <li>If the <code>source</code> element is <code>null</code> or
1493              * <code>undefined</code> throw an error.</li>
1494              * <li>If the <code>source</code> argument is not a <code>string</code> or
1495              * DOM element object, throw an error.</li>
1496              * <li>If the <code>source</code> argument is a <code>string</code>, find the
1497              * DOM element for that <code>string</code> identifier.
1498              * <li>If the DOM element could not be determined, throw an error.</li>
1499              * <li>If the <code>onerror</code> and <code>onevent</code> arguments are set,
1500              * they must be functions, or throw an error.
1501              * <li>Determine the <code>source</code> element's <code>form</code>
1502              * element.</li>
1503              * <li>Get the <code>form</code> view state by calling
1504              * {@link jsf.getViewState} passing the
1505              * <code>form</code> element as the argument.</li>
1506              * <li>Collect post data arguments for the Ajax request.
1507              * <ul>
1508              * <li>The following name/value pairs are required post data arguments:
1509              * <table border="1">
1510              * <tr>
1511              * <th>name</th>
1512              * <th>value</th>
1513              * </tr>
1514              * <tr>
1515              * <td><code>javax.faces.ViewState</code></td>
1516              * <td><code>Contents of javax.faces.ViewState hidden field.  This is included when
1517              * {@link jsf.getViewState} is used.</code></td>
1518              * </tr>
1519              * <tr>
1520              * <td><code>javax.faces.partial.ajax</code></td>
1521              * <td><code>true</code></td>
1522              * </tr>
1523              * <tr>
1524              * <td><code>javax.faces.source</code></td>
1525              * <td><code>The identifier of the element that triggered this request.</code></td>
1526              * </tr>
1527              * </table>
1528              * </li>
1529              * </ul>
1530              * </li>
1531              * <li>Collect optional post data arguments for the Ajax request.
1532              * <ul>
1533              * <li>Determine additional arguments (if any) from the <code>options</code>
1534              * argument. If <code>options.execute</code> exists:
1535              * <ul>
1536              * <li>If the keyword <code>@none</code> is present, do not create and send
1537              * the post data argument <code>javax.faces.partial.execute</code>.</li>
1538              * <li>If the keyword <code>@all</code> is present, create the post data argument with
1539              * the name <code>javax.faces.partial.execute</code> and the value <code>@all</code>.</li>
1540              * <li>Otherwise, there are specific identifiers that need to be sent.  Create the post
1541              * data argument with the name <code>javax.faces.partial.execute</code> and the value as a
1542              * space delimited <code>string</code> of client identifiers.</li>
1543              * </ul>
1544              * </li>
1545              * <li>If <code>options.execute</code> does not exist, create the post data argument with the
1546              * name <code>javax.faces.partial.execute</code> and the value as the identifier of the
1547              * element that caused this request.</li>
1548              * <li>If <code>options.render</code> exists:
1549              * <ul>
1550              * <li>If the keyword <code>@none</code> is present, do not create and send
1551              * the post data argument <code>javax.faces.partial.render</code>.</li>
1552              * <li>If the keyword <code>@all</code> is present, create the post data argument with
1553              * the name <code>javax.faces.partial.render</code> and the value <code>@all</code>.</li>
1554              * <li>Otherwise, there are specific identifiers that need to be sent.  Create the post
1555              * data argument with the name <code>javax.faces.partial.render</code> and the value as a
1556              * space delimited <code>string</code> of client identifiers.</li>
1557              * </ul>
1558              * <li>If <code>options.render</code> does not exist do not create and send the
1559              * post data argument <code>javax.faces.partial.render</code>.</li>
1560              * <li>Determine additional arguments (if any) from the <code>event</code>
1561              * argument.  The following name/value pairs may be used from the
1562              * <code>event</code> object:
1563              * <ul>
1564              * <li><code>target</code> - the ID of the element that triggered the event.</li>
1565              * <li><code>captured</code> - the ID of the element that captured the event.</li>
1566              * <li><code>type</code> - the type of event (ex: onkeypress)</li>
1567              * <li><code>alt</code> - <code>true</code> if ALT key was pressed.</li>
1568              * <li><code>ctrl</code> - <code>true</code> if CTRL key was pressed.</li>
1569              * <li><code>shift</code> - <code>true</code> if SHIFT key was pressed. </li>
1570              * <li><code>meta</code> - <code>true</code> if META key was pressed. </li>
1571              * <li><code>right</code> - <code>true</code> if right mouse button
1572              * was pressed. </li>
1573              * <li><code>left</code> - <code>true</code> if left mouse button
1574              * was pressed. </li>
1575              * <li><code>keycode</code> - the key code.
1576              * </ul>
1577              * </li>
1578              * </ul>
1579              * </li>
1580              * <li>Encode the set of post data arguments.</li>
1581              * <li>Join the encoded view state with the encoded set of post data arguments
1582              * to form the <code>query string</code> that will be sent to the server.</li>
1583              * <li>Create a request <code>context</code> object and set the properties:
1584              * <ul><li><code>source</code> (the source DOM element for this request)</li>
1585              * <li><code>onerror</code> (the error handler for this request)</li>
1586              * <li><code>onevent</code> (the event handler for this request)</li></ul>
1587              * The request context will be used during error/event handling.</li>
1588              * <li>Send a <code>begin</code> event following the procedure as outlined
1589              * in the Chapter 13 "Sending Events" section of the spec prose document <a
1590              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
1591              *  overview summary</a></li>
1592              * <li>Set the request header with the name: <code>Faces-Request</code> and the
1593              * value: <code>partial/ajax</code>.</li>
1594              * <li>Determine the <code>posting URL</code> as follows: If the hidden field
1595              * <code>javax.faces.encodedURL</code> is present in the submitting form, use its
1596              * value as the <code>posting URL</code>.  Otherwise, use the <code>action</code>
1597              * property of the <code>form</code> element as the <code>URL</code>.</li>
1598              * <li>Send the request as an <code>asynchronous POST</code> using the
1599              * <code>posting URL</code> that was determined in the previous step.</li>
1600              * </ul>
1601              * Before the request is sent it must be put into a queue to ensure requests
1602              * are sent in the same order as when they were initiated.  The request callback function
1603              * must examine the queue and determine the next request to be sent.  The behavior of the
1604              * request callback function must be as follows:
1605              * <ul>
1606              * <li>If the request completed successfully invoke {@link jsf.ajax.response}
1607              * passing the <code>request</code> object.</li>
1608              * <li>If the request did not complete successfully, notify the client.</li>
1609              * <li>Regardless of the outcome of the request (success or error) every request in the
1610              * queue must be handled.  Examine the status of each request in the queue starting from
1611              * the request that has been in the queue the longest.  If the status of the request is
1612              * <code>complete</code> (readyState 4), dequeue the request (remove it from the queue).
1613              * If the request has not been sent (readyState 0), send the request.  Requests that are
1614              * taken off the queue and sent should not be put back on the queue.</li>
1615              * </ul>
1616              *
1617              * </p>
1618              *
1619              * @param source The DOM element that triggered this Ajax request, or an id string of the
1620              * element to use as the triggering element.
1621              * @param event The DOM event that triggered this Ajax request.  The
1622              * <code>event</code> argument is optional.
1623              * @param options The set of available options that can be sent as
1624              * request parameters to control client and/or server side
1625              * request processing. Acceptable name/value pair options are:
1626              * <table border="1">
1627              * <tr>
1628              * <th>name</th>
1629              * <th>value</th>
1630              * </tr>
1631              * <tr>
1632              * <td><code>execute</code></td>
1633              * <td><code>space seperated list of client identifiers</code></td>
1634              * </tr>
1635              * <tr>
1636              * <td><code>render</code></td>
1637              * <td><code>space seperated list of client identifiers</code></td>
1638              * </tr>
1639              * <tr>
1640              * <td><code>onevent</code></td>
1641              * <td><code>function to callback for event</code></td>
1642              * </tr>
1643              * <tr>
1644              * <td><code>onerror</code></td>
1645              * <td><code>function to callback for error</code></td>
1646              * </tr>
1647              * <tr>
1648              * <td><code>params</code></td>
1649              * <td><code>object containing parameters to include in the request</code></td>
1650              * </tr>
1651              * </table>
1652              * The <code>options</code> argument is optional.
1653              * @member jsf.ajax
1654              * @function jsf.ajax.request
1655              * @throws Error if first required argument <code>element</code> is not specified
1656              */
1657             request: function request(source, event, options) {
1658 
1659                 var element, form;   //  Element variables
1660                 var all, none;
1661 
1662                 if (typeof source === 'undefined' || source === null) {
1663                     throw new Error("jsf.ajax.request: source not set");
1664                 }
1665 
1666                 // set up the element based on source
1667                 if (typeof source === 'string') {
1668                     element = document.getElementById(source);
1669                 } else if (typeof source === 'object') {
1670                     element = source;
1671                 } else {
1672                     throw new Error("jsf.request: source must be object or string");
1673                 }
1674                 // attempt to handle case of name unset
1675                 // this might be true in a badly written composite component
1676                 if (!element.name) {
1677                     element.name = element.id;
1678                 }
1679 
1680                 if (typeof(options) === 'undefined' || options === null) {
1681                     options = {};
1682                 }
1683 
1684                 // Error handler for this request
1685                 var onerror = false;
1686 
1687                 if (options.onerror && typeof options.onerror === 'function') {
1688                     onerror = options.onerror;
1689                 } else if (options.onerror && typeof options.onerror !== 'function') {
1690                     throw new Error("jsf.ajax.request: Added an onerror callback that was not a function");
1691                 }
1692 
1693                 // Event handler for this request
1694                 var onevent = false;
1695 
1696                 if (options.onevent && typeof options.onevent === 'function') {
1697                     onevent = options.onevent;
1698                 } else if (options.onevent && typeof options.onevent !== 'function') {
1699                     throw new Error("jsf.ajax.request: Added an onevent callback that was not a function");
1700                 }
1701 
1702                 form = getForm(element);
1703                 if (!form) {
1704                     throw new Error("jsf.ajax.request: Method must be called within a form");
1705                 }
1706                 var viewState = jsf.getViewState(form);
1707 
1708                 // Set up additional arguments to be used in the request..
1709                 // Make sure "javax.faces.source" is set up.
1710                 // If there were "execute" ids specified, make sure we
1711                 // include the identifier of the source element in the
1712                 // "execute" list.  If there were no "execute" ids
1713                 // specified, determine the default.
1714 
1715                 var args = {};
1716 
1717                 args["javax.faces.source"] = element.id;
1718 
1719                 if (event && !!event.type) {
1720                     args["javax.faces.partial.event"] = event.type;
1721                 }
1722 
1723                 // If we have 'execute' identifiers:
1724                 // Handle any keywords that may be present.
1725                 // If @none present anywhere, do not send the
1726                 // "javax.faces.partial.execute" parameter.
1727                 // The 'execute' and 'render' lists must be space
1728                 // delimited.
1729 
1730                 if (options.execute) {
1731                     none = options.execute.search(/@none/);
1732                     if (none < 0) {
1733                         all = options.execute.search(/@all/);
1734                         if (all < 0) {
1735                             options.execute = options.execute.replace("@this", element.id);
1736                             options.execute = options.execute.replace("@form", form.id);
1737                             var temp = options.execute.split(' ');
1738                             if (!isInArray(temp, element.name)) {
1739                                 options.execute = element.name + " " + options.execute;
1740                             }
1741                         } else {
1742                             options.execute = "@all";
1743                         }
1744                         args["javax.faces.partial.execute"] = options.execute;
1745                     }
1746                 } else {
1747                     options.execute = element.name + " " + element.id;
1748                     args["javax.faces.partial.execute"] = options.execute;
1749                 }
1750 
1751                 if (options.render) {
1752                     none = options.render.search(/@none/);
1753                     if (none < 0) {
1754                         all = options.render.search(/@all/);
1755                         if (all < 0) {
1756                             options.render = options.render.replace("@this", element.id);
1757                             options.render = options.render.replace("@form", form.id);
1758                         } else {
1759                             options.render = "@all";
1760                         }
1761                         args["javax.faces.partial.render"] = options.render;
1762                     }
1763                 }
1764 
1765                 // remove non-passthrough options
1766                 delete options.execute;
1767                 delete options.render;
1768                 delete options.onerror;
1769                 delete options.onevent;
1770                 // copy all other options to args
1771                 for (var property in options) {
1772                     if (options.hasOwnProperty(property)) {
1773                         args[property] = options[property];
1774                     }
1775                 }
1776 
1777                 args["javax.faces.partial.ajax"] = "true";
1778                 args["method"] = "POST";
1779 
1780                 // Determine the posting url
1781 
1782                 var encodedUrlField = form.elements["javax.faces.encodedURL"];
1783                 if (typeof encodedUrlField == 'undefined') {
1784                     args["url"] = form.action;
1785                 } else {
1786                     args["url"] = encodedUrlField.value;
1787                 }
1788 
1789                 var ajaxEngine = new AjaxEngine();
1790                 ajaxEngine.setupArguments(args);
1791                 ajaxEngine.queryString = viewState;
1792                 ajaxEngine.context.onevent = onevent;
1793                 ajaxEngine.context.onerror = onerror;
1794                 ajaxEngine.context.sourceid = element.id;
1795                 ajaxEngine.context.formid = form.id;
1796                 ajaxEngine.context.render = args["javax.faces.partial.render"];
1797                 ajaxEngine.sendRequest();
1798 
1799                 // null out element variables to protect against IE memory leak
1800                 element = null;
1801                 form = null;
1802 
1803             },
1804             /**
1805              * <p>Receive an Ajax response from the server.
1806              * <p><b>Usage:</b></p>
1807              * <pre><code>
1808              * jsf.ajax.response(request, context);
1809              * </pre></code>
1810              * <p><b>Implementation Requirements:</b></p>
1811              * This function must evaluate the markup returned in the
1812              * <code>request.responseXML</code> object and perform the following action:
1813              * <ul>
1814              * <p>If there is no XML response returned, signal an <code>emptyResponse</code>
1815              * error. If the XML response does not follow the format as outlined
1816              * in Appendix A of the spec prose document <a
1817              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
1818              *  overview summary</a> signal a <code>malformedError</code> error.  Refer to
1819              * section "Signaling Errors" in Chapter 13 of the spec prose document <a
1820              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
1821              *  overview summary</a>.</p>
1822              * <p>If the response was successfully processed, send a <code>success</code>
1823              * event as outlined in Chapter 13 "Sending Events" section of the spec prose
1824              * document <a
1825              * href="../../javadocs/overview-summary.html#prose_document">linked in the
1826              * overview summary</a>.</p>
1827              * <p><i>Update Element Processing</i></p>
1828              * <li>If an <code>update</code> element is found in the response
1829              * with the identifier <code>javax.faces.ViewRoot</code>:
1830              * <pre><code><update id="javax.faces.ViewRoot">
1831              *    <![CDATA[...]]>
1832              * </update></code></pre>
1833              * Update the entire DOM replacing the appropriate <code>head</code> and/or
1834              * <code>body</code> sections with the content from the response.</li>
1835              * <li>If an <code>update</code> element is found in the response with the identifier
1836              * <code>javax.faces.ViewState</code>:
1837              * <pre><code><update id="javax.faces.ViewState">
1838              *    <![CDATA[...]]>
1839              * </update></code></pre>
1840              * locate and update the submitting form's <code>javax.faces.ViewState</code> value
1841              * with the <code>CDATA</code> contents from the response.</li>
1842              * <li>If an <code>update</code> element is found in the response with the identifier
1843              * <code>javax.faces.ViewHead</code>:
1844              * <pre><code><update id="javax.faces.ViewHead">
1845              *    <![CDATA[...]]>
1846              * </update></code></pre>
1847              * update the document's <code>head</code> section with the <code>CDATA</code>
1848              * contents from the response.</li>
1849              * <li>If an <code>update</code> element is found in the response with the identifier
1850              * <code>javax.faces.ViewBody</code>:
1851              * <pre><code><update id="javax.faces.ViewBody">
1852              *    <![CDATA[...]]>
1853              * </update></code></pre>
1854              * update the document's <code>body</code> section with the <code>CDATA</code>
1855              * contents from the response.</li>
1856              * <li>For any other <code><update></code> element:
1857              * <pre><code><update id="update id">
1858              *    <![CDATA[...]]>
1859              * </update></code></pre>
1860              * Find the DOM element with the identifier that matches the
1861              * <code><update></code> element identifier, and replace its contents with
1862              * the <code><update></code> element's <code>CDATA</code> contents.</li>
1863              * </li>
1864              * <p><i>Insert Element Processing</i></p>
1865              * <li>If an <code><insert></code> element is found in the response with the
1866              * attribute <code>before</code>:
1867              * <pre><code><insert id="insert id" before="before id">
1868              *    <![CDATA[...]]>
1869              * </insert></code></pre>
1870              * <ul>
1871              * <li>Extract this <code><insert></code> element's <code>CDATA</code> contents
1872              * from the response.</li>
1873              * <li>Find the DOM element whose identifier matches <code>before id</code> and insert
1874              * the <code><insert></code> element's <code>CDATA</code> content before
1875              * the DOM element in the document.</li>
1876              * </ul>
1877              * </li>
1878              * <li>If an <code><insert></code> element is found in the response with the
1879              * attribute <code>after</code>:
1880              * <pre><code><insert id="insert id" after="after id">
1881              *    <![CDATA[...]]>
1882              * </insert></code></pre>
1883              * <ul>
1884              * <li>Extract this <code><insert></code> element's <code>CDATA</code> contents
1885              * from the response.</li>
1886              * <li>Find the DOM element whose identifier matches <code>after id</code> and insert
1887              * the <code><insert></code> element's <code>CDATA</code> content after
1888              * the DOM element in the document.</li>
1889              * </ul>
1890              * </li>
1891              * <p><i>Delete Element Processing</i></p>
1892              * <li>If a <code><delete></code> element is found in the response:
1893              * <pre><code><delete id="delete id"/></code></pre>
1894              * Find the DOM element whose identifier matches <code>delete id</code> and remove it
1895              * from the DOM.</li>
1896              * <p><i>Element Attribute Update Processing</i></p>
1897              * <li>If an <code><attributes></code> element is found in the response:
1898              * <pre><code><attributes id="id of element with attribute">
1899              *    <attribute name="attribute name" value="attribute value">
1900              *    ...
1901              * </attributes></code></pre>
1902              * <ul>
1903              * <li>Find the DOM element that matches the <code><attributes></code> identifier.</li>
1904              * <li>For each nested <code><attribute></code> element in <code><attribute></code>,
1905              * update the DOM element attribute value (whose name matches <code>attribute name</code>),
1906              * with <code>attribute value</code>.</li>
1907              * </ul>
1908              * </li>
1909              * <p><i>JavaScript Processing</i></p>
1910              * <li>If an <code><eval></code> element is found in the response:
1911              * <pre><code><eval>
1912              *    <![CDATA[...JavaScript...]]>
1913              * </eval></code></pre>
1914              * <ul>
1915              * <li>Extract this <code><eval></code> element's <code>CDATA</code> contents
1916              * from the response and execute it as if it were JavaScript code.</li>
1917              * </ul>
1918              * </li>
1919              * <p><i>Redirect Processing</i></p>
1920              * <li>If a <code><redirect></code> element is found in the response:
1921              * <pre><code><redirect url="redirect url"/></code></pre>
1922              * Cause a redirect to the url <code>redirect url</code>.</li>
1923              * <p><i>Error Processing</i></p>
1924              * <li>If an <code><error></code> element is found in the response:
1925              * <pre><code><error>
1926              *    <error-name>..fully qualified class name string...<error-name>
1927              *    <error-message><![CDATA[...]]><error-message>
1928              * </error></code></pre>
1929              * Extract this <code><error></code> element's <code>error-name</code> contents
1930              * and the <code>error-message</code> contents. Signal a <code>serverError</code> passing
1931              * the <code>errorName</code> and <code>errorMessage</code>.  Refer to
1932              * section "Signaling Errors" in Chapter 13 of the spec prose document <a
1933              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
1934              *  overview summary</a>.</li>
1935              * <p><i>Extensions</i></p>
1936              * <li>The <code><extensions></code> element provides a way for framework
1937              * implementations to provide their own information.</li>
1938              * </ul>
1939              *
1940              * </p>
1941              *
1942              * @param request The <code>XMLHttpRequest</code> instance that
1943              * contains the status code and response message from the server.
1944              *
1945              * @param context An object containing the request context, including the following properties:
1946              * the source element, per call onerror callback function, and per call onevent callback function.
1947              *
1948              * @throws  Error if request contains no data
1949              *
1950              * @function jsf.ajax.response
1951              */
1952             response: function response(request, context) {
1953                 if (!request) {
1954                     throw new Error("jsf.ajax.response: Request parameter is unset");
1955                 }
1956 
1957                 var xml = request.responseXML;
1958                 if (xml === null) {
1959                     sendError(request, context, "emptyResponse");
1960                     return;
1961                 }
1962 
1963                 if (getParseErrorText(xml) !== PARSED_OK) {
1964                     sendError(request, context, "malformedXML");
1965                     return;
1966                 }
1967 
1968                 var responseType = xml.getElementsByTagName("partial-response")[0].firstChild;
1969 
1970                 if (responseType.nodeName === "error") { // it's an error
1971                     var errorName = responseType.firstChild.firstChild.nodeValue;
1972                     var errorMessage = responseType.firstChild.nextSibling.firstChild.nodeValue;
1973                     sendError(request, context, "serverError", null, errorName, errorMessage);
1974                     return;
1975                 }
1976 
1977 
1978                 if (responseType.nodeName === "redirect") {
1979                     window.location = responseType.getAttribute("url");
1980                     return;
1981                 }
1982 
1983 
1984                 if (responseType.nodeName !== "changes") {
1985                     sendError(request, context, "malformedXML", "Top level node must be one of: changes, redirect, error, received: " + responseType.nodeName + " instead.");
1986                     return;
1987                 }
1988 
1989 
1990                 var changes = responseType.childNodes;
1991 
1992                 try {
1993                     for (var i = 0; i < changes.length; i++) {
1994                         switch (changes[i].nodeName) {
1995                             case "update":
1996                                 doUpdate(changes[i], context);
1997                                 break;
1998                             case "delete":
1999                                 doDelete(changes[i]);
2000                                 break;
2001                             case "insert":
2002                                 doInsert(changes[i]);
2003                                 break;
2004                             case "attributes":
2005                                 doAttributes(changes[i]);
2006                                 break;
2007                             case "eval":
2008                                 doEval(changes[i]);
2009                                 break;
2010                             case "extension":
2011                                 // no action
2012                                 break;
2013                             default:
2014                                 sendError(request, context, "malformedXML", "Changes allowed are: update, delete, insert, attributes, eval, extension.  Received " + changes[i].nodeName + " instead.");
2015                                 return;
2016                         }
2017                     }
2018                 } catch (ex) {
2019                     sendError(request, context, "malformedXML", ex.message);
2020                     return;
2021                 }
2022                 sendEvent(request, context, "success");
2023 
2024             }
2025         };
2026     }();
2027 
2028     /**
2029      *
2030      * <p>Return the value of <code>Application.getProjectStage()</code> for
2031      * the currently running application instance.  Calling this method must
2032      * not cause any network transaction to happen to the server.</p>
2033      * <p><b>Usage:</b></p>
2034      * <pre><code>
2035      * var stage = jsf.getProjectStage();
2036      * if (stage === ProjectStage.Development) {
2037      *  ...
2038      * } else if stage === ProjectStage.Production) {
2039      *  ...
2040      * }
2041      * </code></pre>
2042      *
2043      * @returns String <code>String</code> representing the current state of the
2044      * running application in a typical product development lifecycle.  Refer
2045      * to <code>javax.faces.application.Application.getProjectStage</code> and
2046      * <code>javax.faces.application.ProjectStage</code>.
2047      * @function jsf.getProjectStage
2048      */
2049     jsf.getProjectStage = function() {
2050         // First, return cached value if available
2051         if (typeof mojarra !== 'undefined' && typeof mojarra.projectStageCache !== 'undefined') {
2052             return mojarra.projectStageCache;
2053         }
2054         var scripts = document.getElementsByTagName("script"); // nodelist of scripts
2055         var script; // jsf.js script
2056         var s = 0; // incremental variable for for loop
2057         var stage; // temp value for stage
2058         var match; // temp value for match
2059         while (s < scripts.length) {
2060             if (typeof scripts[s].src === 'string' && scripts[s].src.match('\/javax\.faces\.resource\/jsf\.js\?.*ln=javax\.faces')) {
2061                 script = scripts[s].src;
2062                 break;
2063             }
2064             s++;
2065         }
2066         if (typeof script == "string") {
2067             match = script.match("stage=(.*)");
2068             if (match) {
2069                 stage = match[1];
2070             }
2071         }
2072         if (typeof stage === 'undefined' || !stage) {
2073             stage = "Production";
2074         }
2075 
2076         mojarra = mojarra || {};
2077         mojarra.projectStageCache = stage;
2078 
2079         return mojarra.projectStageCache;
2080     };
2081 
2082 
2083     /**
2084      * <p>Collect and encode state for input controls associated
2085      * with the specified <code>form</code> element.  This will include
2086      * all input controls of type <code>hidden</code>.</p>
2087      * <p><b>Usage:</b></p>
2088      * <pre><code>
2089      * var state = jsf.getViewState(form);
2090      * </pre></code>
2091      *
2092      * @param form The <code>form</code> element whose contained
2093      * <code>input</code> controls will be collected and encoded.
2094      * Only successful controls will be collected and encoded in
2095      * accordance with: <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2">
2096      * Section 17.13.2 of the HTML Specification</a>.
2097      *
2098      * @returns String The encoded state for the specified form's input controls.
2099      * @function jsf.getViewState
2100      */
2101     jsf.getViewState = function(form) {
2102         if (!form) {
2103             throw new Error("jsf.getViewState:  form must be set");
2104         }
2105         var els = form.elements;
2106         var len = els.length;
2107         // create an array which we'll use to hold all the intermediate strings
2108         // this bypasses a problem in IE when repeatedly concatenating very
2109         // large strings - we'll perform the concatenation once at the end
2110         var qString = [];
2111         var addField = function(name, value) {
2112             var tmpStr = "";
2113             if (qString.length > 0) {
2114                 tmpStr = "&";
2115             }
2116             tmpStr += encodeURIComponent(name) + "=" + encodeURIComponent(value);
2117             qString.push(tmpStr);
2118         };
2119         for (var i = 0; i < len; i++) {
2120             var el = els[i];
2121             if (!el.disabled) {
2122                 switch (el.type) {
2123                     case 'text':
2124                     case 'password':
2125                     case 'hidden':
2126                     case 'textarea':
2127                         addField(el.name, el.value);
2128                         break;
2129                     case 'select-one':
2130                         if (el.selectedIndex >= 0) {
2131                             addField(el.name, el.options[el.selectedIndex].value);
2132                         }
2133                         break;
2134                     case 'select-multiple':
2135                         for (var j = 0; j < el.options.length; j++) {
2136                             if (el.options[j].selected) {
2137                                 addField(el.name, el.options[j].value);
2138                             }
2139                         }
2140                         break;
2141                     case 'checkbox':
2142                     case 'radio':
2143                         if (el.checked) {
2144                             addField(el.name, el.value);
2145                         }
2146                         break;
2147                 }
2148             }
2149         }
2150         // concatenate the array
2151         return qString.join("");
2152     };
2153 
2154     /**
2155      * The namespace for JavaServer Faces JavaScript utilities.
2156      * @name jsf.util
2157      * @namespace
2158      */
2159     jsf.util = {};
2160 
2161     /**
2162      * <p>A varargs function that invokes an arbitrary number of scripts.
2163      * If any script in the chain returns false, the chain is short-circuited
2164      * and subsequent scripts are not invoked.  Any number of scripts may
2165      * specified after the <code>event</code> argument.</p>
2166      *
2167      * @param source The DOM element that triggered this Ajax request, or an
2168      * id string of the element to use as the triggering element.
2169      * @param event The DOM event that triggered this Ajax request.  The
2170      * <code>event</code> argument is optional.
2171      *
2172      * @function jsf.util.chain
2173      */
2174     jsf.util.chain = function(source, event) {
2175 
2176         if (arguments.length < 3) {
2177             return;
2178         }
2179 
2180         // RELEASE_PENDING rogerk - shouldn't this be getElementById instead of null
2181         var thisArg = (typeof source === 'object') ? source : null;
2182 
2183         // Call back any scripts that were passed in
2184         for (var i = 2; i < arguments.length; i++) {
2185 
2186             var f = new Function("event", arguments[i]);
2187             var returnValue = f.call(thisArg, event);
2188 
2189             if (returnValue === false) {
2190                 break;
2191             }
2192         }
2193     };
2194 
2195     /**
2196      * <p>An integer specifying the specification version that this file implements.
2197      * It's format is: rightmost two digits, bug release number, next two digits,
2198      * minor release number, leftmost digits, major release number.
2199      * This number may only be incremented by a new release of the specification.</p>
2200      */
2201     jsf.specversion = 20000;
2202 
2203     /**
2204      * <p>An integer specifying the implementation version that this file implements.
2205      * It's a monotonically increasing number, reset with every increment of
2206      * <code>jsf.specversion</code>
2207      * This number is implementation dependent.</p>
2208      */
2209     jsf.implversion = 3;
2210 
2211 
2212 } //end if version detection block
2213