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