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