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