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