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