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