1 /*
  2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3  *
  4  * Copyright 1997-2008 Sun Microsystems, Inc. All rights reserved.
  5  *
  6  * The contents of this file are subject to the terms of either the GNU
  7  * General Public License Version 2 only ("GPL") or the Common Development
  8  * and Distribution License("CDDL") (collectively, the "License").  You
  9  * may not use this file except in compliance with the License. You can obtain
 10  * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 11  * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 12  * language governing permissions and limitations under the License.
 13  *
 14  * When distributing the software, include this License Header Notice in each
 15  * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 16  * Sun designates this particular file as subject to the "Classpath" exception
 17  * as provided by Sun in the GPL Version 2 section of the License file that
 18  * accompanied this code.  If applicable, add the following below the License
 19  * Header, with the fields enclosed by brackets [] replaced by your own
 20  * identifying information: "Portions Copyrighted [year]
 21  * [name of copyright owner]"
 22  *
 23  * Contributor(s):
 24  *
 25  * If you wish your version of this file to be governed by only the CDDL or
 26  * only the GPL Version 2, indicate your decision by adding "[Contributor]
 27  * elects to include this software in this distribution under the [CDDL or GPL
 28  * Version 2] license."  If you don't indicate a single choice of license, a
 29  * recipient has the option to distribute your version of this file under
 30  * either the CDDL, the GPL Version 2 or to extend the choice of license to
 31  * its licensees as provided above.  However, if you add GPL Version 2 code
 32  * and therefore, elected the GPL Version 2 license, then the option applies
 33  * only if the new code is made subject to such option by the copyright
 34  * holder.
 35  *
 36  *
 37  * This file incorporates work covered by the following copyright and
 38  * permission notice:
 39  *
 40  * Copyright 2004 The Apache Software Foundation
 41  *
 42  * Licensed under the Apache License, Version 2.0 (the "License");
 43  * you may not use this file except in compliance with the License.
 44  * You may obtain a copy of the License at
 45  *
 46  *     http://www.apache.org/licenses/LICENSE-2.0
 47  *
 48  * Unless required by applicable law or agreed to in writing, software
 49  * distributed under the License is distributed on an "AS IS" BASIS,
 50  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 51  * See the License for the specific language governing permissions and
 52  * limitations under the License.
 53  */
 54 
 55 /**
 56  @project JSF JavaScript Library
 57  @version 2.0
 58  @description This is the standard implementation of the JSF JavaScript Library.
 59  */
 60 
 61 /**
 62  * Register with OpenAjax
 63  */
 64 if (typeof OpenAjax !== "undefined" &&
 65     typeof OpenAjax.hub.registerLibrary !== "undefined") {
 66     OpenAjax.hub.registerLibrary("jsf", "www.sun.com", "2.0", null);
 67 }
 68 
 69 // Detect if this is already loaded, and if loaded, if it's a higher version
 70 if (!((jsf && jsf.specversion && jsf.specversion > 20000 ) &&
 71       (jsf.implversion && jsf.implversion > 1))) {
 72 
 73     /**
 74      * The top level global namespace for JavaServer Faces functionality.
 75      * @name jsf
 76      * @namespace
 77      */
 78     var jsf = {};
 79 
 80     /**
 81      * The namespace for Ajax functionality.
 82      * @name jsf.ajax
 83      * @namespace
 84      * @exec
 85      */
 86 
 87     jsf.ajax = function() {
 88 
 89         var eventListeners = [];
 90         var errorListeners = [];
 91 
 92         /**
 93          * @ignore
 94          */
 95         var getTransport = function getTransport() {
 96             var methods = [
 97                 function() {
 98                     return new XMLHttpRequest();
 99                 },
100                 function() {
101                     return new ActiveXObject('Msxml2.XMLHTTP');
102                 },
103                 function() {
104                     return new ActiveXObject('Microsoft.XMLHTTP');
105                 }
106             ];
107 
108             var returnVal;
109             for (var i = 0, len = methods.length; i < len; i++) {
110                 try {
111                     returnVal = methods[i]();
112                 } catch(e) {
113                     continue;
114                 }
115                 return returnVal;
116             }
117             throw new Error('Could not create an XHR object.');
118         };
119 
120         /**
121          * @ignore
122          */
123         var $ = function $() {
124             var results = [], element;
125             for (var i = 0; i < arguments.length; i++) {
126                 element = arguments[i];
127                 if (typeof element == 'string') {
128                     element = document.getElementById(element);
129                 }
130                 results.push(element);
131             }
132             return results.length > 1 ? results : results[0];
133         };
134 
135         /**
136          * @ignore
137          */
138         var getForm = function getForm(element) {
139             if (element) {
140                 var form = $(element);
141                 while (form && form.nodeName && form.nodeName.toLowerCase() !== 'form') {
142                     if (form.form) {
143                         return form.form;
144                     }
145                     if (form.parentNode) {
146                         form = form.parentNode;
147                     } else {
148                         form = null;
149                     }
150                     if (form) {
151                         return form;
152                     }
153                 }
154                 return document.forms[0];
155             }
156             return null;
157         };
158 
159         /**
160          * Remove trailing and leading whitespace
161          * @ignore
162          */
163         var trim = function trim(str) {
164             return str.replace(/^\s+/g, "").replace(/\s+$/g, "");
165         };
166 
167         /**
168          * Split a delimited string into an array, trimming whitespace
169          * param s String to split
170          * param e delimiter character - cannot be a space
171          * @ignore
172          */
173         var toArray = function toArray(s, e) {
174             var sarray;
175             if (typeof s === 'string') {
176                 sarray = s.split((e) ? e : ' ');
177                 for (var i = 0; i < sarray.length; i++) {
178                     sarray[i] = trim(sarray[i]);
179                 }
180             }
181             return sarray;
182         };
183 
184         /**
185          * Check if a value exists in an array
186          * @ignore
187          */
188         var isInArray = function isInArray(array, value) {
189             for (var i = 0; i < array.length; i++) {
190                 if (array[i] === value) {
191                     return true;
192                 }
193             }
194             return false;
195         };
196 
197 
198         /**
199          * Replace DOM element
200          * @ignore
201          */
202         var elementReplace = function elementReplace(d, tempTagName, src) {
203             var parent = d.parentNode;
204             var temp = document.createElement(tempTagName);
205             var result = null;
206             temp.id = d.id;
207 
208             // Creating a head element isn't allowed in IE, so we'll disallow it
209             if (d.nodeName.toLowerCase() === "head") {
210                 throw new Error("Attempted to replace a head element");
211             } else {
212                 temp.innerHTML = src;
213             }
214 
215             result = temp;
216             parent.replaceChild(temp, d);
217             return result;
218         };
219 
220 
221         /**
222          * Do update.
223          * @ignore
224          */
225         var doUpdate = function doUpdate(element) {
226             var id, content, markup, str, state;
227             var stateElem;
228 
229             id = element.getAttribute('id');
230             if (id === "javax.faces.ViewState") {
231                 state = element.firstChild;
232 
233                 // Now set the view state from the server into the DOM
234                 // If there are multiple forms, make sure they all have a
235                 // viewState hidden field.
236 
237                 stateElem = $("javax.faces.ViewState");
238                 if (stateElem) {
239                     stateElem.value = state.text || state.data;
240                 }
241                 var field;
242                 for (var k = 0; k < document.forms.length; k++) {
243                     field = document.forms[k].elements["javax.faces.ViewState"];
244                     if (typeof field == 'undefined') {
245                         field = document.createElement("input");
246                         field.type = "hidden";
247                         field.name = "javax.faces.ViewState";
248                         document.forms[k].appendChild(field);
249                     }
250                     field.value = state.text || state.data;
251                 }
252                 return;
253             }
254 
255             // join the CDATA sections in the markup
256             // RELEASE_PENDING are there allowed to be more than one CDATA?
257             markup = '';
258             for (var j = 0; j < element.childNodes.length; j++) {
259                 content = element.childNodes[j];
260                 markup += content.text || content.data;
261             }
262 
263             // RELEASE_PENDING - doc what this does
264             str = markup.replace(new RegExp('(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)', 'img'), '');
265 
266             var src = str;
267 
268             // If our special render all markup is present..
269             if (id === "javax.faces.ViewRoot") {
270                 // if src contains <html>, trim the <html> and </html>, if present.
271                 //   if src contains <head>
272                 //      extract the contents of <head> and replace current document's
273                 //      <head> with the contents.
274                 //   if src contains <body>
275                 //      extract the contents of <body> and replace the current
276                 //      document's <body> with the contents.
277                 //   if src does not contain <body>
278                 //      replace the current document's <body> with the contents.
279                 var
280                         htmlStartEx = new RegExp("< *html.*>", "gi"),
281                         htmlEndEx = new RegExp("< */ *html.*>", "gi"),
282                         headStartEx = new RegExp("< *head.*>", "gi"),
283                         headEndEx = new RegExp("< */ *head.*>", "gi"),
284                         bodyStartEx = new RegExp("< *body.*>", "gi"),
285                         bodyEndEx = new RegExp("< */ *body.*>", "gi"),
286                         htmlStart, htmlEnd, headStart, headEnd, bodyStart, bodyEnd;
287                 var srcHead = null, srcBody = null;
288                 // find the current document's "body" element
289                 var docBody = document.getElementsByTagName("body")[0];
290                 // if src contains <html>
291                 if (null !== (htmlStart = htmlStartEx.exec(src))) {
292                     // if src contains </html>
293                     if (null !== (htmlEnd = htmlEndEx.exec(src))) {
294                         src = src.substring(htmlStartEx.lastIndex, htmlEnd.index);
295                     } else {
296                         src = src.substring(htmlStartEx.lastIndex);
297                     }
298                 }
299                 // if src contains <head>
300                 if (null !== (headStart = headStartEx.exec(src))) {
301                     // if src contains </head>
302                     if (null !== (headEnd = headEndEx.exec(src))) {
303                         srcHead = src.substring(headStartEx.lastIndex,
304                                 headEnd.index);
305                     } else {
306                         srcHead = src.substring(headStartEx.lastIndex);
307                     }
308                     // find the "head" element
309                     var docHead = document.getElementsByTagName("head")[0];
310                     if (docHead) {
311                         elementReplace(docHead, "head", srcHead);
312                     }
313                 }
314                 // if src contains <body>
315                 if (null !== (bodyStart = bodyStartEx.exec(src))) {
316                     // if src contains </body>
317                     if (null !== (bodyEnd = bodyEndEx.exec(src))) {
318                         srcBody = src.substring(bodyStartEx.lastIndex,
319                                 bodyEnd.index);
320                     } else {
321                         srcBody = src.substring(bodyStartEx.lastIndex);
322                     }
323                     elementReplace(docBody, "body", srcBody);
324                 }
325                 if (!srcBody) {
326                     elementReplace(docBody, "body", src);
327                 }
328 
329             } else {
330                 var d = $(id);
331                 if (!d) {
332                     throw new Error("jsf.ajax.response: " + id + " not found");
333                 }
334                 var parent = d.parentNode;
335                 var temp = document.createElement('div');
336                 temp.id = d.id;
337                 temp.innerHTML = trim(str);
338 
339                 parent.replaceChild(temp.firstChild, d);
340             }
341         };
342 
343         /**
344          * Delete a node specified by the element.
345          * @param element
346          * @ignore
347          */
348         var doDelete = function doDelete(element) {
349             var id = element.getAttribute('id');
350             var target = $(id);
351             var parent = target.parentNode;
352             parent.removeChild(target);
353         };
354 
355         /**
356          * Insert a node specified by the element.
357          * @param element
358          * @ignore
359          */
360         var doInsert = function doInsert(element) {
361             // RELEASE_PENDING there may be an insert issue in IE tables.  Needs testing.
362             var target = $(element.firstChild.getAttribute('id'));
363             var parent = target.parentNode;
364             var tempElement = document.createElement('span');
365             tempElement.innerHTML = element.firstChild.firstChild.nodeValue;
366             if (element.firstChild.nodeName === 'after') {
367                 // Get the next in the list, to insert before
368                 target = target.nextSibling;
369             }  // otherwise, this is a 'before' element
370             parent.insertBefore(tempElement.firstChild, target);
371             document.removeChild(tempElement);
372         };
373 
374         /**
375          * Modify attributes of given element id.
376          * @param element
377          * @ignore
378          */
379         var doAttributes = function doAttributes(element) {
380 
381             // RELEASE_PENDING IE 5-7 does not allow setting styles with setAttribute
382             // RELEASE_PENDING IE 5-7 will clear event attributes if you try to set them
383 
384             // Get id of element we'll act against
385             var id = element.getAttribute('id');
386 
387             var target = $(id);
388 
389             // There can be multiple attributes modified.  Loop through the list.
390             var nodes = element.childNodes;
391             for (var i = 0; i < nodes.length; i++) {
392                 var name = nodes[i].firstChild.firstChild.nodeValue;
393                 var value = nodes[i].firstChild.nextSibling.firstChild.nodeValue;
394                 target.setAttribute(name,value);
395             }
396         };
397 
398         /**
399          * Eval the CDATA of the element.
400          * @param element to eval
401          * @ignore
402          */
403         var doEval = function doEval(element) {
404             var evalText = element.firstChild.nodeValue;
405             eval(evalText);
406         }
407 
408         /**
409          * Ajax Request Queue
410          * @ignore
411          */
412         var Queue = new function Queue() {
413 
414             // Create the internal queue
415             var queue = [];
416 
417 
418             // the amount of space at the front of the queue, initialised to zero
419             var queueSpace = 0;
420 
421             /** Returns the size of this Queue. The size of a Queue is equal to the number
422              * of elements that have been enqueued minus the number of elements that have
423              * been dequeued.
424              * @ignore
425              */
426             this.getSize = function getSize() {
427                 return queue.length - queueSpace;
428             };
429 
430             /** Returns true if this Queue is empty, and false otherwise. A Queue is empty
431              * if the number of elements that have been enqueued equals the number of
432              * elements that have been dequeued.
433              * @ignore
434              */
435             this.isEmpty = function isEmpty() {
436                 return (queue.length === 0);
437             };
438 
439             /** Enqueues the specified element in this Queue.
440              *
441              * @param element - the element to enqueue
442              * @ignore
443              */
444             this.enqueue = function enqueue(element) {
445                 // Queue the request
446                 queue.push(element);
447             };
448 
449 
450             /** Dequeues an element from this Queue. The oldest element in this Queue is
451              * removed and returned. If this Queue is empty then undefined is returned.
452              *
453              * @returns Object The element that was removed from the queue.
454              * @ignore
455              */
456             this.dequeue = function dequeue() {
457                 // initialise the element to return to be undefined
458                 var element = undefined;
459 
460                 // check whether the queue is empty
461                 if (queue.length) {
462                     // fetch the oldest element in the queue
463                     element = queue[queueSpace];
464 
465                     // update the amount of space and check whether a shift should occur
466                     if (++queueSpace * 2 >= queue.length) {
467                         // set the queue equal to the non-empty portion of the queue
468                         queue = queue.slice(queueSpace);
469                         // reset the amount of space at the front of the queue
470                         queueSpace = 0;
471                     }
472                 }
473                 // return the removed element
474                 return element;
475             };
476 
477             /** Returns the oldest element in this Queue. If this Queue is empty then
478              * undefined is returned. This function returns the same value as the dequeue
479              * function, but does not remove the returned element from this Queue.
480              * @ignore
481              */
482             this.getOldestElement = function getOldestElement() {
483                 // initialise the element to return to be undefined
484                 var element = undefined;
485 
486                 // if the queue is not element then fetch the oldest element in the queue
487                 if (queue.length) {
488                     element = queue[queueSpace];
489                 }
490                 // return the oldest element
491                 return element;
492             };
493         }();
494 
495 
496         /**
497          * AjaxEngine handles Ajax implementation details.
498          * @ignore
499          */
500         var AjaxEngine = function AjaxEngine() {
501 
502             var req = {};                  // Request Object
503             req.url = null;                // Request URL
504             req.context = {};              // Context of request and response
505             req.context.source = null;             // Source of this request
506             req.context.onerror = null;            // Error handler for request
507             req.context.onevent = null;            // Event handler for request
508             req.xmlReq = null;             // XMLHttpRequest Object
509             req.async = true;              // Default - Asynchronous
510             req.parameters = {};           // Parameters For GET or POST
511             req.queryString = null;        // Encoded Data For GET or POST
512             req.method = null;             // GET or POST
513             req.status = null;             // Response Status Code From Server
514             req.fromQueue = false;         // Indicates if the request was taken off the queue
515             // before being sent.  This prevents the request from
516             // entering the queue redundantly.
517 
518             req.que = Queue;
519 
520             // Get an XMLHttpRequest Handle
521             req.xmlReq = getTransport();
522             if (req.xmlReq === null) {
523                 return null;
524             }
525 
526             // Set up request/response state callbacks
527             /**
528              * @ignore
529              */
530             req.xmlReq.onreadystatechange = function() {
531                 if (req.xmlReq.readyState === 4) {
532                     req.onComplete();
533                 }
534             };
535 
536             /**
537              * This function is called when the request/response interaction
538              * is complete.  If the return status code is successfull,
539              * dequeue all requests from the queue that have completed.  If a
540              * request has been found on the queue that has not been sent,
541              * send the request.
542              * @ignore
543              */
544             req.onComplete = function onComplete() {
545                 req.status = req.xmlReq.status;
546                 if ((req.status !== null && typeof req.status !== 'undefined' &&
547                      req.status !== 0) && (req.status >= 200 && req.status < 300)) {
548                     sendEvent(req.xmlReq, req.context, "complete");
549                     jsf.ajax.response(req.xmlReq, req.context);
550                 } else {
551                     sendEvent(req.xmlReq, req.context, "complete");
552                     sendError(req.xmlReq, req.context, "httpError");
553                 }
554 
555                 // Regardless of whether the request completed successfully (or not),
556                 // dequeue requests that have been completed (readyState 4) and send
557                 // requests that ready to be sent (readyState 0).
558 
559                 var nextReq = req.que.getOldestElement();
560                 if (nextReq === null || typeof nextReq === 'undefined') {
561                     return;
562                 }
563                 while ((typeof nextReq.xmlReq !== 'undefined' && nextReq.xmlReq !== null) &&
564                        nextReq.xmlReq.readyState === 4) {
565                     req.que.dequeue();
566                     nextReq = req.que.getOldestElement();
567                     if (nextReq === null || typeof nextReq === 'undefined') {
568                         break;
569                     }
570                 }
571                 if (nextReq === null || typeof nextReq === 'undefined') {
572                     return;
573                 }
574                 if ((typeof nextReq.xmlReq !== 'undefined' && nextReq.xmlReq !== null) &&
575                     nextReq.xmlReq.readyState === 0) {
576                     nextReq.fromQueue = true;
577                     nextReq.sendRequest();
578                 }
579             };
580 
581             /**
582              * Utility method that accepts additional arguments for the AjaxEngine.
583              * If an argument is passed in that matches an AjaxEngine property, the
584              * argument value becomes the value of the AjaxEngine property.
585              * Arguments that don't match AjaxEngine properties are added as
586              * request parameters.
587              * @ignore
588              */
589             req.setupArguments = function(args) {
590                 for (var i in args) {
591                     if (args.hasOwnProperty(i)) {
592                         if (typeof req[i] === 'undefined') {
593                             req.parameters[i] = args[i];
594                         } else {
595                             req[i] = args[i];
596                         }
597                     }
598                 }
599             };
600 
601             /**
602              * This function does final encoding of parameters, determines the request method
603              * (GET or POST) and sends the request using the specified url.
604              * @ignore
605              */
606             req.sendRequest = function() {
607                 if (req.xmlReq !== null) {
608                     // if there is already a request on the queue waiting to be processed..
609                     // just queue this request
610                     if (!req.que.isEmpty()) {
611                         if (!req.fromQueue) {
612                             req.que.enqueue(req);
613                             return;
614                         }
615                     }
616                     // If the queue is empty, queue up this request and send
617                     if (!req.fromQueue) {
618                         req.que.enqueue(req);
619                     }
620                     // Some logic to get the real request URL
621                     if (req.generateUniqueUrl && req.method == "GET") {
622                         req.parameters["AjaxRequestUniqueId"] = new Date().getTime() + "" + req.requestIndex;
623                     }
624                     var content = null; // For POST requests, to hold query string
625                     for (var i in req.parameters) {
626                         if (req.parameters.hasOwnProperty(i)) {
627                             if (req.queryString.length > 0) {
628                                 req.queryString += "&";
629                             }
630                             req.queryString += encodeURIComponent(i) + "=" + encodeURIComponent(req.parameters[i]);
631                         }
632                     }
633                     if (req.method === "GET") {
634                         if (req.queryString.length > 0) {
635                             req.url += ((req.url.indexOf("?") > -1) ? "&" : "?") + req.queryString;
636                         }
637                     }
638                     req.xmlReq.open(req.method, req.url, req.async);
639                     if (req.method === "POST") {
640                         if (typeof req.xmlReq.setRequestHeader !== 'undefined') {
641                             req.xmlReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
642                         }
643                         content = req.queryString;
644                     }
645                     sendEvent(req.xmlReq, req.context, "begin");
646                     req.xmlReq.send(content);
647                 }
648             };
649 
650             return req;
651         };
652 
653         /**
654          * Error handling callback.
655          * Assumes that the request has completed.
656          * @ignore
657          */
658         var sendError = function sendError(request, context, name, serverErrorName, serverErrorMessage) {
659 
660             // Possible errornames:
661             // httpError
662             // emptyResponse
663             // serverError  RELEASE_PENDING or facesError?
664             // malformedXML
665 
666             var sent = false;
667             var data = {};  // data payload for function
668             data.type = "error";
669             data.name = name;
670             // RELEASE_PENDING won't work in response
671             data.source = context.source;
672             data.responseCode = request.status;
673             // RELEASE_PENDING pass a copy, not a reference
674             data.responseXML = request.responseXML;
675             // RELEASE_PENDING won't work in response
676             data.responseText = request.responseText;
677 
678             if (name == "serverError") {
679                 data.errorName = serverErrorName;
680                 data.errorMessage = serverErrorMessage;
681             }
682 
683             // If we have a registered callback, send the error to it.
684             if (context.onerror) {
685                 context.onerror.call(null, data);
686                 sent = true;
687             }
688 
689             for (var i in errorListeners) {
690                 if (errorListeners.hasOwnProperty(i)) {
691                     errorListeners[i].call(null, data);
692                     sent = true;
693                 }
694             }
695 
696             if (!sent && jsf.getProjectStage() === "Development") {
697                 switch (name) {
698                     case "httpError":
699                         alert("httpError " + request.status);
700                         break;
701                     case "serverError":
702                         alert("serverError: " + serverErrorName + " " + serverErrorMessage);
703                         break;
704                     default:
705                         alert("Error " + name);
706                         break;
707                 }
708             }
709         };
710 
711         /**
712          * Event handling callback.
713          * Request is assumed to have completed, except in the case of event = 'begin'.
714          * @ignore
715          */
716         var sendEvent = function sendEvent(request, context, name) {
717 
718             var data = {};
719             data.type = "event";
720             data.name = name;
721             data.source = context.source;
722             if (name !== 'begin') {
723                 data.responseCode = request.status;
724                 // RELEASE_PENDING pass a copy, not a reference
725                 data.responseXML = request.responseXML;
726                 data.responseText = request.responseText;
727             }
728 
729             if (context.onevent) {
730                 context.onevent.call(null, data);
731             }
732 
733             for (var i in eventListeners) {
734                 if (eventListeners.hasOwnProperty(i)) {
735                     eventListeners[i].call(null, data);
736                 }
737             }
738         };
739 
740         // Use module pattern to return the functions we actually expose
741         return {
742             /**
743              * Register a callback for error handling.
744              * <p><b>Usage:</b></p>
745              * <pre><code>
746              * jsf.ajax.addOnError(handleError);
747              * ...
748              * var handleError = function handleError(data) {
749              * ...
750              * }
751              * </pre></code>
752              * <p><b>Implementation Requirements:</b></p>
753              * This function must accept a reference to an existing JavaScript function.
754              * The JavaScript function reference must be added to a list of callbacks, making it possible
755              * to register more than one callback by invoking <code>jsf.ajax.addOnError</code>
756              * more than once.  This function must throw an error if the <code>callback</code>
757              * argument is not a function.
758              *
759              * @member jsf.ajax
760              * @param callback a reference to a function to call on an error
761              */
762             addOnError: function addOnError(callback) {
763                 if (typeof callback === 'function') {
764                     errorListeners[errorListeners.length] = callback;
765                 } else {
766                     throw new Error("jsf.ajax.addOnError:  Added a callback that was not a function.");
767                 }
768             },
769             /**
770              * Register a callback for event handling.
771              * <p><b>Usage:</b></p>
772              * <pre><code>
773              * jsf.ajax.addOnEvent(statusUpdate);
774              * ...
775              * var statusUpdate = function statusUpdate(data) {
776              * ...
777              * }
778              * </pre></code>
779              * <p><b>Implementation Requirements:</b></p>
780              * This function must accept a reference to an existing JavaScript function.
781              * The JavaScript function reference must be added to a list of callbacks, making it possible
782              * to register more than one callback by invoking <code>jsf.ajax.addOnEvent</code>
783              * more than once.  This function must throw an error if the <code>callback</code>
784              * argument is not a function.
785              *
786              * @member jsf.ajax
787              * @param callback a reference to a function to call on an event
788              */
789             addOnEvent: function addOnEvent(callback) {
790                 if (typeof callback === 'function') {
791                     eventListeners[eventListeners.length] = callback;
792                 } else {
793                     throw new Error("jsf.ajax.addOnEvent: Added a callback that was not a function");
794                 }
795             },
796             /**
797              * <p>Send an asynchronous Ajax request to the server.
798              * <p><b>Usage:</b></p>
799              * <pre><code>
800              * Example showing all optional arguments:
801              *
802              * <commandButton id="button1" value="submit"
803              *     onclick="jsf.ajax.request(this,event,
804              *       {execute:'button1',render:'status',onevent: handleEvent,onerror: handleError});return false;"/>
805              * </commandButton/>
806              * </pre></code>
807              * <p><b>Implementation Requirements:</b></p>
808              * This function must:
809              * <ul>
810              * <li>Capture the element that triggered this Ajax request
811              * (from the <code>source</code> argument, also known as the
812              * <code>source</code> element.</li>
813              * <li>If the <code>source</code> element is <code>null</code> or
814              * <code>undefined</code> throw an error.</li>
815              * <li>If the <code>source</code> argument is not a <code>string</code> or
816              * DOM element object, throw an error.</li>
817              * <li>If the <code>source</code> argument is a <code>string</code>, find the
818              * DOM element for that <code>string</code> identifier.
819              * <li>If the DOM element could not be determined, throw an error.</li>
820              * <li>If the <code>onerror</code> and <code>onevent</code> arguments are set,
821              * they must be functions, or throw an error.
822              * <li>Determine the <code>source</code> element's <code>form</code>
823              * element.</li>
824              * <li>Get the <code>form</code> view state by calling
825              * {@link jsf.viewState} passing the
826              * <code>form</code> element as the argument.</li>
827              * <li>Collect post data arguments for the Ajax request.
828              * <ul>
829              * <li>The following name/value pairs are required post data arguments:
830              * <ul>
831              * <li><code>javax.faces.partial.source</code> with the value as the
832              * source element identifier.</li>
833              * <li>The name and value of the <code>source</code> element that
834              * triggered this request;</li>
835              * <li><code>javax.faces.partial.ajax</code> with the value
836              * <code>true</code></li>
837              * </ul>
838              * </li>
839              * </ul>
840              * </li>
841              * <li>Collect optional post data arguments for the Ajax request.
842              * <ul>
843              * <li>Determine additional arguments (if any) from the <code>options</code>
844              * argument. If <code>options.execute</code> exists, create the post data argument
845              * with the name <code>javax.faces.partial.execute</code> and the value as a
846              * space delimited <code>string</code> of client identifiers.  If
847              * <code>options.render</code> exists, create the post data argument with the name
848              * <code>javax.faces.partial.render</code> and the value as a space delimited
849              * <code>string</code> of client identifiers.</li>
850              * <li>Determine additional arguments (if any) from the <code>event</code>
851              * argument.  The following name/value pairs may be used from the
852              * <code>event</code> object:
853              * <ul>
854              * <li><code>target</code> - the ID of the element that triggered the event.</li>
855              * <li><code>captured</code> - the ID of the element that captured the event.</li>
856              * <li><code>type</code> - the type of event (ex: onkeypress)</li>
857              * <li><code>alt</code> - <code>true</code> if ALT key was pressed.</li>
858              * <li><code>ctrl</code> - <code>true</code> if CTRL key was pressed.</li>
859              * <li><code>shift</code> - <code>true</code> if SHIFT key was pressed. </li>
860              * <li><code>meta</code> - <code>true</code> if META key was pressed. </li>
861              * <li><code>right</code> - <code>true</code> if right mouse button
862              * was pressed. </li>
863              * <li><code>left</code> - <code>true</code> if left mouse button
864              * was pressed. </li>
865              * <li><code>keycode</code> - the key code.
866              * </ul>
867              * </li>
868              * </ul>
869              * </li>
870              * <li>Encode the set of post data arguments.</li>
871              * <li>Join the encoded view state with the encoded set of post data arguments
872              * to form the <code>query string</code> that will be sent to the server.</li>
873              * <li>Create a request <code>context</code> object and set the properties:
874              * <ul><li><code>source</code> (the source DOM element for this request)</li>
875              * <li><code>onerror</code> (the error handler for this request)</li>
876              * <li><code>onevent</code> (the event handler for this request)</li></ul>
877              * The request context will be used during error/event handling.</li>
878              * <li>Send a <code>begin</code> event following the procedure as outlined
879              * in the Chapter 13 "Sending Events" section of the spec prose document <a
880              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
881              *  overview summary</a></li>
882              * <li>Send the request as an <code>asynchronous POST</code> using the
883              * <code>action</code> property of the <code>form</code> element as the
884              * <code>url</code>.</li>
885              * </ul>
886              * Before the request is sent it must be put into a queue to ensure requests
887              * are sent in the same order as when they were initiated.  The request callback function
888              * must examine the queue and determine the next request to be sent.  The behavior of the
889              * request callback function must be as follows:
890              * <ul>
891              * <li>If the request completed successfully invoke {@link jsf.ajax.response}
892              * passing the <code>request</code> object.</li>
893              * <li>If the request did not complete successfully, notify the client.</li>
894              * <li>Regardless of the outcome of the request (success or error) every request in the
895              * queue must be handled.  Examine the status of each request in the queue starting from
896              * the request that has been in the queue the longest.  If the status of the request is
897              * <code>complete</code> (readyState 4), dequeue the request (remove it from the queue).
898              * If the request has not been sent (readyState 0), send the request.  Requests that are
899              * taken off the queue and sent should not be put back on the queue.</li>
900              * </ul>
901              *
902              * </p>
903              *
904              * @param source The DOM element that triggered this Ajax request, or an id string of the
905              * element to use as the triggering element.
906              * @param event The DOM event that triggered this Ajax request.  The
907              * <code>event</code> argument is optional.
908              * @param options The set of available options that can be sent as
909              * request parameters to control client and/or server side
910              * request processing. Acceptable name/value pair options are:
911              * <table border="1">
912              * <tr>
913              * <th>name</th>
914              * <th>value</th>
915              * </tr>
916              * <tr>
917              * <td><code>execute</code></td>
918              * <td><code>space seperated list of client identifiers</code></td>
919              * </tr>
920              * <tr>
921              * <td><code>render</code></td>
922              * <td><code>space seperated list of client identifiers</code></td>
923              * </tr>
924              * <tr>
925              * <td><code>onevent</code></td>
926              * <td><code>function to callback for event</code></td>
927              * </tr>
928              * <tr>
929              * <td><code>onerror</code></td>
930              * <td><code>function to callback for error</code></td>
931              * </tr>
932              * </table>
933              * The <code>options</code> argument is optional.
934              * @member jsf.ajax
935              * @function jsf.ajax.request
936              * @throws ArgNotSet Error if first required argument <code>element</code> is not specified
937              */
938             request: function request(source, event, options) {
939 
940                 var element;
941 
942                 if (typeof source === 'undefined' || source === null) {
943                     throw new Error("jsf.ajax.request: source not set");
944                 }
945                 if (typeof source === 'string') {
946                     element = document.getElementById(source);
947                 } else if (typeof source === 'object') {
948                     element = source;
949                 } else {
950                     throw new Error("jsf.request: source must be object or string");
951                 }
952 
953                 if (typeof(options) === 'undefined' || options === null) {
954                     options = {};
955                 }
956 
957                 // Error handler for this request
958                 var onerror = false;
959 
960                 if (options.onerror && typeof options.onerror === 'function') {
961                     onerror = options.onerror;
962                 } else if (options.onerror && typeof options.onerror !== 'function') {
963                     throw new Error("jsf.ajax.request: Added an onerror callback that was not a function");
964                 }
965 
966                 // Event handler for this request
967                 var onevent = false;
968 
969                 if (options.onevent && typeof options.onevent === 'function') {
970                     onevent = options.onevent;
971                 } else if (options.onevent && typeof options.onevent !== 'function') {
972                     throw new Error("jsf.ajax.request: Added an onevent callback that was not a function");
973                 }
974 
975                 var form = getForm(element);
976                 var viewState = jsf.getViewState(form);
977 
978                 // Set up additional arguments to be used in the request..
979                 // Make sure "javax.faces.partial.source" is set up.
980                 // If there were "execute" ids specified, make sure we
981                 // include the identifier of the source element in the
982                 // "execute" list.  If there were no "execute" ids
983                 // specified, determine the default.
984 
985                 var args = {};
986 
987                 args["javax.faces.partial.source"] = element.id;
988 
989                 // RELEASE_PENDING Get rid of commas.  It's supposed to be spaces.
990                 if (options.execute) {
991                     var temp = toArray(options.execute, ',');
992                     // RELEASE_PENDING remove isInArray function
993                     if (!isInArray(temp, element.name)) {
994                         options.execute = element.name + "," + options.execute;
995                     }
996                 } else {
997                     options.execute = element.id;
998                 }
999 
1000                 args["javax.faces.partial.execute"] = toArray(options.execute, ',').join(',');
1001                 if (options.render) {
1002                     args["javax.faces.partial.render"] = toArray(options.render, ',').join(',');
1003                 }
1004 
1005                 // remove non-passthrough options
1006                 delete options.execute;
1007                 delete options.render;
1008                 delete options.onerror;
1009                 delete options.onevent;
1010                 // copy all other options to args
1011                 for (var property in options) {
1012                     if (options.hasOwnProperty(property)) {
1013                         args[property] = options[property];
1014                     }
1015                 }
1016 
1017                 args["javax.faces.partial.ajax"] = "true";
1018                 args["method"] = "POST";
1019                 args["url"] = form.action;
1020                 // add source
1021                 var action = $(element);
1022                 if (action && action.form) {
1023                     args[action.name] = action.value || 'x';
1024                 } else {
1025                     args[element] = element;
1026                 }
1027 
1028                 var ajaxEngine = new AjaxEngine();
1029                 ajaxEngine.setupArguments(args);
1030                 ajaxEngine.queryString = viewState;
1031                 ajaxEngine.context.onevent = onevent;
1032                 ajaxEngine.context.onerror = onerror;
1033                 ajaxEngine.context.source = element;
1034                 ajaxEngine.sendRequest();
1035             },
1036             /**
1037              * <p>Receive an Ajax response from the server.
1038              * <p><b>Usage:</b></p>
1039              * <pre><code>
1040              * jsf.ajax.response(request, context);
1041              * </pre></code>
1042              * <p><b>Implementation Requirements:</b></p>
1043              * This function must evaluate the markup returned in the
1044              * <code>request.responseXML</code> object and perform the following action:
1045              * <ul>
1046              * <p>If there is no XML response returned, signal an <code>emptyResponse</code>
1047              * error. If the XML response does not follow the format as outlined
1048              * in Appendix A of the spec prose document <a
1049              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
1050              *  overview summary</a> signal a <code>malformedError</code> error.  Refer to
1051              * section "Signaling Errors" in Chapter 13 of the spec prose document <a
1052              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
1053              *  overview summary</a>.</p>
1054              * <p>If the response was successfully processed, send a <code>success</code>
1055              * event as outlined in Chapter 13 "Sending Events" section of the spec prose
1056              * document <a
1057              * href="../../javadocs/overview-summary.html#prose_document">linked in the
1058              * overview summary</a>.</p>
1059              * <p><i>Update Element Processing</i></p>
1060              * <li>If an <code>update</code> element is found in the response
1061              * with the identifier <code>javax.faces.ViewRoot</code>:
1062              * <pre><code><update id="javax.faces.ViewRoot">
1063              *    <![CDATA[...]]>
1064              * </update></code></pre>
1065              * Update the entire DOM as follows:
1066              * <ul>
1067              * <li>Extract the <code>CDATA</code> content and trim the <html>
1068              * and </html> from the <code>CDATA</code> content if it is present.</li>
1069              * <li>If the <code>CDATA</code> content contains a <head> element,
1070              * and the document has a <code><head></code> section, extract the
1071              * contents of the <head> element from the <code><update></code>
1072              * element's <code>CDATA</code> content and replace the document's <head>
1073              * section with this contents.</li>
1074              * <li>If the <code>CDATA</code> content contains a <body> element,
1075              * and the document has a <code><body></code> section, extract the contents
1076              * of the <body> element from the <code><update></code>
1077              * element's <code>CDATA</code> content and replace the document's <body>
1078              * section with this contents.</li>
1079              * <li>If the <code>CDATA</code> content does not contain a <body> element,
1080              * replace the document's <body> section with the <code>CDATA</code>
1081              * contents.</li>
1082              * </ul>
1083              * <li>If an <code>update</code> element is found in the response with the identifier
1084              * <code>javax.faces.ViewState</code>:
1085              * <pre><code><update id="javax.faces.ViewState">
1086              *    <![CDATA[...]]>
1087              * </update></code></pre>
1088              * Include this <code>state</code> in the document as follows:
1089              * <ul>
1090              * <li>Extract this <code><update></code> element's <code>CDATA</code> contents
1091              * from the response.</li>
1092              * <li>If the document contains an element with the identifier
1093              * <code>javax.faces.ViewState</code> replace its contents with the
1094              * <code>CDATA</code> contents.</li>
1095              * <li>For each <code><form></code> element in the document:
1096              * <ul>
1097              * <li>If the <code><form></code> element contains an <code><input></code>
1098              * element with the identifier <code>javax.faces.ViewState</code>, replace the
1099              * <code><input></code> element contents with the <code><update></code>
1100              * element's <code>CDATA</code> contents.</li>
1101              * <li>If the <code><form></code> element does not contain an element with
1102              * the identifier <code>javax.faces.ViewState</code>, create an
1103              * <code><input></code> element of the type <code>hidden</code>,
1104              * with the identifier <code>javax.faces.ViewState</code>, set its contents
1105              * to the <code><update></code> element's <code>CDATA</code> contents, and
1106              * add the <code><input></code> element as a child to the
1107              * <code><form></code> element.</li>
1108              * </ul>
1109              * </li>
1110              * </ul>
1111              * </li>
1112              * <li>For any other <code><update></code> element:
1113              * <pre><code><update id="update id">
1114              *    <![CDATA[...]]>
1115              * </update></code></pre>
1116              * Find the DOM element with the identifier that matches the
1117              * <code><update></code> element identifier, and replace its contents with
1118              * the <code><update></code> element's <code>CDATA</code> contents.</li>
1119              * </li>
1120              * <p><i>Insert Element Processing</i></p>
1121              * <li>If an <code><insert></code> element is found in the response with the
1122              * attribute <code>before</code>:
1123              * <pre><code><insert id="insert id" before="before id">
1124              *    <![CDATA[...]]>
1125              * </insert></code></pre>
1126              * <ul>
1127              * <li>Extract this <code><insert></code> element's <code>CDATA</code> contents
1128              * from the response.</li>
1129              * <li>Find the DOM element whose identifier matches <code>before id</code> and insert
1130              * the <code><insert></code> element's <code>CDATA</code> content before
1131              * the DOM element in the document.</li>
1132              * </ul>
1133              * </li>
1134              * <li>If an <code><insert></code> element is found in the response with the
1135              * attribute <code>after</code>:
1136              * <pre><code><insert id="insert id" after="after id">
1137              *    <![CDATA[...]]>
1138              * </insert></code></pre>
1139              * <ul>
1140              * <li>Extract this <code><insert></code> element's <code>CDATA</code> contents
1141              * from the response.</li>
1142              * <li>Find the DOM element whose identifier matches <code>after id</code> and insert
1143              * the <code><insert></code> element's <code>CDATA</code> content after
1144              * the DOM element in the document.</li>
1145              * </ul>
1146              * </li>
1147              * <p><i>Delete Element Processing</i></p>
1148              * <li>If a <code><delete></code> element is found in the response:
1149              * <pre><code><delete id="delete id"/></code></pre>
1150              * Find the DOM element whose identifier matches <code>delete id</code> and remove it
1151              * from the DOM.</li>
1152              * <p><i>Element Attribute Update Processing</i></p>
1153              * <li>If an <code><attributes></code> element is found in the response:
1154              * <pre><code><attributes id="id of element with attribute">
1155              *    <attribute name="attribute name" value="attribute value">
1156              *    ...
1157              * </attributes></code></pre>
1158              * <ul>
1159              * <li>Find the DOM element that matches the <code><attributes></code> identifier.</li>
1160              * <li>For each nested <code><attribute></code> element in <code><attribute></code>,
1161              * update the DOM element attribute value (whose name matches <code>attribute name</code>),
1162              * with <code>attribute value</code>.</li>
1163              * </ul>
1164              * </li>
1165              * <p><i>JavaScript Processing</i></p>
1166              * <li>If an <code><eval></code> element is found in the response:
1167              * <pre><code><eval>
1168              *    <![CDATA[...JavaScript...]]>
1169              * </eval></code></pre>
1170              * <ul>
1171              * <li>Extract this <code><eval></code> element's <code>CDATA</code> contents
1172              * from the response and execute it as if it were JavaScript code.</li>
1173              * </ul>
1174              * </li>
1175              * <p><i>Redirect Processing</i></p>
1176              * <li>If a <code><redirect></code> element is found in the response:
1177              * <pre><code><redirect url="redirect url"/></code></pre>
1178              * Cause a redirect to the url <code>redirect url</code>.</li>
1179              * <p><i>Error Processing</i></p>
1180              * <li>If an <code><error></code> element is found in the response:
1181              * <pre><code><error>
1182              *    <error-name>..fully qualified class name string...<error-name>
1183              *    <error-message><![CDATA[...]]><error-message>
1184              * </error></code></pre>
1185              * Extract this <code><error></code> element's <code>error-name</code> contents
1186              * and the <code>error-message</code> contents. Signal a <code>serverError</code> passing
1187              * the <code>errorName</code> and <code>errorMessage</code>.  Refer to
1188              * section "Signaling Errors" in Chapter 13 of the spec prose document <a
1189              *  href="../../javadocs/overview-summary.html#prose_document">linked in the
1190              *  overview summary</a>.</li>
1191              * <p><i>Extensions</i></p>
1192              * <li>The <code><extensions></code> element provides a way for framework
1193              * implementations to provide their own information.</li>
1194              * </ul>
1195              *
1196              * </p>
1197              *
1198              * @param request The <code>XMLHttpRequest</code> instance that
1199              * contains the status code and response message from the server.
1200              *
1201              * @param context An object containing the request context, including the following properties:
1202              * the source element, per call onerror callback function, and per call onevent callback function.
1203              *
1204              * @throws EmptyResponse error if request contains no data
1205              *
1206              * @function jsf.ajax.response
1207              */
1208             response: function response(request, context) {
1209                 if (!request) {
1210                     throw new Error("jsf.ajax.response: Request parameter is unset");
1211                 }
1212 
1213                 var xmlReq = request;
1214 
1215                 var xml = xmlReq.responseXML;
1216                 if (xml === null) {
1217                     sendError(request, context, "emptyResponse");
1218                 }
1219 
1220 
1221                 var responseType = xml.getElementsByTagName("partial-response")[0].firstChild;
1222 
1223                 if (responseType.nodeName === "error") { // it's an error
1224                     var errorName = responseType.firstChild.firstChild.nodeValue;
1225                     var errorMessage = responseType.firstChild.nextSibling.firstChild.nodeValue;
1226                     sendError(request, context, "serverError", errorName, errorMessage);
1227                     return;
1228                 }
1229 
1230 
1231                 if (responseType.nodeName === "redirect") {
1232                     window.location = responseType.getAttribute("url");
1233                     return;
1234                 }
1235 
1236 
1237                 if (responseType.nodeName !== "changes") {
1238                     sendError(request, context, "malformedXML");
1239                     return;
1240                 }
1241 
1242 
1243                 var changes = responseType.childNodes;
1244 
1245 
1246                 for (var i = 0; i < changes.length; i++) {
1247                     switch (changes[i].nodeName) {
1248                         case "update":
1249                             doUpdate(changes[i]);
1250                             break;
1251                         case "delete":
1252                             doDelete(changes[i]);
1253                             break;
1254                         case "insert":
1255                             doInsert(changes[i]);
1256                             break;
1257                         case "attributes":
1258                             doAttributes(changes[i]);
1259                             break;
1260                         case "eval":
1261                             doEval(changes[i]);
1262                             break;
1263                         case "extension":
1264                         // RELEASE_PENDING no action?
1265                             break;
1266                         default:
1267                             sendError(request, context, "malformedXML");
1268                             return;
1269                     }
1270                 }
1271                 sendEvent(request, context, "success");
1272 
1273                 //////////////////////
1274                 // Check for updates..
1275                 //////////////////////
1276 
1277                 //////////////////////
1278                 // Check For Inserts.
1279                 //////////////////////
1280 
1281                 //////////////////////
1282                 // Check For Deletes.
1283                 //////////////////////
1284 
1285                 //////////////////////
1286                 // Update Attributes.
1287                 //////////////////////
1288 
1289                 //////////////////////
1290                 // JavaScript Eval.
1291                 //////////////////////
1292 
1293             }
1294         };
1295     }();
1296 
1297     /**
1298      *
1299      * <p>Return the value of <code>Application.getProjectStage()</code> for
1300      * the currently running application instance.  Calling this method must
1301      * not cause any network transaction to happen to the server.</p>
1302      * <p><b>Usage:</b></p>
1303      * <pre><code>
1304      * var stage = jsf.getProjectStage();
1305      * if (stage === ProjectStage.Development) {
1306      *  ...
1307      * } else if stage === ProjectStage.Production) {
1308      *  ...
1309      * }
1310      * </code></pre>
1311      *
1312      * @returns String <code>String</code> representing the current state of the
1313      * running application in a typical product development lifecycle.  Refer
1314      * to <code>javax.faces.application.Application.getProjectStage</code> and
1315      * <code>javax.faces.application.ProjectStage</code>.
1316      * @function jsf.getProjectStage
1317      */
1318     jsf.getProjectStage = function() {
1319         return "#{facesContext.application.projectStage}";
1320     };
1321 
1322 
1323     /**
1324      * <p>Collect and encode state for input controls associated
1325      * with the specified <code>form</code> element.</p>
1326      * <p><b>Usage:</b></p>
1327      * <pre><code>
1328      * var state = jsf.getViewState(form);
1329      * </pre></code>
1330      *
1331      * @param form The <code>form</code> element whose contained
1332      * <code>input</code> controls will be collected and encoded.
1333      * Only successful controls will be collected and encoded in
1334      * accordance with: <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2">
1335      * Section 17.13.2 of the HTML Specification</a>.
1336      *
1337      * @returns String The encoded state for the specified form's input controls.
1338      * @function jsf.getViewState
1339      */
1340     jsf.getViewState = function(form) {
1341         var els = form.elements;
1342         var len = els.length;
1343         var qString = "";
1344         var addField = function(name, value) {
1345             if (qString.length > 0) {
1346                 qString += "&";
1347             }
1348             qString += encodeURIComponent(name) + "=" + encodeURIComponent(value);
1349         };
1350         for (var i = 0; i < len; i++) {
1351             var el = els[i];
1352             if (!el.disabled) {
1353                 switch (el.type) {
1354                     case 'text':
1355                     case 'password':
1356                     case 'hidden':
1357                     case 'textarea':
1358                         addField(el.name, el.value);
1359                         break;
1360                     case 'select-one':
1361                         if (el.selectedIndex >= 0) {
1362                             addField(el.name, el.options[el.selectedIndex].value);
1363                         }
1364                         break;
1365                     case 'select-multiple':
1366                         for (var j = 0; j < el.options.length; j++) {
1367                             if (el.options[j].selected) {
1368                                 addField(el.name, el.options[j].value);
1369                             }
1370                         }
1371                         break;
1372                     case 'checkbox':
1373                     case 'radio':
1374                         addField(el.name, el.checked + "");
1375                         break;
1376                 }
1377             }
1378         }
1379         return qString;
1380     };
1381 
1382     /**
1383      * An integer specifying the specification version that this file implements.
1384      * It's format is: rightmost two digits, bug release number, next two digits,
1385      * minor release number, leftmost digits, major release number.
1386      * This number may only be incremented by a new release of the specification.
1387      * @ignore
1388      */
1389     jsf.specversion = 20000;
1390 
1391     /**
1392      * An integer specifying the implementation version that this file implements.
1393      * It's a monotonically increasing number, reset with every increment of
1394      * <code>jsf.specversion</code>
1395      * This number is implementation dependent.
1396      * @ignore
1397      */
1398     jsf.implversion = 1;
1399 
1400 
1401 } //end if version detection block
1402