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