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