1 /*
  2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3  *
  4  * Copyright 1997-2011 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 >= 3))) {
 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 isAutoExec = function isAutoExec() {
116             try {
117                 if (typeof isAutoExecCache !== "undefined") {
118                     return isAutoExecCache;
119                 }
120                 var autoExecTestString = "<script>var mojarra = mojarra || {};mojarra.autoExecTest = true;</script>";
121                 var tempElement = document.createElement('span');
122                 tempElement.innerHTML = autoExecTestString;
123                 var body = document.getElementsByTagName('body')[0];
124                 var tempNode = body.appendChild(tempElement);
125                 if (mojarra && mojarra.autoExecTest) {
126                     isAutoExecCache = true;
127                     delete mojarra.autoExecTest;
128                 } else {
129                     isAutoExecCache = false;
130                 }
131                 deleteNode(tempNode);
132                 return isAutoExecCache;
133             } catch (ex) {
134                 // OK, that didn't work, we'll have to make an assumption
135                 if (typeof isAutoExecCache === "undefined") {
136                     isAutoExecCache = false;
137                 }
138                 return isAutoExecCache;
139             }
140         };
141         var isAutoExecCache;
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 
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 (!node.parentNode) {
548                 // if there's no parent, there's nothing to do
549                 return;
550             }
551             if (!isIE()) {
552                 // nothing special required
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          * @ignore
641          */
642         var replaceNode = function replaceNode(newNode, node) {
643                if(isIE()){
644                     node.parentNode.insertBefore(newNode, node);
645                     deleteNode(node);
646                } else {
647                     node.parentNode.replaceChild(newNode, node);
648                }
649         };
650 
651 
652         /**
653          * copy all attributes from one element to another - except id
654          * @param target element to copy attributes to
655          * @param source element to copy attributes from
656          * @ignore
657          */
658         var cloneAttributes = function cloneAttributes(target, source) {
659 
660             // enumerate core element attributes - without 'dir' as special case
661             var coreElementAttributes = ['className', 'title', 'lang', 'xml:lang'];
662 
663             // Enumerate additional input element attributes
664             var inputElementAttributes =
665                     [   'name', 'value', 'checked', 'disabled', 'readOnly',
666                         'size', 'maxLength', 'src', 'alt', 'useMap', 'isMap',
667                         'tabIndex', 'accessKey', 'accept', 'type'
668                     ];
669 
670             // Enumerate all the names of the event listeners
671             var listenerNames =
672                     [ 'onclick', 'ondblclick', 'onmousedown', 'onmousemove', 'onmouseout',
673                         'onmouseover', 'onmouseup', 'onkeydown', 'onkeypress', 'onkeyup',
674                         'onhelp', 'onblur', 'onfocus', 'onchange', 'onload', 'onunload', 'onabort',
675                         'onreset', 'onselect', 'onsubmit'
676                     ];
677 
678             var iIndex, iLength; // for loop variables
679             var attributeName; // name of the attribute to set
680             var newValue, oldValue; // attribute values in each element
681 
682             // First, copy over core attributes
683             for (iIndex = 0,iLength = coreElementAttributes.length; iIndex < iLength; iIndex++) {
684                 attributeName = coreElementAttributes[iIndex];
685                 newValue = source[attributeName];
686                 oldValue = target[attributeName];
687                 if (oldValue != newValue) {
688                     target[attributeName] = newValue;
689                 }
690             }
691 
692             // Next, if it's an input, copy those over
693             if (target.nodeName.toLowerCase() === 'input') {
694                 for (iIndex = 0,iLength = inputElementAttributes.length; iIndex < iLength; iIndex++) {
695                     attributeName = inputElementAttributes[iIndex];
696                     newValue = source[attributeName];
697                     oldValue = target[attributeName];
698                     if (oldValue != newValue) {
699                         target[attributeName] = newValue;
700                     }
701                 }
702             }
703             //'style' attribute special case
704             var newStyle = source.getAttribute('style');
705             var oldStyle = target.getAttribute('style');
706             if (newStyle != oldStyle) {
707                 if (isIE()) {
708                     target.style.setAttribute('cssText', newStyle, 0);
709                 } else {
710                     target.setAttribute('style',newStyle);
711                 }
712             }
713             for (var lIndex = 0, lLength = listenerNames.length; lIndex < lLength; lIndex++) {
714                 var name = listenerNames[lIndex];
715                 target[name] = source[name] ? source[name] : null;
716                 if (source[name]) {
717                     source[name] = null;
718                 }
719             }
720             // Special case for 'dir' attribute
721             if (!isIE() && source.dir != target.dir) {
722                 target.dir = source.dir ? source.dir : null;
723             }
724         };
725 
726         /**
727          * Replace an element from one document into another
728          * @param newElement new element to put in document
729          * @param origElement original element to replace
730          * @ignore
731          */
732         var elementReplace = function elementReplace(newElement, origElement) {
733             copyChildNodes(newElement, origElement);
734             // sadly, we have to reparse all over again
735             // to reregister the event handlers and styles
736             // PENDING do some performance tests on large pages
737             origElement.innerHTML = origElement.innerHTML;
738 
739             try {
740                 cloneAttributes(origElement, newElement);
741             } catch (ex) {
742                 // if in dev mode, report an error, else try to limp onward
743                 if (jsf.getProjectStage() == "Development") {
744                     throw new Error("Error updating attributes");
745                 }
746             }
747             deleteNode(newElement);
748 
749         };
750 
751         /**
752          * Create a new document, then select the body element within it
753          * @param docStr Stringified version of document to create
754          * @return element the body element
755          * @ignore
756          */
757         var getBodyElement = function getBodyElement(docStr) {
758 
759             var doc;  // intermediate document we'll create
760             var body; // Body element to return
761 
762             if (typeof DOMParser !== "undefined") {  // FF, S, Chrome
763                 doc = (new DOMParser()).parseFromString(docStr, "text/xml");
764             } else if (typeof ActiveXObject !== "undefined") { // IE
765                 doc = new ActiveXObject("MSXML2.DOMDocument");
766                 doc.loadXML(docStr);
767             } else {
768                 throw new Error("You don't seem to be running a supported browser");
769             }
770 
771             if (getParseErrorText(doc) !== PARSED_OK) {
772                 throw new Error(getParseErrorText(doc));
773             }
774 
775             body = doc.getElementsByTagName("body")[0];
776 
777             if (!body) {
778                 throw new Error("Can't find body tag in returned document.");
779             }
780 
781             return body;
782         };
783 
784         /**
785          * Do update.
786          * @param element element to update
787          * @param context context of request
788          * @ignore
789          */
790         var doUpdate = function doUpdate(element, context) {
791             var id, content, markup, state;
792             var stateForm;
793             var scripts = []; // temp holding value for array of script nodes
794 
795             id = element.getAttribute('id');
796             if (id === "javax.faces.ViewState") {
797 
798                 state = element.firstChild;
799 
800                 // Now set the view state from the server into the DOM
801                 // but only for the form that submitted the request.
802 
803                 stateForm = document.getElementById(context.formid);
804                 if (!stateForm || !stateForm.elements) {
805                     // if the form went away for some reason, or it lacks elements 
806                     // we're going to just return silently.
807                     return;
808                 }
809                 var field = stateForm.elements["javax.faces.ViewState"];
810                 if (typeof field == 'undefined') {
811                     field = document.createElement("input");
812                     field.type = "hidden";
813                     field.name = "javax.faces.ViewState";
814                     stateForm.appendChild(field);
815                 }
816                 field.value = state.nodeValue;
817 
818                 // Now set the view state from the server into the DOM
819                 // for any form that is a render target.
820 
821                 if (typeof context.render !== 'undefined' && context.render !== null) {
822                     var temp = context.render.split(' ');
823                     for (var i = 0; i < temp.length; i++) {
824                         if (temp.hasOwnProperty(i)) {
825                             // See if the element is a form and
826                             // the form is not the one that caused the submission..
827                             var f = document.forms[temp[i]];
828                             if (typeof f !== 'undefined' && f !== null && f.id !== context.formid) {
829                                 field = f.elements["javax.faces.ViewState"];
830                                 if (typeof field === 'undefined') {
831                                     field = document.createElement("input");
832                                     field.type = "hidden";
833                                     field.name = "javax.faces.ViewState";
834                                     f.appendChild(field);
835                                 }
836                                 field.value = state.nodeValue;
837                             }
838                         }
839                     }
840                 }
841                 return;
842             }
843 
844             // join the CDATA sections in the markup
845             markup = '';
846             for (var j = 0; j < element.childNodes.length; j++) {
847                 content = element.childNodes[j];
848                 markup += content.nodeValue;
849             }
850 
851             var src = markup;
852 
853             // If our special render all markup is present..
854             if (id === "javax.faces.ViewRoot" || id === "javax.faces.ViewBody") {
855                 var bodyStartEx = new RegExp("< *body[^>]*>", "gi");
856                 var bodyEndEx = new RegExp("< */ *body[^>]*>", "gi");
857                 var newsrc;
858 
859                 var docBody = document.getElementsByTagName("body")[0];
860                 var bodyStart = bodyStartEx.exec(src);
861 
862                 if (bodyStart !== null) { // replace body tag
863                     // First, try with XML manipulation
864                     try {
865                         // Get scripts from text
866                         scripts = stripScripts(src);
867                         // Remove scripts from text
868                         newsrc = src.replace(/<script[^>]*>([\S\s]*?)<\/script>/igm, "");
869                         elementReplace(getBodyElement(newsrc), docBody);
870                         runScripts(scripts);
871                     } catch (e) {
872                         // OK, replacing the body didn't work with XML - fall back to quirks mode insert
873                         var srcBody, bodyEnd;
874                         // if src contains </body>
875                         bodyEnd = bodyEndEx.exec(src);
876                         if (bodyEnd !== null) {
877                             srcBody = src.substring(bodyStartEx.lastIndex,
878                                     bodyEnd.index);
879                         } else { // can't find the </body> tag, punt
880                             srcBody = src.substring(bodyStartEx.lastIndex);
881                         }
882                         // replace body contents with innerHTML - note, script handling happens within function
883                         elementReplaceStr(docBody, "body", srcBody);
884 
885                     }
886 
887                 } else {  // replace body contents with innerHTML - note, script handling happens within function
888                     elementReplaceStr(docBody, "body", src);
889                 }
890             } else if (id === "javax.faces.ViewHead") {
891                 throw new Error("javax.faces.ViewHead not supported - browsers cannot reliably replace the head's contents");
892             } else {
893                 var d = $(id);
894                 if (!d) {
895                     throw new Error("During update: " + id + " not found");
896                 }
897                 var parent = d.parentNode;
898                 // Trim space padding before assigning to innerHTML
899                 var html = src.replace(/^\s+/g, '').replace(/\s+$/g, '');
900                 var parserElement = document.createElement('div');
901                 var tag = d.nodeName.toLowerCase();
902                 var tableElements = ['td', 'th', 'tr', 'tbody', 'thead', 'tfoot'];
903                 var isInTable = false;
904                 for (var tei = 0, tel = tableElements.length; tei < tel; tei++) {
905                     if (tableElements[tei] == tag) {
906                         isInTable = true;
907                         break;
908                     }
909                 }
910                 if (isInTable) {
911 
912                     if (isAutoExec()) {
913                         // Create html
914                         parserElement.innerHTML = '<table>' + html + '</table>';
915                     } else {
916                         // Get the scripts from the text
917                         scripts = stripScripts(html);
918                         // Remove scripts from text
919                         html = html.replace(/<script[^>]*>([\S\s]*?)<\/script>/igm,"");
920                         parserElement.innerHTML = '<table>' + html + '</table>';
921                     }
922                     var newElement = parserElement.firstChild;
923                     //some browsers will also create intermediary elements such as table>tbody>tr>td
924                     while ((null !== newElement) && (id !== newElement.id)) {
925                         newElement = newElement.firstChild;
926                     }
927                     parent.replaceChild(newElement, d);
928                     runScripts(scripts);
929                 } else if (d.nodeName.toLowerCase() === 'input') {
930                     // special case handling for 'input' elements
931                     // in order to not lose focus when updating,
932                     // input elements need to be added in place.
933                     parserElement = document.createElement('div');
934                     parserElement.innerHTML = html;
935                     newElement = parserElement.firstChild;
936 
937                     cloneAttributes(d, newElement);
938                     deleteNode(parserElement);
939                 } else if (html.length > 0) {
940                     if (isAutoExec()) {
941                         // Create html
942                         parserElement.innerHTML = html;
943                     } else {
944                         // Get the scripts from the text
945                         scripts = stripScripts(html);
946                         // Remove scripts from text
947                         html = html.replace(/<script[^>]*>([\S\s]*?)<\/script>/igm,"");
948                         parserElement.innerHTML = html;
949                     }
950                     replaceNode(parserElement.firstChild, d);
951                     deleteNode(parserElement);
952                     runScripts(scripts);
953                 }
954             }
955         };
956 
957         /**
958          * Delete a node specified by the element.
959          * @param element
960          * @ignore
961          */
962         var doDelete = function doDelete(element) {
963             var id = element.getAttribute('id');
964             var target = $(id);
965             deleteNode(target);
966         };
967 
968         /**
969          * Insert a node specified by the element.
970          * @param element
971          * @ignore
972          */
973         var doInsert = function doInsert(element) {
974             var tablePattern = new RegExp("<\\s*(td|th|tr|tbody|thead|tfoot)", "i");
975             var scripts = [];
976             var target = $(element.firstChild.getAttribute('id'));
977             var parent = target.parentNode;
978             var html = element.firstChild.firstChild.nodeValue;
979             var isInTable = tablePattern.test(html);
980 
981             if (!isAutoExec())  {
982                 // Get the scripts from the text
983                 scripts = stripScripts(html);
984                 // Remove scripts from text
985                 html = html.replace(/<script[^>]*>([\S\s]*?)<\/script>/igm,"");
986             }
987             var tempElement = document.createElement('div');
988             var newElement = null;
989             if (isInTable)  {
990                 tempElement.innerHTML = '<table>' + html + '</table>';
991                 newElement = tempElement.firstChild;
992                 //some browsers will also create intermediary elements such as table>tbody>tr>td
993                 //test for presence of id on the new element since we do not have it directly
994                 while ((null !== newElement) && ("" == newElement.id)) {
995                     newElement = newElement.firstChild;
996                 }
997             } else {
998                 tempElement.innerHTML = html;
999                 newElement = tempElement.firstChild;
1000             }
1001 
1002             if (element.firstChild.nodeName === 'after') {
1003                 // Get the next in the list, to insert before
1004                 target = target.nextSibling;
1005             }  // otherwise, this is a 'before' element
1006             if (!!tempElement.innerHTML) { // check if only scripts were inserted - if so, do nothing here
1007                 parent.insertBefore(newElement, target);
1008             }
1009             runScripts(scripts);
1010             deleteNode(tempElement);
1011         };
1012 
1013         /**
1014          * Modify attributes of given element id.
1015          * @param element
1016          * @ignore
1017          */
1018         var doAttributes = function doAttributes(element) {
1019 
1020             // Get id of element we'll act against
1021             var id = element.getAttribute('id');
1022 
1023             var target = $(id);
1024 
1025             if (!target) {
1026                 throw new Error("The specified id: " + id + " was not found in the page.");
1027             }
1028 
1029             // There can be multiple attributes modified.  Loop through the list.
1030             var nodes = element.childNodes;
1031             for (var i = 0; i < nodes.length; i++) {
1032                 var name = nodes[i].getAttribute('name');
1033                 var value = nodes[i].getAttribute('value');
1034                 if (!isIE()) {
1035                     target.setAttribute(name, value);
1036                 } else { // if it's IE, then quite a bit more work is required
1037                     if (name === 'class') {
1038                         name = 'className';
1039                         target.setAttribute(name, value, 0);
1040                     } else if (name === "for") {
1041                         name = 'htmlFor';
1042                         target.setAttribute(name, value, 0);
1043                     } else if (name === 'style') {
1044                         target.style.setAttribute('cssText', value, 0);
1045                     } else if (name.substring(0, 2) === 'on') {
1046                         var fn = function(value) {
1047                             return function() {
1048                                 window.execScript(value);
1049                             };
1050                         }(value);
1051                         target.setAttribute(name, fn, 0);
1052                     } else if (name === 'dir') {
1053                         if (jsf.getProjectStage() == 'Development') {
1054                             throw new Error("Cannot set 'dir' attribute in IE");
1055                         }
1056                     } else {
1057                         target.setAttribute(name, value, 0);
1058                     }
1059                 }
1060             }
1061         };
1062 
1063         /**
1064          * Eval the CDATA of the element.
1065          * @param element to eval
1066          * @ignore
1067          */
1068         var doEval = function doEval(element) {
1069             var evalText = element.firstChild.nodeValue;
1070             globalEval(evalText);
1071         };
1072 
1073         /**
1074          * Ajax Request Queue
1075          * @ignore
1076          */
1077         var Queue = new function Queue() {
1078 
1079             // Create the internal queue
1080             var queue = [];
1081 
1082 
1083             // the amount of space at the front of the queue, initialised to zero
1084             var queueSpace = 0;
1085 
1086             /** Returns the size of this Queue. The size of a Queue is equal to the number
1087              * of elements that have been enqueued minus the number of elements that have
1088              * been dequeued.
1089              * @ignore
1090              */
1091             this.getSize = function getSize() {
1092                 return queue.length - queueSpace;
1093             };
1094 
1095             /** Returns true if this Queue is empty, and false otherwise. A Queue is empty
1096              * if the number of elements that have been enqueued equals the number of
1097              * elements that have been dequeued.
1098              * @ignore
1099              */
1100             this.isEmpty = function isEmpty() {
1101                 return (queue.length === 0);
1102             };
1103 
1104             /** Enqueues the specified element in this Queue.
1105              *
1106              * @param element - the element to enqueue
1107              * @ignore
1108              */
1109             this.enqueue = function enqueue(element) {
1110                 // Queue the request
1111                 queue.push(element);
1112             };
1113 
1114 
1115             /** Dequeues an element from this Queue. The oldest element in this Queue is
1116              * removed and returned. If this Queue is empty then undefined is returned.
1117              *
1118              * @returns Object The element that was removed from the queue.
1119              * @ignore
1120              */
1121             this.dequeue = function dequeue() {
1122                 // initialise the element to return to be undefined
1123                 var element = undefined;
1124 
1125                 // check whether the queue is empty
1126                 if (queue.length) {
1127                     // fetch the oldest element in the queue
1128                     element = queue[queueSpace];
1129 
1130                     // update the amount of space and check whether a shift should occur
1131                     if (++queueSpace * 2 >= queue.length) {
1132                         // set the queue equal to the non-empty portion of the queue
1133                         queue = queue.slice(queueSpace);
1134                         // reset the amount of space at the front of the queue
1135                         queueSpace = 0;
1136                     }
1137                 }
1138                 // return the removed element
1139                 try {
1140                     return element;
1141                 } finally {
1142                     element = null; // IE 6 leak prevention
1143                 }
1144             };
1145 
1146             /** Returns the oldest element in this Queue. If this Queue is empty then
1147              * undefined is returned. This function returns the same value as the dequeue
1148              * function, but does not remove the returned element from this Queue.
1149              * @ignore
1150              */
1151             this.getOldestElement = function getOldestElement() {
1152                 // initialise the element to return to be undefined
1153                 var element = undefined;
1154 
1155                 // if the queue is not element then fetch the oldest element in the queue
1156                 if (queue.length) {
1157                     element = queue[queueSpace];
1158                 }
1159                 // return the oldest element
1160                 try {
1161                     return element;
1162                 } finally {
1163                     element = null; //IE 6 leak prevention
1164                 }
1165             };
1166         }();
1167 
1168 
1169         /**
1170          * AjaxEngine handles Ajax implementation details.
1171          * @ignore
1172          */
1173         var AjaxEngine = function AjaxEngine() {
1174 
1175             var req = {};                  // Request Object
1176             req.url = null;                // Request URL
1177             req.context = {};              // Context of request and response
1178             req.context.sourceid = null;   // Source of this request
1179             req.context.onerror = null;    // Error handler for request
1180             req.context.onevent = null;    // Event handler for request
1181             req.context.formid = null;     // Form that's the context for this request
1182             req.xmlReq = null;             // XMLHttpRequest Object
1183             req.async = true;              // Default - Asynchronous
1184             req.parameters = {};           // Parameters For GET or POST
1185             req.queryString = null;        // Encoded Data For GET or POST
1186             req.method = null;             // GET or POST
1187             req.status = null;             // Response Status Code From Server
1188             req.fromQueue = false;         // Indicates if the request was taken off the queue
1189             // before being sent.  This prevents the request from
1190             // entering the queue redundantly.
1191 
1192             req.que = Queue;
1193 
1194             // Get an XMLHttpRequest Handle
1195             req.xmlReq = getTransport();
1196             if (req.xmlReq === null) {
1197                 return null;
1198             }
1199 
1200             function noop() {}
1201             
1202             // Set up request/response state callbacks
1203             /**
1204              * @ignore
1205              */
1206             req.xmlReq.onreadystatechange = function() {
1207                 if (req.xmlReq.readyState === 4) {
1208                     req.onComplete();
1209                     // next two lines prevent closure/ciruclar reference leaks
1210                     // of XHR instances in IE
1211                     req.xmlReq.onreadystatechange = noop;
1212                     req.xmlReq = null;
1213                 }
1214             };
1215 
1216             /**
1217              * This function is called when the request/response interaction
1218              * is complete.  If the return status code is successfull,
1219              * dequeue all requests from the queue that have completed.  If a
1220              * request has been found on the queue that has not been sent,
1221              * send the request.
1222              * @ignore
1223              */
1224             req.onComplete = function onComplete() {
1225                 if (req.xmlReq.status && (req.xmlReq.status >= 200 && req.xmlReq.status < 300)) {
1226                     sendEvent(req.xmlReq, req.context, "complete");
1227                     jsf.ajax.response(req.xmlReq, req.context);
1228                 } else {
1229                     sendEvent(req.xmlReq, req.context, "complete");
1230                     sendError(req.xmlReq, req.context, "httpError");
1231                 }
1232 
1233                 // Regardless of whether the request completed successfully (or not),
1234                 // dequeue requests that have been completed (readyState 4) and send
1235                 // requests that ready to be sent (readyState 0).
1236 
1237                 var nextReq = req.que.getOldestElement();
1238                 if (nextReq === null || typeof nextReq === 'undefined') {
1239                     return;
1240                 }
1241                 while ((typeof nextReq.xmlReq !== 'undefined' && nextReq.xmlReq !== null) &&
1242                        nextReq.xmlReq.readyState === 4) {
1243                     req.que.dequeue();
1244                     nextReq = req.que.getOldestElement();
1245                     if (nextReq === null || typeof nextReq === 'undefined') {
1246                         break;
1247                     }
1248                 }
1249                 if (nextReq === null || typeof nextReq === 'undefined') {
1250                     return;
1251                 }
1252                 if ((typeof nextReq.xmlReq !== 'undefined' && nextReq.xmlReq !== null) &&
1253                     nextReq.xmlReq.readyState === 0) {
1254                     nextReq.fromQueue = true;
1255                     nextReq.sendRequest();
1256                 }
1257             };
1258 
1259             /**
1260              * Utility method that accepts additional arguments for the AjaxEngine.
1261              * If an argument is passed in that matches an AjaxEngine property, the
1262              * argument value becomes the value of the AjaxEngine property.
1263              * Arguments that don't match AjaxEngine properties are added as
1264              * request parameters.
1265              * @ignore
1266              */
1267             req.setupArguments = function(args) {
1268                 for (var i in args) {
1269                     if (args.hasOwnProperty(i)) {
1270                         if (typeof req[i] === 'undefined') {
1271                             req.parameters[i] = args[i];
1272                         } else {
1273                             req[i] = args[i];
1274                         }
1275                     }
1276                 }
1277             };
1278 
1279             /**
1280              * This function does final encoding of parameters, determines the request method
1281              * (GET or POST) and sends the request using the specified url.
1282              * @ignore
1283              */
1284             req.sendRequest = function() {
1285                 if (req.xmlReq !== null) {
1286                     // if there is already a request on the queue waiting to be processed..
1287                     // just queue this request
1288                     if (!req.que.isEmpty()) {
1289                         if (!req.fromQueue) {
1290                             req.que.enqueue(req);
1291                             return;
1292                         }
1293                     }
1294                     // If the queue is empty, queue up this request and send
1295                     if (!req.fromQueue) {
1296                         req.que.enqueue(req);
1297                     }
1298                     // Some logic to get the real request URL
1299                     if (req.generateUniqueUrl && req.method == "GET") {
1300                         req.parameters["AjaxRequestUniqueId"] = new Date().getTime() + "" + req.requestIndex;
1301                     }
1302                     var content = null; // For POST requests, to hold query string
1303                     for (var i in req.parameters) {
1304                         if (req.parameters.hasOwnProperty(i)) {
1305                             if (req.queryString.length > 0) {
1306                                 req.queryString += "&";
1307                             }
1308                             req.queryString += encodeURIComponent(i) + "=" + encodeURIComponent(req.parameters[i]);
1309                         }
1310                     }
1311                     if (req.method === "GET") {
1312                         if (req.queryString.length > 0) {
1313                             req.url += ((req.url.indexOf("?") > -1) ? "&" : "?") + req.queryString;
1314                         }
1315                     }
1316                     req.xmlReq.open(req.method, req.url, req.async);
1317                     // note that we are including the charset=UTF-8 as part of the content type (even
1318                     // if encodeURIComponent encodes as UTF-8), because with some
1319                     // browsers it will not be set in the request.  Some server implementations need to 
1320                     // determine the character encoding from the request header content type.
1321                     if (req.method === "POST") {
1322                         if (typeof req.xmlReq.setRequestHeader !== 'undefined') {
1323                             req.xmlReq.setRequestHeader('Faces-Request', 'partial/ajax');
1324                             req.xmlReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
1325                         }
1326                         content = req.queryString;
1327                     }
1328                     // note that async == false is not a supported feature.  We may change it in ways
1329                     // that break existing programs at any time, with no warning.
1330                     if(!req.async) {
1331                         req.xmlReq.onreadystatechange = null; // no need for readystate change listening
1332                     }
1333                     sendEvent(req.xmlReq, req.context, "begin");
1334                     req.xmlReq.send(content);
1335                     if(!req.async){
1336                         req.onComplete();
1337                 }
1338                 }
1339             };
1340 
1341             return req;
1342         };
1343 
1344         /**
1345          * Error handling callback.
1346          * Assumes that the request has completed.
1347          * @ignore
1348          */
1349         var sendError = function sendError(request, context, status, description, serverErrorName, serverErrorMessage) {
1350 
1351             // Possible errornames:
1352             // httpError
1353             // emptyResponse
1354             // serverError
1355             // malformedXML
1356 
1357             var sent = false;
1358             var data = {};  // data payload for function
1359             data.type = "error";
1360             data.status = status;
1361             data.source = context.sourceid;
1362             data.responseCode = request.status;
1363             data.responseXML = request.responseXML;
1364             data.responseText = request.responseText;
1365 
1366             // ensure data source is the dom element and not the ID
1367             // per 14.4.1 of the 2.0 specification.
1368             if (typeof data.source === 'string') {
1369                 data.source = document.getElementById(data.source);
1370             }
1371 
1372             if (description) {
1373                 data.description = description;
1374             } else if (status == "httpError") {
1375                 if (data.responseCode === 0) {
1376                     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.";
1377                 } else {
1378                     data.description = "There was an error communicating with the server, status: " + data.responseCode;
1379                 }
1380             } else if (status == "serverError") {
1381                 data.description = serverErrorMessage;
1382             } else if (status == "emptyResponse") {
1383                 data.description = "An empty response was received from the server.  Check server error logs.";
1384             } else if (status == "malformedXML") {
1385                 if (getParseErrorText(data.responseXML) !== PARSED_OK) {
1386                     data.description = getParseErrorText(data.responseXML);
1387                 } else {
1388                     data.description = "An invalid XML response was received from the server.";
1389                 }
1390             }
1391 
1392             if (status == "serverError") {
1393                 data.errorName = serverErrorName;
1394                 data.errorMessage = serverErrorMessage;
1395             }
1396 
1397             // If we have a registered callback, send the error to it.
1398             if (context.onerror) {
1399                 context.onerror.call(null, data);
1400                 sent = true;
1401             }
1402 
1403             for (var i in errorListeners) {
1404                 if (errorListeners.hasOwnProperty(i)) {
1405                     errorListeners[i].call(null, data);
1406                     sent = true;
1407                 }
1408             }
1409 
1410             if (!sent && jsf.getProjectStage() === "Development") {
1411                 if (status == "serverError") {
1412                     alert("serverError: " + serverErrorName + " " + serverErrorMessage);
1413                 } else {
1414                     alert(status + ": " + data.description);
1415                 }
1416             }
1417         };
1418 
1419         /**
1420          * Event handling callback.
1421          * Request is assumed to have completed, except in the case of event = 'begin'.
1422          * @ignore
1423          */
1424         var sendEvent = function sendEvent(request, context, status) {
1425 
1426             var data = {};
1427             data.type = "event";
1428             data.status = status;
1429             data.source = context.sourceid;
1430             // ensure data source is the dom element and not the ID
1431             // per 14.4.1 of the 2.0 specification.
1432             if (typeof data.source === 'string') {
1433                 data.source = document.getElementById(data.source);
1434             }
1435             if (status !== 'begin') {
1436                 data.responseCode = request.status;
1437                 data.responseXML = request.responseXML;
1438                 data.responseText = request.responseText;
1439             }
1440 
1441             if (context.onevent) {
1442                 context.onevent.call(null, data);
1443             }
1444 
1445             for (var i in eventListeners) {
1446                 if (eventListeners.hasOwnProperty(i)) {
1447                     eventListeners[i].call(null, data);
1448                 }
1449             }
1450         };
1451 
1452         // Use module pattern to return the functions we actually expose
1453         return {
1454             /**
1455              * Register a callback for error handling.
1456              * <p><b>Usage:</b></p>
1457              * <pre><code>
1458              * jsf.ajax.addOnError(handleError);
1459              * ...
1460              * var handleError = function handleError(data) {
1461              * ...
1462              * }
1463              * </pre></code>
1464              * <p><b>Implementation Requirements:</b></p>
1465              * This function must accept a reference to an existing JavaScript function.
1466              * The JavaScript function reference must be added to a list of callbacks, making it possible
1467              * to register more than one callback by invoking <code>jsf.ajax.addOnError</code>
1468              * more than once.  This function must throw an error if the <code>callback</code>
1469              * argument is not a function.
1470              *
1471              * @member jsf.ajax
1472              * @param callback a reference to a function to call on an error
1473              */
1474             addOnError: function addOnError(callback) {
1475                 if (typeof callback === 'function') {
1476                     errorListeners[errorListeners.length] = callback;
1477                 } else {
1478                     throw new Error("jsf.ajax.addOnError:  Added a callback that was not a function.");
1479                 }
1480             },
1481             /**
1482              * Register a callback for event handling.
1483              * <p><b>Usage:</b></p>
1484              * <pre><code>
1485              * jsf.ajax.addOnEvent(statusUpdate);
1486              * ...
1487              * var statusUpdate = function statusUpdate(data) {
1488              * ...
1489              * }
1490              * </pre></code>
1491              * <p><b>Implementation Requirements:</b></p>
1492              * This function must accept a reference to an existing JavaScript function.
1493              * The JavaScript function reference must be added to a list of callbacks, making it possible
1494              * to register more than one callback by invoking <code>jsf.ajax.addOnEvent</code>
1495              * more than once.  This function must throw an error if the <code>callback</code>
1496              * argument is not a function.
1497              *
1498              * @member jsf.ajax
1499              * @param callback a reference to a function to call on an event
1500              */
1501             addOnEvent: function addOnEvent(callback) {
1502                 if (typeof callback === 'function') {
1503                     eventListeners[eventListeners.length] = callback;
1504                 } else {
1505                     throw new Error("jsf.ajax.addOnEvent: Added a callback that was not a function");
1506                 }
1507             },
1508             /**
1509              * <p>Send an asynchronous Ajax request to the server.
1510              * <p><b>Usage:</b></p>
1511              * <pre><code>
1512              * Example showing all optional arguments:
1513              *
1514              * <commandButton id="button1" value="submit"
1515              *     onclick="jsf.ajax.request(this,event,
1516              *       {execute:'button1',render:'status',onevent: handleEvent,onerror: handleError});return false;"/>
1517              * </commandButton/>
1518              * </pre></code>
1519              * <p><b>Implementation Requirements:</b></p>
1520              * This function must:
1521              * <ul>
1522              * <li>Be used within the context of a <code>form</code>.</li>
1523              * <li>Capture the element that triggered this Ajax request
1524              * (from the <code>source</code> argument, also known as the
1525              * <code>source</code> element.</li>
1526              * <li>If the <code>source</code> element is <code>null</code> or
1527              * <code>undefined</code> throw an error.</li>
1528              * <li>If the <code>source</code> argument is not a <code>string</code> or
1529              * DOM element object, throw an error.</li>
1530              * <li>If the <code>source</code> argument is a <code>string</code>, find the
1531              * DOM element for that <code>string</code> identifier.
1532              * <li>If the DOM element could not be determined, throw an error.</li>
1533              * <li>If the <code>onerror</code> and <code>onevent</code> arguments are set,
1534              * they must be functions, or throw an error.
1535              * <li>Determine the <code>source</code> element's <code>form</code>
1536              * element.</li>
1537              * <li>Get the <code>form</code> view state by calling
1538              * {@link jsf.getViewState} passing the
1539              * <code>form</code> element as the argument.</li>
1540              * <li>Collect post data arguments for the Ajax request.
1541              * <ul>
1542              * <li>The following name/value pairs are required post data arguments:
1543              * <table border="1">
1544              * <tr>
1545              * <th>name</th>
1546              * <th>value</th>
1547              * </tr>
1548              * <tr>
1549              * <td><code>javax.faces.ViewState</code></td>
1550              * <td><code>Contents of javax.faces.ViewState hidden field.  This is included when
1551              * {@link jsf.getViewState} is used.</code></td>
1552              * </tr>
1553              * <tr>
1554              * <td><code>javax.faces.partial.ajax</code></td>
1555              * <td><code>true</code></td>
1556              * </tr>
1557              * <tr>
1558              * <td><code>javax.faces.source</code></td>
1559              * <td><code>The identifier of the element that triggered this request.</code></td>
1560              * </tr>
1561              * </table>
1562              * </li>
1563              * </ul>
1564              * </li>
1565              * <li>Collect optional post data arguments for the Ajax request.
1566              * <ul>
1567              * <li>Determine additional arguments (if any) from the <code>options</code>
1568              * argument. If <code>options.execute</code> exists:
1569              * <ul>
1570              * <li>If the keyword <code>@none</code> is present, do not create and send
1571              * the post data argument <code>javax.faces.partial.execute</code>.</li>
1572              * <li>If the keyword <code>@all</code> is present, create the post data argument with
1573              * the name <code>javax.faces.partial.execute</code> and the value <code>@all</code>.</li>
1574              * <li>Otherwise, there are specific identifiers that need to be sent.  Create the post
1575              * data argument with the name <code>javax.faces.partial.execute</code> and the value as a
1576              * space delimited <code>string</code> of client identifiers.</li>
1577              * </ul>
1578              * </li>
1579              * <li>If <code>options.execute</code> does not exist, create the post data argument with the
1580              * name <code>javax.faces.partial.execute</code> and the value as the identifier of the
1581              * element that caused this request.</li>
1582              * <li>If <code>options.render</code> exists:
1583              * <ul>
1584              * <li>If the keyword <code>@none</code> is present, do not create and send
1585              * the post data argument <code>javax.faces.partial.render</code>.</li>
1586              * <li>If the keyword <code>@all</code> is present, create the post data argument with
1587              * the name <code>javax.faces.partial.render</code> and the value <code>@all</code>.</li>
1588              * <li>Otherwise, there are specific identifiers that need to be sent.  Create the post
1589              * data argument with the name <code>javax.faces.partial.render</code> and the value as a
1590              * space delimited <code>string</code> of client identifiers.</li>
1591              * </ul>
1592              * <li>If <code>options.render</code> does not exist do not create and send the
1593              * post data argument <code>javax.faces.partial.render</code>.</li>
1594              * <li>Determine additional arguments (if any) from the <code>event</code>
1595              * argument.  The following name/value pairs may be used from the
1596              * <code>event</code> object:
1597              * <ul>
1598              * <li><code>target</code> - the ID of the element that triggered the event.</li>
1599              * <li><code>captured</code> - the ID of the element that captured the event.</li>
1600              * <li><code>type</code> - the type of event (ex: onkeypress)</li>
1601              * <li><code>alt</code> - <code>true</code> if ALT key was pressed.</li>
1602              * <li><code>ctrl</code> - <code>true</code> if CTRL key was pressed.</li>
1603              * <li><code>shift</code> - <code>true</code> if SHIFT key was pressed. </li>
1604              * <li><code>meta</code> - <code>true</code> if META key was pressed. </li>
1605              * <li><code>right</code> - <code>true</code> if right mouse button
1606              * was pressed. </li>
1607              * <li><code>left</code> - <code>true</code> if left mouse button
1608              * was pressed. </li>
1609              * <li><code>keycode</code> - the key code.
1610              * </ul>
1611              * </li>
1612              * </ul>
1613              * </li>
1614              * <li>Encode the set of post data arguments.</li>
1615              * <li>Join the encoded view state with the encoded set of post data arguments
1616              * to form the <code>query string</code> that will be sent to the server.</li>
1617              * <li>Create a request <code>context</code> object and set the properties:
1618              * <ul><li><code>source</code> (the source DOM element for this request)</li>
1619              * <li><code>onerror</code> (the error handler for this request)</li>
1620              * <li><code>onevent</code> (the event handler for this request)</li></ul>
1621              * The request context will be used during error/event handling.</li>
1622              * <li>Send a <code>begin</code> event following the procedure as outlined
1623              * in the Chapter 13 "Sending Events" section of the spec prose document <a
1624              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
1625              *  overview summary</a></li>
1626              * <li>Set the request header with the name: <code>Faces-Request</code> and the
1627              * value: <code>partial/ajax</code>.</li>
1628              * <li>Determine the <code>posting URL</code> as follows: If the hidden field
1629              * <code>javax.faces.encodedURL</code> is present in the submitting form, use its
1630              * value as the <code>posting URL</code>.  Otherwise, use the <code>action</code>
1631              * property of the <code>form</code> element as the <code>URL</code>.</li>
1632              * <li>Send the request as an <code>asynchronous POST</code> using the
1633              * <code>posting URL</code> that was determined in the previous step.</li>
1634              * </ul>
1635              * Form serialization should occur just before the request is sent to minimize 
1636              * the amount of time between the creation of the serialized form data and the 
1637              * sending of the serialized form data (in the case of long requests in the queue).
1638              * Before the request is sent it must be put into a queue to ensure requests
1639              * are sent in the same order as when they were initiated.  The request callback function
1640              * must examine the queue and determine the next request to be sent.  The behavior of the
1641              * request callback function must be as follows:
1642              * <ul>
1643              * <li>If the request completed successfully invoke {@link jsf.ajax.response}
1644              * passing the <code>request</code> object.</li>
1645              * <li>If the request did not complete successfully, notify the client.</li>
1646              * <li>Regardless of the outcome of the request (success or error) every request in the
1647              * queue must be handled.  Examine the status of each request in the queue starting from
1648              * the request that has been in the queue the longest.  If the status of the request is
1649              * <code>complete</code> (readyState 4), dequeue the request (remove it from the queue).
1650              * If the request has not been sent (readyState 0), send the request.  Requests that are
1651              * taken off the queue and sent should not be put back on the queue.</li>
1652              * </ul>
1653              *
1654              * </p>
1655              *
1656              * @param source The DOM element that triggered this Ajax request, or an id string of the
1657              * element to use as the triggering element.
1658              * @param event The DOM event that triggered this Ajax request.  The
1659              * <code>event</code> argument is optional.
1660              * @param options The set of available options that can be sent as
1661              * request parameters to control client and/or server side
1662              * request processing. Acceptable name/value pair options are:
1663              * <table border="1">
1664              * <tr>
1665              * <th>name</th>
1666              * <th>value</th>
1667              * </tr>
1668              * <tr>
1669              * <td><code>execute</code></td>
1670              * <td><code>space seperated list of client identifiers</code></td>
1671              * </tr>
1672              * <tr>
1673              * <td><code>render</code></td>
1674              * <td><code>space seperated list of client identifiers</code></td>
1675              * </tr>
1676              * <tr>
1677              * <td><code>onevent</code></td>
1678              * <td><code>function to callback for event</code></td>
1679              * </tr>
1680              * <tr>
1681              * <td><code>onerror</code></td>
1682              * <td><code>function to callback for error</code></td>
1683              * </tr>
1684              * <tr>
1685              * <td><code>params</code></td>
1686              * <td><code>object containing parameters to include in the request</code></td>
1687              * </tr>
1688              * </table>
1689              * The <code>options</code> argument is optional.
1690              * @member jsf.ajax
1691              * @function jsf.ajax.request
1692              * @throws Error if first required argument <code>element</code> is not specified
1693              */
1694             request: function request(source, event, options) {
1695 
1696                 var element, form;   //  Element variables
1697                 var all, none;
1698 
1699                 if (typeof source === 'undefined' || source === null) {
1700                     throw new Error("jsf.ajax.request: source not set");
1701                 }
1702 
1703                 // set up the element based on source
1704                 if (typeof source === 'string') {
1705                     element = document.getElementById(source);
1706                 } else if (typeof source === 'object') {
1707                     element = source;
1708                 } else {
1709                     throw new Error("jsf.request: source must be object or string");
1710                 }
1711                 // attempt to handle case of name unset
1712                 // this might be true in a badly written composite component
1713                 if (!element.name) {
1714                     element.name = element.id;
1715                 }
1716 
1717                 if (typeof(options) === 'undefined' || options === null) {
1718                     options = {};
1719                 }
1720 
1721                 // Error handler for this request
1722                 var onerror = false;
1723 
1724                 if (options.onerror && typeof options.onerror === 'function') {
1725                     onerror = options.onerror;
1726                 } else if (options.onerror && typeof options.onerror !== 'function') {
1727                     throw new Error("jsf.ajax.request: Added an onerror callback that was not a function");
1728                 }
1729 
1730                 // Event handler for this request
1731                 var onevent = false;
1732 
1733                 if (options.onevent && typeof options.onevent === 'function') {
1734                     onevent = options.onevent;
1735                 } else if (options.onevent && typeof options.onevent !== 'function') {
1736                     throw new Error("jsf.ajax.request: Added an onevent callback that was not a function");
1737                 }
1738 
1739                 form = getForm(element);
1740                 if (!form) {
1741                     throw new Error("jsf.ajax.request: Method must be called within a form");
1742                 }
1743                 var viewState = jsf.getViewState(form);
1744 
1745                 // Set up additional arguments to be used in the request..
1746                 // Make sure "javax.faces.source" is set up.
1747                 // If there were "execute" ids specified, make sure we
1748                 // include the identifier of the source element in the
1749                 // "execute" list.  If there were no "execute" ids
1750                 // specified, determine the default.
1751 
1752                 var args = {};
1753 
1754                 args["javax.faces.source"] = element.id;
1755 
1756                 if (event && !!event.type) {
1757                     args["javax.faces.partial.event"] = event.type;
1758                 }
1759 
1760                 // If we have 'execute' identifiers:
1761                 // Handle any keywords that may be present.
1762                 // If @none present anywhere, do not send the
1763                 // "javax.faces.partial.execute" parameter.
1764                 // The 'execute' and 'render' lists must be space
1765                 // delimited.
1766 
1767                 if (options.execute) {
1768                     none = options.execute.search(/@none/);
1769                     if (none < 0) {
1770                         all = options.execute.search(/@all/);
1771                         if (all < 0) {
1772                             options.execute = options.execute.replace("@this", element.id);
1773                             options.execute = options.execute.replace("@form", form.id);
1774                             var temp = options.execute.split(' ');
1775                             if (!isInArray(temp, element.name)) {
1776                                 options.execute = element.name + " " + options.execute;
1777                             }
1778                         } else {
1779                             options.execute = "@all";
1780                         }
1781                         args["javax.faces.partial.execute"] = options.execute;
1782                     }
1783                 } else {
1784                     options.execute = element.name + " " + element.id;
1785                     args["javax.faces.partial.execute"] = options.execute;
1786                 }
1787 
1788                 if (options.render) {
1789                     none = options.render.search(/@none/);
1790                     if (none < 0) {
1791                         all = options.render.search(/@all/);
1792                         if (all < 0) {
1793                             options.render = options.render.replace("@this", element.id);
1794                             options.render = options.render.replace("@form", form.id);
1795                         } else {
1796                             options.render = "@all";
1797                         }
1798                         args["javax.faces.partial.render"] = options.render;
1799                     }
1800                 }
1801 
1802                 // remove non-passthrough options
1803                 delete options.execute;
1804                 delete options.render;
1805                 delete options.onerror;
1806                 delete options.onevent;
1807                 // copy all other options to args
1808                 for (var property in options) {
1809                     if (options.hasOwnProperty(property)) {
1810                         args[property] = options[property];
1811                     }
1812                 }
1813 
1814                 args["javax.faces.partial.ajax"] = "true";
1815                 args["method"] = "POST";
1816 
1817                 // Determine the posting url
1818 
1819                 var encodedUrlField = form.elements["javax.faces.encodedURL"];
1820                 if (typeof encodedUrlField == 'undefined') {
1821                     args["url"] = form.action;
1822                 } else {
1823                     args["url"] = encodedUrlField.value;
1824                 }
1825 
1826                 var ajaxEngine = new AjaxEngine();
1827                 ajaxEngine.setupArguments(args);
1828                 ajaxEngine.queryString = viewState;
1829                 ajaxEngine.context.onevent = onevent;
1830                 ajaxEngine.context.onerror = onerror;
1831                 ajaxEngine.context.sourceid = element.id;
1832                 ajaxEngine.context.formid = form.id;
1833                 ajaxEngine.context.render = args["javax.faces.partial.render"];
1834                 ajaxEngine.sendRequest();
1835 
1836                 // null out element variables to protect against IE memory leak
1837                 element = null;
1838                 form = null;
1839 
1840             },
1841             /**
1842              * <p>Receive an Ajax response from the server.
1843              * <p><b>Usage:</b></p>
1844              * <pre><code>
1845              * jsf.ajax.response(request, context);
1846              * </pre></code>
1847              * <p><b>Implementation Requirements:</b></p>
1848              * This function must evaluate the markup returned in the
1849              * <code>request.responseXML</code> object and perform the following action:
1850              * <ul>
1851              * <p>If there is no XML response returned, signal an <code>emptyResponse</code>
1852              * error. If the XML response does not follow the format as outlined
1853              * in Appendix A of the spec prose document <a
1854              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
1855              *  overview summary</a> signal a <code>malformedError</code> error.  Refer to
1856              * section "Signaling Errors" in Chapter 13 of the spec prose document <a
1857              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
1858              *  overview summary</a>.</p>
1859              * <p>If the response was successfully processed, send a <code>success</code>
1860              * event as outlined in Chapter 13 "Sending Events" section of the spec prose
1861              * document <a
1862              * href="../../javadocs/overview-summary.html#prose_document">linked in the
1863              * overview summary</a>.</p>
1864              * <p><i>Update Element Processing</i></p>
1865              * The <code>update</code> element is used to update a single DOM element.  The
1866              * "id" attribute of the <code>update</code> element refers to the DOM element that
1867              * will be updated.  The contents of the <code>CDATA</code> section is the data that 
1868              * will be used when updating the contents of the DOM element as specified by the
1869              * <code><update></code> element identifier.
1870              * <li>If an <code>update</code> element is found in the response
1871              * with the identifier <code>javax.faces.ViewRoot</code>:
1872              * <pre><code><update id="javax.faces.ViewRoot">
1873              *    <![CDATA[...]]>
1874              * </update></code></pre>
1875              * Update the entire DOM replacing the appropriate <code>head</code> and/or
1876              * <code>body</code> sections with the content from the response.</li>
1877              * <li>If an <code>update</code> element is found in the response with the identifier
1878              * <code>javax.faces.ViewState</code>:
1879              * <pre><code><update id="javax.faces.ViewState">
1880              *    <![CDATA[...]]>
1881              * </update></code></pre>
1882              * locate and update the submitting form's <code>javax.faces.ViewState</code> value
1883              * with the <code>CDATA</code> contents from the response.  Locate and update the 
1884              * <code>javax.faces.ViewState</code> value for all forms specified in the 
1885              * <code>render</code> target list.</li>
1886              * <li>If an <code>update</code> element is found in the response with the identifier
1887              * <code>javax.faces.ViewHead</code>:
1888              * <pre><code><update id="javax.faces.ViewHead">
1889              *    <![CDATA[...]]>
1890              * </update></code></pre>
1891              * update the document's <code>head</code> section with the <code>CDATA</code>
1892              * contents from the response.</li>
1893              * <li>If an <code>update</code> element is found in the response with the identifier
1894              * <code>javax.faces.ViewBody</code>:
1895              * <pre><code><update id="javax.faces.ViewBody">
1896              *    <![CDATA[...]]>
1897              * </update></code></pre>
1898              * update the document's <code>body</code> section with the <code>CDATA</code>
1899              * contents from the response.</li>
1900              * <li>For any other <code><update></code> element:
1901              * <pre><code><update id="update id">
1902              *    <![CDATA[...]]>
1903              * </update></code></pre>
1904              * Find the DOM element with the identifier that matches the
1905              * <code><update></code> element identifier, and replace its contents with
1906              * the <code><update></code> element's <code>CDATA</code> contents.</li>
1907              * </li>
1908              * <p><i>Insert Element Processing</i></p>
1909              * <li>If an <code><insert></code> element is found in the response with the
1910              * attribute <code>before</code>:
1911              * <pre><code><insert id="insert id" before="before id">
1912              *    <![CDATA[...]]>
1913              * </insert></code></pre>
1914              * <ul>
1915              * <li>Extract this <code><insert></code> element's <code>CDATA</code> contents
1916              * from the response.</li>
1917              * <li>Find the DOM element whose identifier matches <code>before id</code> and insert
1918              * the <code><insert></code> element's <code>CDATA</code> content before
1919              * the DOM element in the document.</li>
1920              * </ul>
1921              * </li>
1922              * <li>If an <code><insert></code> element is found in the response with the
1923              * attribute <code>after</code>:
1924              * <pre><code><insert id="insert id" after="after id">
1925              *    <![CDATA[...]]>
1926              * </insert></code></pre>
1927              * <ul>
1928              * <li>Extract this <code><insert></code> element's <code>CDATA</code> contents
1929              * from the response.</li>
1930              * <li>Find the DOM element whose identifier matches <code>after id</code> and insert
1931              * the <code><insert></code> element's <code>CDATA</code> content after
1932              * the DOM element in the document.</li>
1933              * </ul>
1934              * </li>
1935              * <p><i>Delete Element Processing</i></p>
1936              * <li>If a <code><delete></code> element is found in the response:
1937              * <pre><code><delete id="delete id"/></code></pre>
1938              * Find the DOM element whose identifier matches <code>delete id</code> and remove it
1939              * from the DOM.</li>
1940              * <p><i>Element Attribute Update Processing</i></p>
1941              * <li>If an <code><attributes></code> element is found in the response:
1942              * <pre><code><attributes id="id of element with attribute">
1943              *    <attribute name="attribute name" value="attribute value">
1944              *    ...
1945              * </attributes></code></pre>
1946              * <ul>
1947              * <li>Find the DOM element that matches the <code><attributes></code> identifier.</li>
1948              * <li>For each nested <code><attribute></code> element in <code><attribute></code>,
1949              * update the DOM element attribute value (whose name matches <code>attribute name</code>),
1950              * with <code>attribute value</code>.</li>
1951              * </ul>
1952              * </li>
1953              * <p><i>JavaScript Processing</i></p>
1954              * <li>If an <code><eval></code> element is found in the response:
1955              * <pre><code><eval>
1956              *    <![CDATA[...JavaScript...]]>
1957              * </eval></code></pre>
1958              * <ul>
1959              * <li>Extract this <code><eval></code> element's <code>CDATA</code> contents
1960              * from the response and execute it as if it were JavaScript code.</li>
1961              * </ul>
1962              * </li>
1963              * <p><i>Redirect Processing</i></p>
1964              * <li>If a <code><redirect></code> element is found in the response:
1965              * <pre><code><redirect url="redirect url"/></code></pre>
1966              * Cause a redirect to the url <code>redirect url</code>.</li>
1967              * <p><i>Error Processing</i></p>
1968              * <li>If an <code><error></code> element is found in the response:
1969              * <pre><code><error>
1970              *    <error-name>..fully qualified class name string...<error-name>
1971              *    <error-message><![CDATA[...]]><error-message>
1972              * </error></code></pre>
1973              * Extract this <code><error></code> element's <code>error-name</code> contents
1974              * and the <code>error-message</code> contents. Signal a <code>serverError</code> passing
1975              * the <code>errorName</code> and <code>errorMessage</code>.  Refer to
1976              * section "Signaling Errors" in Chapter 13 of the spec prose document <a
1977              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
1978              *  overview summary</a>.</li>
1979              * <p><i>Extensions</i></p>
1980              * <li>The <code><extensions></code> element provides a way for framework
1981              * implementations to provide their own information.</li>
1982              * <p><li>The implementation must check if <script> elements in the response can
1983              * be automatically run, as some browsers support this feature and some do not.  
1984              * If they can not be run, then scripts should be extracted from the response and
1985              * run separately.</li></p> 
1986              * </ul>
1987              *
1988              * </p>
1989              *
1990              * @param request The <code>XMLHttpRequest</code> instance that
1991              * contains the status code and response message from the server.
1992              *
1993              * @param context An object containing the request context, including the following properties:
1994              * the source element, per call onerror callback function, and per call onevent callback function.
1995              *
1996              * @throws  Error if request contains no data
1997              *
1998              * @function jsf.ajax.response
1999              */
2000             response: function response(request, context) {
2001                 if (!request) {
2002                     throw new Error("jsf.ajax.response: Request parameter is unset");
2003                 }
2004 
2005                 // ensure context source is the dom element and not the ID
2006                 // per 14.4.1 of the 2.0 specification.  We're doing it here
2007                 // *before* any errors or events are propagated becasue the
2008                 // DOM element may be removed after the update has been processed.
2009                 if (typeof context.sourceid === 'string') {
2010                     context.sourceid = document.getElementById(context.sourceid);
2011                 }
2012 
2013                 var xml = request.responseXML;
2014                 if (xml === null) {
2015                     sendError(request, context, "emptyResponse");
2016                     return;
2017                 }
2018 
2019                 if (getParseErrorText(xml) !== PARSED_OK) {
2020                     sendError(request, context, "malformedXML");
2021                     return;
2022                 }
2023 
2024                 var responseType = xml.getElementsByTagName("partial-response")[0].firstChild;
2025 
2026                 if (responseType.nodeName === "error") { // it's an error
2027                     var errorName = responseType.firstChild.firstChild.nodeValue;
2028                     var errorMessage = responseType.firstChild.nextSibling.firstChild.nodeValue;
2029                     sendError(request, context, "serverError", null, errorName, errorMessage);
2030                     sendEvent(request, context, "success");
2031                     return;
2032                 }
2033 
2034 
2035                 if (responseType.nodeName === "redirect") {
2036                     window.location = responseType.getAttribute("url");
2037                     return;
2038                 }
2039 
2040 
2041                 if (responseType.nodeName !== "changes") {
2042                     sendError(request, context, "malformedXML", "Top level node must be one of: changes, redirect, error, received: " + responseType.nodeName + " instead.");
2043                     return;
2044                 }
2045 
2046 
2047                 var changes = responseType.childNodes;
2048 
2049                 try {
2050                     for (var i = 0; i < changes.length; i++) {
2051                         switch (changes[i].nodeName) {
2052                             case "update":
2053                                 doUpdate(changes[i], context);
2054                                 break;
2055                             case "delete":
2056                                 doDelete(changes[i]);
2057                                 break;
2058                             case "insert":
2059                                 doInsert(changes[i]);
2060                                 break;
2061                             case "attributes":
2062                                 doAttributes(changes[i]);
2063                                 break;
2064                             case "eval":
2065                                 doEval(changes[i]);
2066                                 break;
2067                             case "extension":
2068                                 // no action
2069                                 break;
2070                             default:
2071                                 sendError(request, context, "malformedXML", "Changes allowed are: update, delete, insert, attributes, eval, extension.  Received " + changes[i].nodeName + " instead.");
2072                                 return;
2073                         }
2074                     }
2075                 } catch (ex) {
2076                     sendError(request, context, "malformedXML", ex.message);
2077                     return;
2078                 }
2079                 sendEvent(request, context, "success");
2080 
2081             }
2082         };
2083     }();
2084 
2085     /**
2086      *
2087      * <p>Return the value of <code>Application.getProjectStage()</code> for
2088      * the currently running application instance.  Calling this method must
2089      * not cause any network transaction to happen to the server.</p>
2090      * <p><b>Usage:</b></p>
2091      * <pre><code>
2092      * var stage = jsf.getProjectStage();
2093      * if (stage === ProjectStage.Development) {
2094      *  ...
2095      * } else if stage === ProjectStage.Production) {
2096      *  ...
2097      * }
2098      * </code></pre>
2099      *
2100      * @returns String <code>String</code> representing the current state of the
2101      * running application in a typical product development lifecycle.  Refer
2102      * to <code>javax.faces.application.Application.getProjectStage</code> and
2103      * <code>javax.faces.application.ProjectStage</code>.
2104      * @function jsf.getProjectStage
2105      */
2106     jsf.getProjectStage = function() {
2107         // First, return cached value if available
2108         if (typeof mojarra !== 'undefined' && typeof mojarra.projectStageCache !== 'undefined') {
2109             return mojarra.projectStageCache;
2110         }
2111         var scripts = document.getElementsByTagName("script"); // nodelist of scripts
2112         var script; // jsf.js script
2113         var s = 0; // incremental variable for for loop
2114         var stage; // temp value for stage
2115         var match; // temp value for match
2116         while (s < scripts.length) {
2117             if (typeof scripts[s].src === 'string' && scripts[s].src.match('\/javax\.faces\.resource\/jsf\.js\?.*ln=javax\.faces')) {
2118                 script = scripts[s].src;
2119                 break;
2120             }
2121             s++;
2122         }
2123         if (typeof script == "string") {
2124             match = script.match("stage=(.*)");
2125             if (match) {
2126                 stage = match[1];
2127             }
2128         }
2129         if (typeof stage === 'undefined' || !stage) {
2130             stage = "Production";
2131         }
2132 
2133         mojarra = mojarra || {};
2134         mojarra.projectStageCache = stage;
2135 
2136         return mojarra.projectStageCache;
2137     };
2138 
2139 
2140     /**
2141      * <p>Collect and encode state for input controls associated
2142      * with the specified <code>form</code> element.  This will include
2143      * all input controls of type <code>hidden</code>.</p>
2144      * <p><b>Usage:</b></p>
2145      * <pre><code>
2146      * var state = jsf.getViewState(form);
2147      * </pre></code>
2148      *
2149      * @param form The <code>form</code> element whose contained
2150      * <code>input</code> controls will be collected and encoded.
2151      * Only successful controls will be collected and encoded in
2152      * accordance with: <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2">
2153      * Section 17.13.2 of the HTML Specification</a>.
2154      *
2155      * @returns String The encoded state for the specified form's input controls.
2156      * @function jsf.getViewState
2157      */
2158     jsf.getViewState = function(form) {
2159         if (!form) {
2160             throw new Error("jsf.getViewState:  form must be set");
2161         }
2162         var els = form.elements;
2163         var len = els.length;
2164         // create an array which we'll use to hold all the intermediate strings
2165         // this bypasses a problem in IE when repeatedly concatenating very
2166         // large strings - we'll perform the concatenation once at the end
2167         var qString = [];
2168         var addField = function(name, value) {
2169             var tmpStr = "";
2170             if (qString.length > 0) {
2171                 tmpStr = "&";
2172             }
2173             tmpStr += encodeURIComponent(name) + "=" + encodeURIComponent(value);
2174             qString.push(tmpStr);
2175         };
2176         for (var i = 0; i < len; i++) {
2177             var el = els[i];
2178             if (!el.disabled) {
2179                 switch (el.type) {
2180                     case 'text':
2181                     case 'password':
2182                     case 'hidden':
2183                     case 'textarea':
2184                         addField(el.name, el.value);
2185                         break;
2186                     case 'select-one':
2187                         if (el.selectedIndex >= 0) {
2188                             addField(el.name, el.options[el.selectedIndex].value);
2189                         }
2190                         break;
2191                     case 'select-multiple':
2192                         for (var j = 0; j < el.options.length; j++) {
2193                             if (el.options[j].selected) {
2194                                 addField(el.name, el.options[j].value);
2195                             }
2196                         }
2197                         break;
2198                     case 'checkbox':
2199                     case 'radio':
2200                         if (el.checked) {
2201                             addField(el.name, el.value || 'on');
2202                         }
2203                         break;
2204                 }
2205             }
2206         }
2207         // concatenate the array
2208         return qString.join("");
2209     };
2210 
2211     /**
2212      * The namespace for JavaServer Faces JavaScript utilities.
2213      * @name jsf.util
2214      * @namespace
2215      */
2216     jsf.util = {};
2217 
2218     /**
2219      * <p>A varargs function that invokes an arbitrary number of scripts.
2220      * If any script in the chain returns false, the chain is short-circuited
2221      * and subsequent scripts are not invoked.  Any number of scripts may
2222      * specified after the <code>event</code> argument.</p>
2223      *
2224      * @param source The DOM element that triggered this Ajax request, or an
2225      * id string of the element to use as the triggering element.
2226      * @param event The DOM event that triggered this Ajax request.  The
2227      * <code>event</code> argument is optional.
2228      *
2229      * @returns boolean <code>false</code> if any scripts in the chain return <code>false</code>,
2230      *  otherwise returns <code>true</code>
2231      * 
2232      * @function jsf.util.chain
2233      */
2234     jsf.util.chain = function(source, event) {
2235 
2236         if (arguments.length < 3) {
2237             return true;
2238         }
2239 
2240         // RELEASE_PENDING rogerk - shouldn't this be getElementById instead of null
2241         var thisArg = (typeof source === 'object') ? source : null;
2242 
2243         // Call back any scripts that were passed in
2244         for (var i = 2; i < arguments.length; i++) {
2245 
2246             var f = new Function("event", arguments[i]);
2247             var returnValue = f.call(thisArg, event);
2248 
2249             if (returnValue === false) {
2250                 return false;
2251             }
2252         }
2253         return true;
2254         
2255     };
2256 
2257     /**
2258      * <p>An integer specifying the specification version that this file implements.
2259      * It's format is: rightmost two digits, bug release number, next two digits,
2260      * minor release number, leftmost digits, major release number.
2261      * This number may only be incremented by a new release of the specification.</p>
2262      */
2263     jsf.specversion = 20000;
2264 
2265     /**
2266      * <p>An integer specifying the implementation version that this file implements.
2267      * It's a monotonically increasing number, reset with every increment of
2268      * <code>jsf.specversion</code>
2269      * This number is implementation dependent.</p>
2270      */
2271     jsf.implversion = 3;
2272 
2273 
2274 } //end if version detection block
2275