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