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