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