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