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