1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright (c) 1997-2011 Oracle and/or its affiliates. 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 10 * obtain a copy of the License at 11 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html 12 * or packager/legal/LICENSE.txt. See the License for the specific 13 * language governing permissions and limitations under the License. 14 * 15 * When distributing the software, include this License Header Notice in each 16 * file and include the License file at packager/legal/LICENSE.txt. 17 * 18 * GPL Classpath Exception: 19 * Oracle designates this particular file as subject to the "Classpath" 20 * exception as provided by Oracle in the GPL Version 2 section of the License 21 * file that accompanied this code. 22 * 23 * Modifications: 24 * If applicable, add the following below the License Header, with the fields 25 * enclosed by brackets [] replaced by your own identifying information: 26 * "Portions Copyright [year] [name of copyright owner]" 27 * 28 * Contributor(s): 29 * If you wish your version of this file to be governed by only the CDDL or 30 * only the GPL Version 2, indicate your decision by adding "[Contributor] 31 * elects to include this software in this distribution under the [CDDL or GPL 32 * Version 2] license." If you don't indicate a single choice of license, a 33 * recipient has the option to distribute your version of this file under 34 * either the CDDL, the GPL Version 2 or to extend the choice of license to 35 * its licensees as provided above. However, if you add GPL Version 2 code 36 * and therefore, elected the GPL Version 2 license, then the option applies 37 * only if the new code is made subject to such option by the copyright 38 * holder. 39 * 40 * 41 * This file incorporates work covered by the following copyright and 42 * permission notices: 43 * 44 * Copyright 2004 The Apache Software Foundation 45 * Copyright 2004-2008 Emmanouil Batsis, mailto: mbatsis at users full stop sourceforge full stop net 46 * 47 * Licensed under the Apache License, Version 2.0 (the "License"); 48 * you may not use this file except in compliance with the License. 49 * You may obtain a copy of the License at 50 * 51 * http://www.apache.org/licenses/LICENSE-2.0 52 * 53 * Unless required by applicable law or agreed to in writing, software 54 * distributed under the License is distributed on an "AS IS" BASIS, 55 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 56 * See the License for the specific language governing permissions and 57 * limitations under the License. 58 */ 59 60 /** 61 @project JSF JavaScript Library 62 @version 2.0 63 @description This is the standard implementation of the JSF JavaScript Library. 64 */ 65 66 /** 67 * Register with OpenAjax 68 */ 69 if (typeof OpenAjax !== "undefined" && 70 typeof OpenAjax.hub.registerLibrary !== "undefined") { 71 OpenAjax.hub.registerLibrary("jsf", "www.sun.com", "2.0", null); 72 } 73 74 // Detect if this is already loaded, and if loaded, if it's a higher version 75 if (!((jsf && jsf.specversion && jsf.specversion > 20000 ) && 76 (jsf.implversion && jsf.implversion > 2))) { 77 78 /** 79 * The top level global namespace for JavaServer Faces functionality. 80 * @name jsf 81 * @namespace 82 */ 83 var jsf = {}; 84 85 /** 86 * The namespace for Ajax functionality. 87 * @name jsf.ajax 88 * @namespace 89 * @exec 90 */ 91 jsf.ajax = function() { 92 93 var eventListeners = []; 94 var errorListeners = []; 95 96 /** 97 * Determine if the current browser is part of Microsoft's failed attempt at 98 * standards modification. 99 * @ignore 100 */ 101 var isIE = function isIE() { 102 if (typeof isIECache !== "undefined") { 103 return isIECache; 104 } 105 isIECache = 106 document.all && window.ActiveXObject && 107 navigator.userAgent.toLowerCase().indexOf("msie") > -1 && 108 navigator.userAgent.toLowerCase().indexOf("opera") == -1; 109 return isIECache; 110 }; 111 var isIECache; 112 113 /** 114 * Determine if loading scripts into the page executes the script. 115 * This is instead of doing a complicated browser detection algorithm. Some do, some don't. 116 * @returns {boolean} does including a script in the dom execute it? 117 * @ignore 118 */ 119 var isAutoExecCache; 120 var isAutoExec = function isAutoExec() { 121 try { 122 if (typeof isAutoExecCache !== "undefined") { 123 return isAutoExecCache; 124 } 125 var autoExecTestString = "<script>var mojarra = mojarra || {};mojarra.autoExecTest = true;</script>"; 126 var tempElement = document.createElement('span'); 127 tempElement.innerHTML = autoExecTestString; 128 var body = document.getElementsByTagName('body')[0]; 129 var tempNode = body.appendChild(tempElement); 130 if (mojarra && mojarra.autoExecTest) { 131 isAutoExecCache = true; 132 delete mojarra.autoExecTest; 133 } else { 134 isAutoExecCache = false; 135 } 136 deleteNode(tempNode); 137 return isAutoExecCache; 138 } catch (ex) { 139 // OK, that didn't work, we'll have to make an assumption 140 if (typeof isAutoExecCache === "undefined") { 141 isAutoExecCache = false; 142 } 143 return isAutoExecCache; 144 } 145 }; 146 147 /** 148 * @ignore 149 */ 150 var getTransport = function getTransport() { 151 var methods = [ 152 function() { 153 return new XMLHttpRequest(); 154 }, 155 function() { 156 return new ActiveXObject('Msxml2.XMLHTTP'); 157 }, 158 function() { 159 return new ActiveXObject('Microsoft.XMLHTTP'); 160 } 161 ]; 162 163 var returnVal; 164 for (var i = 0, len = methods.length; i < len; i++) { 165 try { 166 returnVal = methods[i](); 167 } catch(e) { 168 continue; 169 } 170 return returnVal; 171 } 172 throw new Error('Could not create an XHR object.'); 173 }; 174 175 /** 176 * Find instance of passed String via getElementById 177 * @ignore 178 */ 179 var $ = function $() { 180 var results = [], element; 181 for (var i = 0; i < arguments.length; i++) { 182 element = arguments[i]; 183 if (typeof element == 'string') { 184 element = document.getElementById(element); 185 } 186 results.push(element); 187 } 188 return results.length > 1 ? results : results[0]; 189 }; 190 191 /** 192 * Get the form element which encloses the supplied element. 193 * @param element - element to act against in search 194 * @returns form element representing enclosing form, or first form if none found. 195 * @ignore 196 */ 197 var getForm = function getForm(element) { 198 if (element) { 199 var form = $(element); 200 while (form) { 201 202 if (form.nodeName && (form.nodeName.toLowerCase() == 'form')) { 203 return form; 204 } 205 if (form.form) { 206 return form.form; 207 } 208 if (form.parentNode) { 209 form = form.parentNode; 210 } else { 211 form = null; 212 } 213 } 214 return document.forms[0]; 215 } 216 return null; 217 }; 218 219 /** 220 * Check if a value exists in an array 221 * @ignore 222 */ 223 var isInArray = function isInArray(array, value) { 224 for (var i = 0; i < array.length; i++) { 225 if (array[i] === value) { 226 return true; 227 } 228 } 229 return false; 230 }; 231 232 233 /** 234 * Evaluate JavaScript code in a global context. 235 * @param src JavaScript code to evaluate 236 * @ignore 237 */ 238 var globalEval = function globalEval(src) { 239 if (window.execScript) { 240 window.execScript(src); 241 return; 242 } 243 // We have to wrap the call in an anon function because of a firefox bug, where this is incorrectly set 244 // We need to explicitly call window.eval because of a Chrome peculiarity 245 var fn = function() { 246 window.eval.call(window,src); 247 }; 248 fn(); 249 }; 250 251 /** 252 * Get all scripts from supplied string, return them as an array for later processing. 253 * @param str 254 * @returns {array} of script text 255 * @ignore 256 */ 257 var stripScripts = function stripScripts(str) { 258 // Regex to find all scripts in a string 259 var findscripts = /<script[^>]*>([\S\s]*?)<\/script>/igm; 260 // Regex to find one script, to isolate it's content [2] and attributes [1] 261 var findscript = /<script([^>]*)>([\S\s]*?)<\/script>/im; 262 // Regex to remove leading cruft 263 var stripStart = /^\s*(<!--)*\s*(\/\/)*\s*(\/\*)*\s*(<!\[CDATA\[)*/; 264 // Regex to find src attribute 265 var findsrc = /src="([\S]*?)"/im; 266 var initialnodes = []; 267 var scripts = []; 268 initialnodes = str.match(findscripts); 269 while (!!initialnodes && initialnodes.length > 0) { 270 var scriptStr = []; 271 scriptStr = initialnodes.shift().match(findscript); 272 var src = []; 273 // check if src specified 274 src = scriptStr[1].match(findsrc); 275 var script; 276 if ( !!src && src[1]) { 277 // if this is a file, load it 278 var url = src[1]; 279 // if this is another copy of jsf.js, don't load it 280 // it's never necessary, and can make debugging difficult 281 if (/\/javax.faces.resource\/jsf.js\?ln=javax\.faces/.test(url)) { 282 script = false; 283 } else { 284 script = loadScript(url); 285 } 286 } else if (!!scriptStr && scriptStr[2]){ 287 // else get content of tag, without leading CDATA and such 288 script = scriptStr[2].replace(stripStart,""); 289 } else { 290 script = false; 291 } 292 if (!!script) { 293 scripts.push(script); 294 } 295 } 296 return scripts; 297 }; 298 299 /** 300 * Load a script via a url, use synchronous XHR request. This is liable to be slow, 301 * but it's probably the only correct way. 302 * @param url the url to load 303 * @ignore 304 */ 305 var loadScript = function loadScript(url) { 306 var xhr = getTransport(); 307 if (xhr === null) { 308 return ""; 309 } 310 311 xhr.open("GET", url, false); 312 xhr.setRequestHeader("Content-Type", "application/x-javascript"); 313 xhr.send(null); 314 315 // PENDING graceful error handling 316 if (xhr.readyState == 4 && xhr.status == 200) { 317 return xhr.responseText; 318 } 319 320 return ""; 321 }; 322 323 /** 324 * Run an array of scripts text 325 * @param scripts array of script nodes 326 * @ignore 327 */ 328 var runScripts = function runScripts(scripts) { 329 if (!scripts || scripts.length === 0) { 330 return; 331 } 332 333 var head = document.getElementsByTagName('head')[0] || document.documentElement; 334 while (scripts.length) { 335 // create script node 336 var scriptNode = document.createElement('script'); 337 scriptNode.type = 'text/javascript'; 338 scriptNode.text = scripts.shift(); // add the code to the script node 339 head.appendChild(scriptNode); // add it to the page 340 head.removeChild(scriptNode); // then remove it 341 } 342 }; 343 344 /** 345 * Replace DOM element with a new tagname and supplied innerHTML 346 * @param element element to replace 347 * @param tempTagName new tag name to replace with 348 * @param src string new content for element 349 * @ignore 350 */ 351 var elementReplaceStr = function elementReplaceStr(element, tempTagName, src) { 352 353 var temp = document.createElement(tempTagName); 354 if (element.id) { 355 temp.id = element.id; 356 } 357 358 // Creating a head element isn't allowed in IE, and faulty in most browsers, 359 // so it is not allowed 360 if (element.nodeName.toLowerCase() === "head") { 361 throw new Error("Attempted to replace a head element - this is not allowed."); 362 } else { 363 var scripts = []; 364 if (isAutoExec()) { 365 temp.innerHTML = src; 366 } else { 367 // Get scripts from text 368 scripts = stripScripts(src); 369 // Remove scripts from text 370 src = src.replace(/<script[^>]*>([\S\s]*?)<\/script>/igm,""); 371 temp.innerHTML = src; 372 } 373 } 374 375 replaceNode(temp, element); 376 runScripts(scripts); 377 378 }; 379 380 /** 381 * Get a string with the concatenated values of all string nodes under the given node 382 * @param oNode the given DOM node 383 * @param deep boolean - whether to recursively scan the children nodes of the given node for text as well. Default is <code>false</code> 384 * @ignore 385 * Note: This code originally from Sarissa: http://dev.abiss.gr/sarissa 386 * It has been modified to fit into the overall codebase 387 */ 388 var getText = function getText(oNode, deep) { 389 var Node = {ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4, 390 ENTITY_REFERENCE_NODE: 5, ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7, 391 COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10, 392 DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12}; 393 394 var s = ""; 395 var nodes = oNode.childNodes; 396 for (var i = 0; i < nodes.length; i++) { 397 var node = nodes[i]; 398 var nodeType = node.nodeType; 399 if (nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE) { 400 s += node.data; 401 } else if (deep === true && (nodeType == Node.ELEMENT_NODE || 402 nodeType == Node.DOCUMENT_NODE || 403 nodeType == Node.DOCUMENT_FRAGMENT_NODE)) { 404 s += getText(node, true); 405 } 406 } 407 return s; 408 }; 409 410 var PARSED_OK = "Document contains no parsing errors"; 411 var PARSED_EMPTY = "Document is empty"; 412 var PARSED_UNKNOWN_ERROR = "Not well-formed or other error"; 413 var getParseErrorText; 414 if (isIE()) { 415 /** 416 * Note: This code orginally from Sarissa: http://dev.abiss.gr/sarissa 417 * @ignore 418 */ 419 getParseErrorText = function (oDoc) { 420 var parseErrorText = PARSED_OK; 421 if (oDoc && oDoc.parseError && oDoc.parseError.errorCode && oDoc.parseError.errorCode !== 0) { 422 parseErrorText = "XML Parsing Error: " + oDoc.parseError.reason + 423 "\nLocation: " + oDoc.parseError.url + 424 "\nLine Number " + oDoc.parseError.line + ", Column " + 425 oDoc.parseError.linepos + 426 ":\n" + oDoc.parseError.srcText + 427 "\n"; 428 for (var i = 0; i < oDoc.parseError.linepos; i++) { 429 parseErrorText += "-"; 430 } 431 parseErrorText += "^\n"; 432 } 433 else if (oDoc.documentElement === null) { 434 parseErrorText = PARSED_EMPTY; 435 } 436 return parseErrorText; 437 }; 438 } else { // (non-IE) 439 440 /** 441 * <p>Returns a human readable description of the parsing error. Useful 442 * for debugging. Tip: append the returned error string in a <pre> 443 * element if you want to render it.</p> 444 * @param oDoc The target DOM document 445 * @returns {String} The parsing error description of the target Document in 446 * human readable form (preformated text) 447 * @ignore 448 * Note: This code orginally from Sarissa: http://dev.abiss.gr/sarissa 449 */ 450 getParseErrorText = function (oDoc) { 451 var parseErrorText = PARSED_OK; 452 if ((!oDoc) || (!oDoc.documentElement)) { 453 parseErrorText = PARSED_EMPTY; 454 } else if (oDoc.documentElement.tagName == "parsererror") { 455 parseErrorText = oDoc.documentElement.firstChild.data; 456 parseErrorText += "\n" + oDoc.documentElement.firstChild.nextSibling.firstChild.data; 457 } else if (oDoc.getElementsByTagName("parsererror").length > 0) { 458 var parsererror = oDoc.getElementsByTagName("parsererror")[0]; 459 parseErrorText = getText(parsererror, true) + "\n"; 460 } else if (oDoc.parseError && oDoc.parseError.errorCode !== 0) { 461 parseErrorText = PARSED_UNKNOWN_ERROR; 462 } 463 return parseErrorText; 464 }; 465 } 466 467 if ((typeof(document.importNode) == "undefined") && isIE()) { 468 try { 469 /** 470 * Implementation of importNode for the context window document in IE. 471 * If <code>oNode</code> is a TextNode, <code>bChildren</code> is ignored. 472 * @param oNode the Node to import 473 * @param bChildren whether to include the children of oNode 474 * @returns the imported node for further use 475 * @ignore 476 * Note: This code orginally from Sarissa: http://dev.abiss.gr/sarissa 477 */ 478 document.importNode = function(oNode, bChildren) { 479 var tmp; 480 if (oNode.nodeName == '#text') { 481 return document.createTextNode(oNode.data); 482 } 483 else { 484 if (oNode.nodeName == "tbody" || oNode.nodeName == "tr") { 485 tmp = document.createElement("table"); 486 } 487 else if (oNode.nodeName == "td") { 488 tmp = document.createElement("tr"); 489 } 490 else if (oNode.nodeName == "option") { 491 tmp = document.createElement("select"); 492 } 493 else { 494 tmp = document.createElement("div"); 495 } 496 if (bChildren) { 497 tmp.innerHTML = oNode.xml ? oNode.xml : oNode.outerHTML; 498 } else { 499 tmp.innerHTML = oNode.xml ? oNode.cloneNode(false).xml : oNode.cloneNode(false).outerHTML; 500 } 501 return tmp.getElementsByTagName("*")[0]; 502 } 503 }; 504 } catch(e) { 505 } 506 } 507 // Setup Node type constants for those browsers that don't have them (IE) 508 var Node = {ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4, 509 ENTITY_REFERENCE_NODE: 5, ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7, 510 COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10, 511 DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12}; 512 513 // PENDING - add support for removing handlers added via DOM 2 methods 514 /** 515 * Delete all events attached to a node 516 * @param node 517 * @ignore 518 */ 519 var clearEvents = function clearEvents(node) { 520 if (!node) { 521 return; 522 } 523 524 // don't do anything for text and comment nodes - unnecessary 525 if (node.nodeType == Node.TEXT_NODE || node.nodeType == Node.COMMENT_NODE) { 526 return; 527 } 528 529 var events = ['abort', 'blur', 'change', 'error', 'focus', 'load', 'reset', 'resize', 'scroll', 'select', 'submit', 'unload', 530 'keydown', 'keypress', 'keyup', 'click', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'dblclick' ]; 531 try { 532 for (var e in events) { 533 if (events.hasOwnProperty(e)) { 534 node[e] = null; 535 } 536 } 537 } catch (ex) { 538 // it's OK if it fails, at least we tried 539 } 540 }; 541 542 /** 543 * Deletes node 544 * @param node 545 * @ignore 546 */ 547 var deleteNode = function deleteNode(node) { 548 if (!node) { 549 return; 550 } 551 if (!node.parentNode) { 552 // if there's no parent, there's nothing to do 553 return; 554 } 555 if (!isIE()) { 556 // nothing special required 557 node.parentNode.removeChild(node); 558 return; 559 } 560 // The rest of this code is specialcasing for IE 561 if (node.nodeName.toLowerCase() === "body") { 562 // special case for removing body under IE. 563 deleteChildren(node); 564 try { 565 node.outerHTML = ''; 566 } catch (ex) { 567 // fails under some circumstances, but not in RI 568 // supplied responses. If we've gotten here, it's 569 // fairly safe to leave a lingering body tag rather than 570 // fail outright 571 } 572 return; 573 } 574 var temp = node.ownerDocument.createElement('div'); 575 var parent = node.parentNode; 576 temp.appendChild(parent.removeChild(node)); 577 // Now clean up the temporary element 578 try { 579 temp.outerHTML = ''; //prevent leak in IE 580 } catch (ex) { 581 // at least we tried. Fails in some circumstances, 582 // but not in RI supplied responses. Better to leave a lingering 583 // temporary div than to fail outright. 584 } 585 }; 586 587 /** 588 * Deletes all children of a node 589 * @param node 590 * @ignore 591 */ 592 var deleteChildren = function deleteChildren(node) { 593 if (!node) { 594 return; 595 } 596 for (var x = node.childNodes.length - 1; x >= 0; x--) { //delete all of node's children 597 var childNode = node.childNodes[x]; 598 deleteNode(childNode); 599 } 600 }; 601 602 /** 603 * <p> Copies the childNodes of nodeFrom to nodeTo</p> 604 * 605 * @param nodeFrom the Node to copy the childNodes from 606 * @param nodeTo the Node to copy the childNodes to 607 * @ignore 608 * Note: This code originally from Sarissa: http://dev.abiss.gr/sarissa 609 * It has been modified to fit into the overall codebase 610 */ 611 var copyChildNodes = function copyChildNodes(nodeFrom, nodeTo) { 612 613 if ((!nodeFrom) || (!nodeTo)) { 614 throw "Both source and destination nodes must be provided"; 615 } 616 617 deleteChildren(nodeTo); 618 var nodes = nodeFrom.childNodes; 619 // if within the same doc, just move, else copy and delete 620 if (nodeFrom.ownerDocument == nodeTo.ownerDocument) { 621 while (nodeFrom.firstChild) { 622 nodeTo.appendChild(nodeFrom.firstChild); 623 } 624 } else { 625 var ownerDoc = nodeTo.nodeType == Node.DOCUMENT_NODE ? nodeTo : nodeTo.ownerDocument; 626 var i; 627 if (typeof(ownerDoc.importNode) != "undefined") { 628 for (i = 0; i < nodes.length; i++) { 629 nodeTo.appendChild(ownerDoc.importNode(nodes[i], true)); 630 } 631 } else { 632 for (i = 0; i < nodes.length; i++) { 633 nodeTo.appendChild(nodes[i].cloneNode(true)); 634 } 635 } 636 } 637 }; 638 639 640 /** 641 * Replace one node with another. Necessary for handling IE memory leak. 642 * @param node 643 * @param newNode 644 */ 645 var replaceNode = function replaceNode(newNode, node) { 646 if(isIE()){ 647 node.parentNode.insertBefore(newNode, node); 648 deleteNode(node); 649 } else { 650 node.parentNode.replaceChild(newNode, node); 651 } 652 }; 653 654 655 /** 656 * copy all attributes from one element to another - except id 657 * @param target element to copy attributes to 658 * @param source element to copy attributes from 659 * @ignore 660 */ 661 var cloneAttributes = function cloneAttributes(target, source) { 662 663 // enumerate core element attributes - without 'dir' as special case 664 var coreElementAttributes = ['className', 'title', 'lang', 'xml:lang']; 665 666 // Enumerate additional input element attributes 667 var inputElementAttributes = 668 [ 'name', 'value', 'checked', 'disabled', 'readOnly', 669 'size', 'maxLength', 'src', 'alt', 'useMap', 'isMap', 670 'tabIndex', 'accessKey', 'accept', 'type' 671 ]; 672 673 // Enumerate all the names of the event listeners 674 var listenerNames = 675 [ 'onclick', 'ondblclick', 'onmousedown', 'onmousemove', 'onmouseout', 676 'onmouseover', 'onmouseup', 'onkeydown', 'onkeypress', 'onkeyup', 677 'onhelp', 'onblur', 'onfocus', 'onchange', 'onload', 'onunload', 'onabort', 678 'onreset', 'onselect', 'onsubmit' 679 ]; 680 681 var iIndex, iLength; // for loop variables 682 var attributeName; // name of the attribute to set 683 var newValue, oldValue; // attribute values in each element 684 685 // First, copy over core attributes 686 for (iIndex = 0,iLength = coreElementAttributes.length; iIndex < iLength; iIndex++) { 687 attributeName = coreElementAttributes[iIndex]; 688 newValue = source[attributeName]; 689 oldValue = target[attributeName]; 690 if (oldValue != newValue) { 691 target[attributeName] = newValue; 692 } 693 } 694 695 // Next, if it's an input, copy those over 696 if (target.nodeName.toLowerCase() === 'input') { 697 for (iIndex = 0,iLength = inputElementAttributes.length; iIndex < iLength; iIndex++) { 698 attributeName = inputElementAttributes[iIndex]; 699 newValue = source[attributeName]; 700 oldValue = target[attributeName]; 701 if (oldValue != newValue) { 702 target[attributeName] = newValue; 703 } 704 } 705 } 706 //'style' attribute special case 707 var newStyle = source.getAttribute('style'); 708 var oldStyle = target.getAttribute('style'); 709 if (newStyle != oldStyle) { 710 if (isIE()) { 711 target.style.setAttribute('cssText', newStyle, 0); 712 } else { 713 target.setAttribute('style',newStyle); 714 } 715 } 716 for (var lIndex = 0, lLength = listenerNames.length; lIndex < lLength; lIndex++) { 717 var name = listenerNames[lIndex]; 718 target[name] = source[name] ? source[name] : null; 719 if (source[name]) { 720 source[name] = null; 721 } 722 } 723 // Special case for 'dir' attribute 724 if (!isIE() && source.dir != target.dir) { 725 target.dir = source.dir ? source.dir : null; 726 } 727 }; 728 729 /** 730 * Replace an element from one document into another 731 * @param newElement new element to put in document 732 * @param origElement original element to replace 733 * @ignore 734 */ 735 var elementReplace = function elementReplace(newElement, origElement) { 736 copyChildNodes(newElement, origElement); 737 // sadly, we have to reparse all over again 738 // to reregister the event handlers and styles 739 // PENDING do some performance tests on large pages 740 origElement.innerHTML = origElement.innerHTML; 741 742 try { 743 cloneAttributes(origElement, newElement); 744 } catch (ex) { 745 // if in dev mode, report an error, else try to limp onward 746 if (jsf.getProjectStage() == "Development") { 747 throw new Error("Error updating attributes"); 748 } 749 } 750 deleteNode(newElement); 751 752 }; 753 754 /** 755 * Create a new document, then select the body element within it 756 * @param docStr Stringified version of document to create 757 * @return element the body element 758 * @ignore 759 */ 760 var getBodyElement = function getBodyElement(docStr) { 761 762 var doc; // intermediate document we'll create 763 var body; // Body element to return 764 765 if (typeof DOMParser !== "undefined") { // FF, S, Chrome 766 doc = (new DOMParser()).parseFromString(docStr, "text/xml"); 767 } else if (typeof ActiveXObject !== "undefined") { // IE 768 doc = new ActiveXObject("MSXML2.DOMDocument"); 769 doc.loadXML(docStr); 770 } else { 771 throw new Error("You don't seem to be running a supported browser"); 772 } 773 774 if (getParseErrorText(doc) !== PARSED_OK) { 775 throw new Error(getParseErrorText(doc)); 776 } 777 778 body = doc.getElementsByTagName("body")[0]; 779 780 if (!body) { 781 throw new Error("Can't find body tag in returned document."); 782 } 783 784 return body; 785 }; 786 787 /** 788 * Do update. 789 * @param element element to update 790 * @param context context of request 791 * @ignore 792 */ 793 var doUpdate = function doUpdate(element, context) { 794 var id, content, markup, state; 795 var stateForm; 796 var scripts = []; // temp holding value for array of script nodes 797 798 id = element.getAttribute('id'); 799 if (id === "javax.faces.ViewState") { 800 801 state = element.firstChild; 802 803 // Now set the view state from the server into the DOM 804 // but only for the form that submitted the request. 805 806 stateForm = document.getElementById(context.formid); 807 if (!stateForm || !stateForm.elements) { 808 // if the form went away for some reason, or it lacks elements 809 // we're going to just return silently. 810 return; 811 } 812 var field = stateForm.elements["javax.faces.ViewState"]; 813 if (typeof field == 'undefined') { 814 field = document.createElement("input"); 815 field.type = "hidden"; 816 field.name = "javax.faces.ViewState"; 817 stateForm.appendChild(field); 818 } 819 field.value = state.nodeValue; 820 821 // Now set the view state from the server into the DOM 822 // for any form that is a render target. 823 824 if (typeof context.render !== 'undefined' && context.render !== null) { 825 var temp = context.render.split(' '); 826 for (var i = 0; i < temp.length; i++) { 827 if (temp.hasOwnProperty(i)) { 828 // See if the element is a form and 829 // the form is not the one that caused the submission.. 830 var f = document.forms[temp[i]]; 831 if (typeof f !== 'undefined' && f !== null && f.id !== context.formid) { 832 field = f.elements["javax.faces.ViewState"]; 833 if (typeof field === 'undefined') { 834 field = document.createElement("input"); 835 field.type = "hidden"; 836 field.name = "javax.faces.ViewState"; 837 f.appendChild(field); 838 } 839 field.value = state.nodeValue; 840 } 841 } 842 } 843 } 844 return; 845 } 846 847 // join the CDATA sections in the markup 848 markup = ''; 849 for (var j = 0; j < element.childNodes.length; j++) { 850 content = element.childNodes[j]; 851 markup += content.nodeValue; 852 } 853 854 var src = markup; 855 856 // If our special render all markup is present.. 857 if (id === "javax.faces.ViewRoot" || id === "javax.faces.ViewBody") { 858 var bodyStartEx = new RegExp("< *body[^>]*>", "gi"); 859 var bodyEndEx = new RegExp("< */ *body[^>]*>", "gi"); 860 var newsrc; 861 862 var docBody = document.getElementsByTagName("body")[0]; 863 var bodyStart = bodyStartEx.exec(src); 864 865 if (bodyStart !== null) { // replace body tag 866 // First, try with XML manipulation 867 try { 868 // Get scripts from text 869 scripts = stripScripts(src); 870 // Remove scripts from text 871 newsrc = src.replace(/<script[^>]*>([\S\s]*?)<\/script>/igm, ""); 872 elementReplace(getBodyElement(newsrc), docBody); 873 runScripts(scripts); 874 } catch (e) { 875 // OK, replacing the body didn't work with XML - fall back to quirks mode insert 876 var srcBody, bodyEnd; 877 // if src contains </body> 878 bodyEnd = bodyEndEx.exec(src); 879 if (bodyEnd !== null) { 880 srcBody = src.substring(bodyStartEx.lastIndex, 881 bodyEnd.index); 882 } else { // can't find the </body> tag, punt 883 srcBody = src.substring(bodyStartEx.lastIndex); 884 } 885 // replace body contents with innerHTML - note, script handling happens within function 886 elementReplaceStr(docBody, "body", srcBody); 887 888 } 889 890 } else { // replace body contents with innerHTML - note, script handling happens within function 891 elementReplaceStr(docBody, "body", src); 892 } 893 } else if (id === "javax.faces.ViewHead") { 894 throw new Error("javax.faces.ViewHead not supported - browsers cannot reliably replace the head's contents"); 895 } else { 896 var d = $(id); 897 if (!d) { 898 throw new Error("During update: " + id + " not found"); 899 } 900 var parent = d.parentNode; 901 // Trim space padding before assigning to innerHTML 902 var html = src.replace(/^\s+/g, '').replace(/\s+$/g, ''); 903 var parserElement = document.createElement('div'); 904 var tag = d.nodeName.toLowerCase(); 905 var tableElements = ['td', 'th', 'tr', 'tbody', 'thead', 'tfoot']; 906 var isInTable = false; 907 for (var tei = 0, tel = tableElements.length; tei < tel; tei++) { 908 if (tableElements[tei] == tag) { 909 isInTable = true; 910 break; 911 } 912 } 913 if (isInTable) { 914 915 if (isAutoExec()) { 916 // Create html 917 parserElement.innerHTML = '<table>' + html + '</table>'; 918 } else { 919 // Get the scripts from the text 920 scripts = stripScripts(html); 921 // Remove scripts from text 922 html = html.replace(/<script[^>]*>([\S\s]*?)<\/script>/igm,""); 923 parserElement.innerHTML = '<table>' + html + '</table>'; 924 } 925 var newElement = parserElement.firstChild; 926 //some browsers will also create intermediary elements such as table>tbody>tr>td 927 while ((null !== newElement) && (id !== newElement.id)) { 928 newElement = newElement.firstChild; 929 } 930 parent.replaceChild(newElement, d); 931 runScripts(scripts); 932 } else if (d.nodeName.toLowerCase() === 'input') { 933 // special case handling for 'input' elements 934 // in order to not lose focus when updating, 935 // input elements need to be added in place. 936 parserElement = document.createElement('div'); 937 parserElement.innerHTML = html; 938 newElement = parserElement.firstChild; 939 940 cloneAttributes(d, newElement); 941 deleteNode(parserElement); 942 } else if (html.length > 0) { 943 if (isAutoExec()) { 944 // Create html 945 parserElement.innerHTML = html; 946 } else { 947 // Get the scripts from the text 948 scripts = stripScripts(html); 949 // Remove scripts from text 950 html = html.replace(/<script[^>]*>([\S\s]*?)<\/script>/igm,""); 951 parserElement.innerHTML = html; 952 } 953 replaceNode(parserElement.firstChild, d); 954 deleteNode(parserElement); 955 runScripts(scripts); 956 } 957 } 958 }; 959 960 /** 961 * Delete a node specified by the element. 962 * @param element 963 * @ignore 964 */ 965 var doDelete = function doDelete(element) { 966 var id = element.getAttribute('id'); 967 var target = $(id); 968 deleteNode(target); 969 }; 970 971 /** 972 * Insert a node specified by the element. 973 * @param element 974 * @ignore 975 */ 976 var doInsert = function doInsert(element) { 977 var tablePattern = new RegExp("<\\s*(td|th|tr|tbody|thead|tfoot)", "i"); 978 var scripts = []; 979 var target = $(element.firstChild.getAttribute('id')); 980 var parent = target.parentNode; 981 var html = element.firstChild.firstChild.nodeValue; 982 var isInTable = tablePattern.test(html); 983 984 if (!isAutoExec()) { 985 // Get the scripts from the text 986 scripts = stripScripts(html); 987 // Remove scripts from text 988 html = html.replace(/<script[^>]*>([\S\s]*?)<\/script>/igm,""); 989 } 990 var tempElement = document.createElement('div'); 991 var newElement = null; 992 if (isInTable) { 993 tempElement.innerHTML = '<table>' + html + '</table>'; 994 newElement = tempElement.firstChild; 995 //some browsers will also create intermediary elements such as table>tbody>tr>td 996 //test for presence of id on the new element since we do not have it directly 997 while ((null !== newElement) && ("" == newElement.id)) { 998 newElement = newElement.firstChild; 999 } 1000 } else { 1001 tempElement.innerHTML = html; 1002 newElement = tempElement.firstChild; 1003 } 1004 1005 if (element.firstChild.nodeName === 'after') { 1006 // Get the next in the list, to insert before 1007 target = target.nextSibling; 1008 } // otherwise, this is a 'before' element 1009 if (!!tempElement.innerHTML) { // check if only scripts were inserted - if so, do nothing here 1010 parent.insertBefore(newElement, target); 1011 } 1012 runScripts(scripts); 1013 deleteNode(tempElement); 1014 }; 1015 1016 /** 1017 * Modify attributes of given element id. 1018 * @param element 1019 * @ignore 1020 */ 1021 var doAttributes = function doAttributes(element) { 1022 1023 // Get id of element we'll act against 1024 var id = element.getAttribute('id'); 1025 1026 var target = $(id); 1027 1028 if (!target) { 1029 throw new Error("The specified id: " + id + " was not found in the page."); 1030 } 1031 1032 // There can be multiple attributes modified. Loop through the list. 1033 var nodes = element.childNodes; 1034 for (var i = 0; i < nodes.length; i++) { 1035 var name = nodes[i].getAttribute('name'); 1036 var value = nodes[i].getAttribute('value'); 1037 if (!isIE()) { 1038 target.setAttribute(name, value); 1039 } else { // if it's IE, then quite a bit more work is required 1040 if (name === 'class') { 1041 name = 'className'; 1042 target.setAttribute(name, value, 0); 1043 } else if (name === "for") { 1044 name = 'htmlFor'; 1045 target.setAttribute(name, value, 0); 1046 } else if (name === 'style') { 1047 target.style.setAttribute('cssText', value, 0); 1048 } else if (name.substring(0, 2) === 'on') { 1049 var fn = function(value) { 1050 return function() { 1051 window.execScript(value); 1052 }; 1053 }(value); 1054 target.setAttribute(name, fn, 0); 1055 } else if (name === 'dir') { 1056 if (jsf.getProjectStage() == 'Development') { 1057 throw new Error("Cannot set 'dir' attribute in IE"); 1058 } 1059 } else { 1060 target.setAttribute(name, value, 0); 1061 } 1062 } 1063 } 1064 }; 1065 1066 /** 1067 * Eval the CDATA of the element. 1068 * @param element to eval 1069 * @ignore 1070 */ 1071 var doEval = function doEval(element) { 1072 var evalText = element.firstChild.nodeValue; 1073 globalEval(evalText); 1074 }; 1075 1076 /** 1077 * Ajax Request Queue 1078 * @ignore 1079 */ 1080 var Queue = new function Queue() { 1081 1082 // Create the internal queue 1083 var queue = []; 1084 1085 1086 // the amount of space at the front of the queue, initialised to zero 1087 var queueSpace = 0; 1088 1089 /** Returns the size of this Queue. The size of a Queue is equal to the number 1090 * of elements that have been enqueued minus the number of elements that have 1091 * been dequeued. 1092 * @ignore 1093 */ 1094 this.getSize = function getSize() { 1095 return queue.length - queueSpace; 1096 }; 1097 1098 /** Returns true if this Queue is empty, and false otherwise. A Queue is empty 1099 * if the number of elements that have been enqueued equals the number of 1100 * elements that have been dequeued. 1101 * @ignore 1102 */ 1103 this.isEmpty = function isEmpty() { 1104 return (queue.length === 0); 1105 }; 1106 1107 /** Enqueues the specified element in this Queue. 1108 * 1109 * @param element - the element to enqueue 1110 * @ignore 1111 */ 1112 this.enqueue = function enqueue(element) { 1113 // Queue the request 1114 queue.push(element); 1115 }; 1116 1117 1118 /** Dequeues an element from this Queue. The oldest element in this Queue is 1119 * removed and returned. If this Queue is empty then undefined is returned. 1120 * 1121 * @returns Object The element that was removed from the queue. 1122 * @ignore 1123 */ 1124 this.dequeue = function dequeue() { 1125 // initialise the element to return to be undefined 1126 var element = undefined; 1127 1128 // check whether the queue is empty 1129 if (queue.length) { 1130 // fetch the oldest element in the queue 1131 element = queue[queueSpace]; 1132 1133 // update the amount of space and check whether a shift should occur 1134 if (++queueSpace * 2 >= queue.length) { 1135 // set the queue equal to the non-empty portion of the queue 1136 queue = queue.slice(queueSpace); 1137 // reset the amount of space at the front of the queue 1138 queueSpace = 0; 1139 } 1140 } 1141 // return the removed element 1142 try { 1143 return element; 1144 } finally { 1145 element = null; // IE 6 leak prevention 1146 } 1147 }; 1148 1149 /** Returns the oldest element in this Queue. If this Queue is empty then 1150 * undefined is returned. This function returns the same value as the dequeue 1151 * function, but does not remove the returned element from this Queue. 1152 * @ignore 1153 */ 1154 this.getOldestElement = function getOldestElement() { 1155 // initialise the element to return to be undefined 1156 var element = undefined; 1157 1158 // if the queue is not element then fetch the oldest element in the queue 1159 if (queue.length) { 1160 element = queue[queueSpace]; 1161 } 1162 // return the oldest element 1163 try { 1164 return element; 1165 } finally { 1166 element = null; //IE 6 leak prevention 1167 } 1168 }; 1169 }(); 1170 1171 1172 /** 1173 * AjaxEngine handles Ajax implementation details. 1174 * @ignore 1175 */ 1176 var AjaxEngine = function AjaxEngine() { 1177 1178 var req = {}; // Request Object 1179 req.url = null; // Request URL 1180 req.context = {}; // Context of request and response 1181 req.context.sourceid = null; // Source of this request 1182 req.context.onerror = null; // Error handler for request 1183 req.context.onevent = null; // Event handler for request 1184 req.context.formid = null; // Form that's the context for this request 1185 req.xmlReq = null; // XMLHttpRequest Object 1186 req.async = true; // Default - Asynchronous 1187 req.parameters = {}; // Parameters For GET or POST 1188 req.queryString = null; // Encoded Data For GET or POST 1189 req.method = null; // GET or POST 1190 req.status = null; // Response Status Code From Server 1191 req.fromQueue = false; // Indicates if the request was taken off the queue 1192 // before being sent. This prevents the request from 1193 // entering the queue redundantly. 1194 1195 req.que = Queue; 1196 1197 // Get an XMLHttpRequest Handle 1198 req.xmlReq = getTransport(); 1199 if (req.xmlReq === null) { 1200 return null; 1201 } 1202 1203 function noop() {} 1204 1205 // Set up request/response state callbacks 1206 /** 1207 * @ignore 1208 */ 1209 req.xmlReq.onreadystatechange = function() { 1210 if (req.xmlReq.readyState === 4) { 1211 req.onComplete(); 1212 // next two lines prevent closure/ciruclar reference leaks 1213 // of XHR instances in IE 1214 req.xmlReq.onreadystatechange = noop; 1215 req.xmlReq = null; 1216 } 1217 }; 1218 1219 /** 1220 * This function is called when the request/response interaction 1221 * is complete. If the return status code is successfull, 1222 * dequeue all requests from the queue that have completed. If a 1223 * request has been found on the queue that has not been sent, 1224 * send the request. 1225 * @ignore 1226 */ 1227 req.onComplete = function onComplete() { 1228 if (req.xmlReq.status && (req.xmlReq.status >= 200 && req.xmlReq.status < 300)) { 1229 sendEvent(req.xmlReq, req.context, "complete"); 1230 jsf.ajax.response(req.xmlReq, req.context); 1231 } else { 1232 sendEvent(req.xmlReq, req.context, "complete"); 1233 sendError(req.xmlReq, req.context, "httpError"); 1234 } 1235 1236 // Regardless of whether the request completed successfully (or not), 1237 // dequeue requests that have been completed (readyState 4) and send 1238 // requests that ready to be sent (readyState 0). 1239 1240 var nextReq = req.que.getOldestElement(); 1241 if (nextReq === null || typeof nextReq === 'undefined') { 1242 return; 1243 } 1244 while ((typeof nextReq.xmlReq !== 'undefined' && nextReq.xmlReq !== null) && 1245 nextReq.xmlReq.readyState === 4) { 1246 req.que.dequeue(); 1247 nextReq = req.que.getOldestElement(); 1248 if (nextReq === null || typeof nextReq === 'undefined') { 1249 break; 1250 } 1251 } 1252 if (nextReq === null || typeof nextReq === 'undefined') { 1253 return; 1254 } 1255 if ((typeof nextReq.xmlReq !== 'undefined' && nextReq.xmlReq !== null) && 1256 nextReq.xmlReq.readyState === 0) { 1257 nextReq.fromQueue = true; 1258 nextReq.sendRequest(); 1259 } 1260 }; 1261 1262 /** 1263 * Utility method that accepts additional arguments for the AjaxEngine. 1264 * If an argument is passed in that matches an AjaxEngine property, the 1265 * argument value becomes the value of the AjaxEngine property. 1266 * Arguments that don't match AjaxEngine properties are added as 1267 * request parameters. 1268 * @ignore 1269 */ 1270 req.setupArguments = function(args) { 1271 for (var i in args) { 1272 if (args.hasOwnProperty(i)) { 1273 if (typeof req[i] === 'undefined') { 1274 req.parameters[i] = args[i]; 1275 } else { 1276 req[i] = args[i]; 1277 } 1278 } 1279 } 1280 }; 1281 1282 /** 1283 * This function does final encoding of parameters, determines the request method 1284 * (GET or POST) and sends the request using the specified url. 1285 * @ignore 1286 */ 1287 req.sendRequest = function() { 1288 if (req.xmlReq !== null) { 1289 // if there is already a request on the queue waiting to be processed.. 1290 // just queue this request 1291 if (!req.que.isEmpty()) { 1292 if (!req.fromQueue) { 1293 req.que.enqueue(req); 1294 return; 1295 } 1296 } 1297 // If the queue is empty, queue up this request and send 1298 if (!req.fromQueue) { 1299 req.que.enqueue(req); 1300 } 1301 // Some logic to get the real request URL 1302 if (req.generateUniqueUrl && req.method == "GET") { 1303 req.parameters["AjaxRequestUniqueId"] = new Date().getTime() + "" + req.requestIndex; 1304 } 1305 var content = null; // For POST requests, to hold query string 1306 for (var i in req.parameters) { 1307 if (req.parameters.hasOwnProperty(i)) { 1308 if (req.queryString.length > 0) { 1309 req.queryString += "&"; 1310 } 1311 req.queryString += encodeURIComponent(i) + "=" + encodeURIComponent(req.parameters[i]); 1312 } 1313 } 1314 if (req.method === "GET") { 1315 if (req.queryString.length > 0) { 1316 req.url += ((req.url.indexOf("?") > -1) ? "&" : "?") + req.queryString; 1317 } 1318 } 1319 req.xmlReq.open(req.method, req.url, req.async); 1320 // note that we are including the charset=UTF-8 as part of the content type (even 1321 // if encodeURIComponent encodes as UTF-8), because with some 1322 // browsers it will not be set in the request. Some server implementations need to 1323 // determine the character encoding from the request header content type. 1324 if (req.method === "POST") { 1325 if (typeof req.xmlReq.setRequestHeader !== 'undefined') { 1326 req.xmlReq.setRequestHeader('Faces-Request', 'partial/ajax'); 1327 req.xmlReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded;charset=UTF-8'); 1328 } 1329 content = req.queryString; 1330 } 1331 // note that async == false is not a supported feature. We may change it in ways 1332 // that break existing programs at any time, with no warning. 1333 if(!req.async) { 1334 req.xmlReq.onreadystatechange = null; // no need for readystate change listening 1335 } 1336 sendEvent(req.xmlReq, req.context, "begin"); 1337 req.xmlReq.send(content); 1338 if(!req.async){ 1339 req.onComplete(); 1340 } 1341 } 1342 }; 1343 1344 return req; 1345 }; 1346 1347 /** 1348 * Error handling callback. 1349 * Assumes that the request has completed. 1350 * @ignore 1351 */ 1352 var sendError = function sendError(request, context, status, description, serverErrorName, serverErrorMessage) { 1353 1354 // Possible errornames: 1355 // httpError 1356 // emptyResponse 1357 // serverError 1358 // malformedXML 1359 1360 var sent = false; 1361 var data = {}; // data payload for function 1362 data.type = "error"; 1363 data.status = status; 1364 data.source = context.sourceid; 1365 data.responseCode = request.status; 1366 data.responseXML = request.responseXML; 1367 data.responseText = request.responseText; 1368 1369 // ensure data source is the dom element and not the ID 1370 // per 14.4.1 of the 2.0 specification. 1371 if (typeof data.source === 'string') { 1372 data.source = document.getElementById(data.source); 1373 } 1374 1375 if (description) { 1376 data.description = description; 1377 } else if (status == "httpError") { 1378 if (data.responseCode === 0) { 1379 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."; 1380 } else { 1381 data.description = "There was an error communicating with the server, status: " + data.responseCode; 1382 } 1383 } else if (status == "serverError") { 1384 data.description = serverErrorMessage; 1385 } else if (status == "emptyResponse") { 1386 data.description = "An empty response was received from the server. Check server error logs."; 1387 } else if (status == "malformedXML") { 1388 if (getParseErrorText(data.responseXML) !== PARSED_OK) { 1389 data.description = getParseErrorText(data.responseXML); 1390 } else { 1391 data.description = "An invalid XML response was received from the server."; 1392 } 1393 } 1394 1395 if (status == "serverError") { 1396 data.errorName = serverErrorName; 1397 data.errorMessage = serverErrorMessage; 1398 } 1399 1400 // If we have a registered callback, send the error to it. 1401 if (context.onerror) { 1402 context.onerror.call(null, data); 1403 sent = true; 1404 } 1405 1406 for (var i in errorListeners) { 1407 if (errorListeners.hasOwnProperty(i)) { 1408 errorListeners[i].call(null, data); 1409 sent = true; 1410 } 1411 } 1412 1413 if (!sent && jsf.getProjectStage() === "Development") { 1414 if (status == "serverError") { 1415 alert("serverError: " + serverErrorName + " " + serverErrorMessage); 1416 } else { 1417 alert(status + ": " + data.description); 1418 } 1419 } 1420 }; 1421 1422 /** 1423 * Event handling callback. 1424 * Request is assumed to have completed, except in the case of event = 'begin'. 1425 * @ignore 1426 */ 1427 var sendEvent = function sendEvent(request, context, status) { 1428 1429 var data = {}; 1430 data.type = "event"; 1431 data.status = status; 1432 data.source = context.sourceid; 1433 // ensure data source is the dom element and not the ID 1434 // per 14.4.1 of the 2.0 specification. 1435 if (typeof data.source === 'string') { 1436 data.source = document.getElementById(data.source); 1437 } 1438 if (status !== 'begin') { 1439 data.responseCode = request.status; 1440 data.responseXML = request.responseXML; 1441 data.responseText = request.responseText; 1442 } 1443 1444 if (context.onevent) { 1445 context.onevent.call(null, data); 1446 } 1447 1448 for (var i in eventListeners) { 1449 if (eventListeners.hasOwnProperty(i)) { 1450 eventListeners[i].call(null, data); 1451 } 1452 } 1453 }; 1454 1455 // Use module pattern to return the functions we actually expose 1456 return { 1457 /** 1458 * Register a callback for error handling. 1459 * <p><b>Usage:</b></p> 1460 * <pre><code> 1461 * jsf.ajax.addOnError(handleError); 1462 * ... 1463 * var handleError = function handleError(data) { 1464 * ... 1465 * } 1466 * </pre></code> 1467 * <p><b>Implementation Requirements:</b></p> 1468 * This function must accept a reference to an existing JavaScript function. 1469 * The JavaScript function reference must be added to a list of callbacks, making it possible 1470 * to register more than one callback by invoking <code>jsf.ajax.addOnError</code> 1471 * more than once. This function must throw an error if the <code>callback</code> 1472 * argument is not a function. 1473 * 1474 * @member jsf.ajax 1475 * @param callback a reference to a function to call on an error 1476 */ 1477 addOnError: function addOnError(callback) { 1478 if (typeof callback === 'function') { 1479 errorListeners[errorListeners.length] = callback; 1480 } else { 1481 throw new Error("jsf.ajax.addOnError: Added a callback that was not a function."); 1482 } 1483 }, 1484 /** 1485 * Register a callback for event handling. 1486 * <p><b>Usage:</b></p> 1487 * <pre><code> 1488 * jsf.ajax.addOnEvent(statusUpdate); 1489 * ... 1490 * var statusUpdate = function statusUpdate(data) { 1491 * ... 1492 * } 1493 * </pre></code> 1494 * <p><b>Implementation Requirements:</b></p> 1495 * This function must accept a reference to an existing JavaScript function. 1496 * The JavaScript function reference must be added to a list of callbacks, making it possible 1497 * to register more than one callback by invoking <code>jsf.ajax.addOnEvent</code> 1498 * more than once. This function must throw an error if the <code>callback</code> 1499 * argument is not a function. 1500 * 1501 * @member jsf.ajax 1502 * @param callback a reference to a function to call on an event 1503 */ 1504 addOnEvent: function addOnEvent(callback) { 1505 if (typeof callback === 'function') { 1506 eventListeners[eventListeners.length] = callback; 1507 } else { 1508 throw new Error("jsf.ajax.addOnEvent: Added a callback that was not a function"); 1509 } 1510 }, 1511 /** 1512 * <p>Send an asynchronous Ajax request to the server. 1513 * <p><b>Usage:</b></p> 1514 * <pre><code> 1515 * Example showing all optional arguments: 1516 * 1517 * <commandButton id="button1" value="submit" 1518 * onclick="jsf.ajax.request(this,event, 1519 * {execute:'button1',render:'status',onevent: handleEvent,onerror: handleError});return false;"/> 1520 * </commandButton/> 1521 * </pre></code> 1522 * <p><b>Implementation Requirements:</b></p> 1523 * This function must: 1524 * <ul> 1525 * <li>Be used within the context of a <code>form</code>.</li> 1526 * <li>Capture the element that triggered this Ajax request 1527 * (from the <code>source</code> argument, also known as the 1528 * <code>source</code> element.</li> 1529 * <li>If the <code>source</code> element is <code>null</code> or 1530 * <code>undefined</code> throw an error.</li> 1531 * <li>If the <code>source</code> argument is not a <code>string</code> or 1532 * DOM element object, throw an error.</li> 1533 * <li>If the <code>source</code> argument is a <code>string</code>, find the 1534 * DOM element for that <code>string</code> identifier. 1535 * <li>If the DOM element could not be determined, throw an error.</li> 1536 * <li>If the <code>onerror</code> and <code>onevent</code> arguments are set, 1537 * they must be functions, or throw an error. 1538 * <li>Determine the <code>source</code> element's <code>form</code> 1539 * element.</li> 1540 * <li>Get the <code>form</code> view state by calling 1541 * {@link jsf.getViewState} passing the 1542 * <code>form</code> element as the argument.</li> 1543 * <li>Collect post data arguments for the Ajax request. 1544 * <ul> 1545 * <li>The following name/value pairs are required post data arguments: 1546 * <table border="1"> 1547 * <tr> 1548 * <th>name</th> 1549 * <th>value</th> 1550 * </tr> 1551 * <tr> 1552 * <td><code>javax.faces.ViewState</code></td> 1553 * <td><code>Contents of javax.faces.ViewState hidden field. This is included when 1554 * {@link jsf.getViewState} is used.</code></td> 1555 * </tr> 1556 * <tr> 1557 * <td><code>javax.faces.partial.ajax</code></td> 1558 * <td><code>true</code></td> 1559 * </tr> 1560 * <tr> 1561 * <td><code>javax.faces.source</code></td> 1562 * <td><code>The identifier of the element that triggered this request.</code></td> 1563 * </tr> 1564 * </table> 1565 * </li> 1566 * </ul> 1567 * </li> 1568 * <li>Collect optional post data arguments for the Ajax request. 1569 * <ul> 1570 * <li>Determine additional arguments (if any) from the <code>options</code> 1571 * argument. If <code>options.execute</code> exists: 1572 * <ul> 1573 * <li>If the keyword <code>@none</code> is present, do not create and send 1574 * the post data argument <code>javax.faces.partial.execute</code>.</li> 1575 * <li>If the keyword <code>@all</code> is present, create the post data argument with 1576 * the name <code>javax.faces.partial.execute</code> and the value <code>@all</code>.</li> 1577 * <li>Otherwise, there are specific identifiers that need to be sent. Create the post 1578 * data argument with the name <code>javax.faces.partial.execute</code> and the value as a 1579 * space delimited <code>string</code> of client identifiers.</li> 1580 * </ul> 1581 * </li> 1582 * <li>If <code>options.execute</code> does not exist, create the post data argument with the 1583 * name <code>javax.faces.partial.execute</code> and the value as the identifier of the 1584 * element that caused this request.</li> 1585 * <li>If <code>options.render</code> exists: 1586 * <ul> 1587 * <li>If the keyword <code>@none</code> is present, do not create and send 1588 * the post data argument <code>javax.faces.partial.render</code>.</li> 1589 * <li>If the keyword <code>@all</code> is present, create the post data argument with 1590 * the name <code>javax.faces.partial.render</code> and the value <code>@all</code>.</li> 1591 * <li>Otherwise, there are specific identifiers that need to be sent. Create the post 1592 * data argument with the name <code>javax.faces.partial.render</code> and the value as a 1593 * space delimited <code>string</code> of client identifiers.</li> 1594 * </ul> 1595 * <li>If <code>options.render</code> does not exist do not create and send the 1596 * post data argument <code>javax.faces.partial.render</code>.</li> 1597 * <li>Determine additional arguments (if any) from the <code>event</code> 1598 * argument. The following name/value pairs may be used from the 1599 * <code>event</code> object: 1600 * <ul> 1601 * <li><code>target</code> - the ID of the element that triggered the event.</li> 1602 * <li><code>captured</code> - the ID of the element that captured the event.</li> 1603 * <li><code>type</code> - the type of event (ex: onkeypress)</li> 1604 * <li><code>alt</code> - <code>true</code> if ALT key was pressed.</li> 1605 * <li><code>ctrl</code> - <code>true</code> if CTRL key was pressed.</li> 1606 * <li><code>shift</code> - <code>true</code> if SHIFT key was pressed. </li> 1607 * <li><code>meta</code> - <code>true</code> if META key was pressed. </li> 1608 * <li><code>right</code> - <code>true</code> if right mouse button 1609 * was pressed. </li> 1610 * <li><code>left</code> - <code>true</code> if left mouse button 1611 * was pressed. </li> 1612 * <li><code>keycode</code> - the key code. 1613 * </ul> 1614 * </li> 1615 * </ul> 1616 * </li> 1617 * <li>Encode the set of post data arguments.</li> 1618 * <li>Join the encoded view state with the encoded set of post data arguments 1619 * to form the <code>query string</code> that will be sent to the server.</li> 1620 * <li>Create a request <code>context</code> object and set the properties: 1621 * <ul><li><code>source</code> (the source DOM element for this request)</li> 1622 * <li><code>onerror</code> (the error handler for this request)</li> 1623 * <li><code>onevent</code> (the event handler for this request)</li></ul> 1624 * The request context will be used during error/event handling.</li> 1625 * <li>Send a <code>begin</code> event following the procedure as outlined 1626 * in the Chapter 13 "Sending Events" section of the spec prose document <a 1627 * href="../../javadocs/overview-summary.html#prose_document">linked in the 1628 * overview summary</a></li> 1629 * <li>Set the request header with the name: <code>Faces-Request</code> and the 1630 * value: <code>partial/ajax</code>.</li> 1631 * <li>Determine the <code>posting URL</code> as follows: If the hidden field 1632 * <code>javax.faces.encodedURL</code> is present in the submitting form, use its 1633 * value as the <code>posting URL</code>. Otherwise, use the <code>action</code> 1634 * property of the <code>form</code> element as the <code>URL</code>.</li> 1635 * <li>Send the request as an <code>asynchronous POST</code> using the 1636 * <code>posting URL</code> that was determined in the previous step.</li> 1637 * </ul> 1638 * Before the request is sent it must be put into a queue to ensure requests 1639 * are sent in the same order as when they were initiated. The request callback function 1640 * must examine the queue and determine the next request to be sent. The behavior of the 1641 * request callback function must be as follows: 1642 * <ul> 1643 * <li>If the request completed successfully invoke {@link jsf.ajax.response} 1644 * passing the <code>request</code> object.</li> 1645 * <li>If the request did not complete successfully, notify the client.</li> 1646 * <li>Regardless of the outcome of the request (success or error) every request in the 1647 * queue must be handled. Examine the status of each request in the queue starting from 1648 * the request that has been in the queue the longest. If the status of the request is 1649 * <code>complete</code> (readyState 4), dequeue the request (remove it from the queue). 1650 * If the request has not been sent (readyState 0), send the request. Requests that are 1651 * taken off the queue and sent should not be put back on the queue.</li> 1652 * </ul> 1653 * 1654 * </p> 1655 * 1656 * @param source The DOM element that triggered this Ajax request, or an id string of the 1657 * element to use as the triggering element. 1658 * @param event The DOM event that triggered this Ajax request. The 1659 * <code>event</code> argument is optional. 1660 * @param options The set of available options that can be sent as 1661 * request parameters to control client and/or server side 1662 * request processing. Acceptable name/value pair options are: 1663 * <table border="1"> 1664 * <tr> 1665 * <th>name</th> 1666 * <th>value</th> 1667 * </tr> 1668 * <tr> 1669 * <td><code>execute</code></td> 1670 * <td><code>space seperated list of client identifiers</code></td> 1671 * </tr> 1672 * <tr> 1673 * <td><code>render</code></td> 1674 * <td><code>space seperated list of client identifiers</code></td> 1675 * </tr> 1676 * <tr> 1677 * <td><code>onevent</code></td> 1678 * <td><code>function to callback for event</code></td> 1679 * </tr> 1680 * <tr> 1681 * <td><code>onerror</code></td> 1682 * <td><code>function to callback for error</code></td> 1683 * </tr> 1684 * <tr> 1685 * <td><code>params</code></td> 1686 * <td><code>object containing parameters to include in the request</code></td> 1687 * </tr> 1688 * </table> 1689 * The <code>options</code> argument is optional. 1690 * @member jsf.ajax 1691 * @function jsf.ajax.request 1692 * @throws Error if first required argument <code>element</code> is not specified 1693 */ 1694 request: function request(source, event, options) { 1695 1696 var element, form; // Element variables 1697 var all, none; 1698 1699 if (typeof source === 'undefined' || source === null) { 1700 throw new Error("jsf.ajax.request: source not set"); 1701 } 1702 1703 // set up the element based on source 1704 if (typeof source === 'string') { 1705 element = document.getElementById(source); 1706 } else if (typeof source === 'object') { 1707 element = source; 1708 } else { 1709 throw new Error("jsf.request: source must be object or string"); 1710 } 1711 // attempt to handle case of name unset 1712 // this might be true in a badly written composite component 1713 if (!element.name) { 1714 element.name = element.id; 1715 } 1716 1717 if (typeof(options) === 'undefined' || options === null) { 1718 options = {}; 1719 } 1720 1721 // Error handler for this request 1722 var onerror = false; 1723 1724 if (options.onerror && typeof options.onerror === 'function') { 1725 onerror = options.onerror; 1726 } else if (options.onerror && typeof options.onerror !== 'function') { 1727 throw new Error("jsf.ajax.request: Added an onerror callback that was not a function"); 1728 } 1729 1730 // Event handler for this request 1731 var onevent = false; 1732 1733 if (options.onevent && typeof options.onevent === 'function') { 1734 onevent = options.onevent; 1735 } else if (options.onevent && typeof options.onevent !== 'function') { 1736 throw new Error("jsf.ajax.request: Added an onevent callback that was not a function"); 1737 } 1738 1739 form = getForm(element); 1740 if (!form) { 1741 throw new Error("jsf.ajax.request: Method must be called within a form"); 1742 } 1743 var viewState = jsf.getViewState(form); 1744 1745 // Set up additional arguments to be used in the request.. 1746 // Make sure "javax.faces.source" is set up. 1747 // If there were "execute" ids specified, make sure we 1748 // include the identifier of the source element in the 1749 // "execute" list. If there were no "execute" ids 1750 // specified, determine the default. 1751 1752 var args = {}; 1753 1754 args["javax.faces.source"] = element.id; 1755 1756 if (event && !!event.type) { 1757 args["javax.faces.partial.event"] = event.type; 1758 } 1759 1760 // If we have 'execute' identifiers: 1761 // Handle any keywords that may be present. 1762 // If @none present anywhere, do not send the 1763 // "javax.faces.partial.execute" parameter. 1764 // The 'execute' and 'render' lists must be space 1765 // delimited. 1766 1767 if (options.execute) { 1768 none = options.execute.search(/@none/); 1769 if (none < 0) { 1770 all = options.execute.search(/@all/); 1771 if (all < 0) { 1772 options.execute = options.execute.replace("@this", element.id); 1773 options.execute = options.execute.replace("@form", form.id); 1774 var temp = options.execute.split(' '); 1775 if (!isInArray(temp, element.name)) { 1776 options.execute = element.name + " " + options.execute; 1777 } 1778 } else { 1779 options.execute = "@all"; 1780 } 1781 args["javax.faces.partial.execute"] = options.execute; 1782 } 1783 } else { 1784 options.execute = element.name + " " + element.id; 1785 args["javax.faces.partial.execute"] = options.execute; 1786 } 1787 1788 if (options.render) { 1789 none = options.render.search(/@none/); 1790 if (none < 0) { 1791 all = options.render.search(/@all/); 1792 if (all < 0) { 1793 options.render = options.render.replace("@this", element.id); 1794 options.render = options.render.replace("@form", form.id); 1795 } else { 1796 options.render = "@all"; 1797 } 1798 args["javax.faces.partial.render"] = options.render; 1799 } 1800 } 1801 1802 // remove non-passthrough options 1803 delete options.execute; 1804 delete options.render; 1805 delete options.onerror; 1806 delete options.onevent; 1807 // copy all other options to args 1808 for (var property in options) { 1809 if (options.hasOwnProperty(property)) { 1810 args[property] = options[property]; 1811 } 1812 } 1813 1814 args["javax.faces.partial.ajax"] = "true"; 1815 args["method"] = "POST"; 1816 1817 // Determine the posting url 1818 1819 var encodedUrlField = form.elements["javax.faces.encodedURL"]; 1820 if (typeof encodedUrlField == 'undefined') { 1821 args["url"] = form.action; 1822 } else { 1823 args["url"] = encodedUrlField.value; 1824 } 1825 1826 var ajaxEngine = new AjaxEngine(); 1827 ajaxEngine.setupArguments(args); 1828 ajaxEngine.queryString = viewState; 1829 ajaxEngine.context.onevent = onevent; 1830 ajaxEngine.context.onerror = onerror; 1831 ajaxEngine.context.sourceid = element.id; 1832 ajaxEngine.context.formid = form.id; 1833 ajaxEngine.context.render = args["javax.faces.partial.render"]; 1834 ajaxEngine.sendRequest(); 1835 1836 // null out element variables to protect against IE memory leak 1837 element = null; 1838 form = null; 1839 1840 }, 1841 /** 1842 * <p>Receive an Ajax response from the server. 1843 * <p><b>Usage:</b></p> 1844 * <pre><code> 1845 * jsf.ajax.response(request, context); 1846 * </pre></code> 1847 * <p><b>Implementation Requirements:</b></p> 1848 * This function must evaluate the markup returned in the 1849 * <code>request.responseXML</code> object and perform the following action: 1850 * <ul> 1851 * <p>If there is no XML response returned, signal an <code>emptyResponse</code> 1852 * error. If the XML response does not follow the format as outlined 1853 * in Appendix A of the spec prose document <a 1854 * href="../../javadocs/overview-summary.html#prose_document">linked in the 1855 * overview summary</a> signal a <code>malformedError</code> error. Refer to 1856 * section "Signaling Errors" in Chapter 13 of the spec prose document <a 1857 * href="../../javadocs/overview-summary.html#prose_document">linked in the 1858 * overview summary</a>.</p> 1859 * <p>If the response was successfully processed, send a <code>success</code> 1860 * event as outlined in Chapter 13 "Sending Events" section of the spec prose 1861 * document <a 1862 * href="../../javadocs/overview-summary.html#prose_document">linked in the 1863 * overview summary</a>.</p> 1864 * <p><i>Update Element Processing</i></p> 1865 * <li>If an <code>update</code> element is found in the response 1866 * with the identifier <code>javax.faces.ViewRoot</code>: 1867 * <pre><code><update id="javax.faces.ViewRoot"> 1868 * <![CDATA[...]]> 1869 * </update></code></pre> 1870 * Update the entire DOM replacing the appropriate <code>head</code> and/or 1871 * <code>body</code> sections with the content from the response.</li> 1872 * <li>If an <code>update</code> element is found in the response with the identifier 1873 * <code>javax.faces.ViewState</code>: 1874 * <pre><code><update id="javax.faces.ViewState"> 1875 * <![CDATA[...]]> 1876 * </update></code></pre> 1877 * locate and update the submitting form's <code>javax.faces.ViewState</code> value 1878 * with the <code>CDATA</code> contents from the response.</li> 1879 * <li>If an <code>update</code> element is found in the response with the identifier 1880 * <code>javax.faces.ViewHead</code>: 1881 * <pre><code><update id="javax.faces.ViewHead"> 1882 * <![CDATA[...]]> 1883 * </update></code></pre> 1884 * update the document's <code>head</code> section with the <code>CDATA</code> 1885 * contents from the response.</li> 1886 * <li>If an <code>update</code> element is found in the response with the identifier 1887 * <code>javax.faces.ViewBody</code>: 1888 * <pre><code><update id="javax.faces.ViewBody"> 1889 * <![CDATA[...]]> 1890 * </update></code></pre> 1891 * update the document's <code>body</code> section with the <code>CDATA</code> 1892 * contents from the response.</li> 1893 * <li>For any other <code><update></code> element: 1894 * <pre><code><update id="update id"> 1895 * <![CDATA[...]]> 1896 * </update></code></pre> 1897 * Find the DOM element with the identifier that matches the 1898 * <code><update></code> element identifier, and replace its contents with 1899 * the <code><update></code> element's <code>CDATA</code> contents.</li> 1900 * </li> 1901 * <p><i>Insert Element Processing</i></p> 1902 * <li>If an <code><insert></code> element is found in the response with the 1903 * attribute <code>before</code>: 1904 * <pre><code><insert id="insert id" before="before id"> 1905 * <![CDATA[...]]> 1906 * </insert></code></pre> 1907 * <ul> 1908 * <li>Extract this <code><insert></code> element's <code>CDATA</code> contents 1909 * from the response.</li> 1910 * <li>Find the DOM element whose identifier matches <code>before id</code> and insert 1911 * the <code><insert></code> element's <code>CDATA</code> content before 1912 * the DOM element in the document.</li> 1913 * </ul> 1914 * </li> 1915 * <li>If an <code><insert></code> element is found in the response with the 1916 * attribute <code>after</code>: 1917 * <pre><code><insert id="insert id" after="after id"> 1918 * <![CDATA[...]]> 1919 * </insert></code></pre> 1920 * <ul> 1921 * <li>Extract this <code><insert></code> element's <code>CDATA</code> contents 1922 * from the response.</li> 1923 * <li>Find the DOM element whose identifier matches <code>after id</code> and insert 1924 * the <code><insert></code> element's <code>CDATA</code> content after 1925 * the DOM element in the document.</li> 1926 * </ul> 1927 * </li> 1928 * <p><i>Delete Element Processing</i></p> 1929 * <li>If a <code><delete></code> element is found in the response: 1930 * <pre><code><delete id="delete id"/></code></pre> 1931 * Find the DOM element whose identifier matches <code>delete id</code> and remove it 1932 * from the DOM.</li> 1933 * <p><i>Element Attribute Update Processing</i></p> 1934 * <li>If an <code><attributes></code> element is found in the response: 1935 * <pre><code><attributes id="id of element with attribute"> 1936 * <attribute name="attribute name" value="attribute value"> 1937 * ... 1938 * </attributes></code></pre> 1939 * <ul> 1940 * <li>Find the DOM element that matches the <code><attributes></code> identifier.</li> 1941 * <li>For each nested <code><attribute></code> element in <code><attribute></code>, 1942 * update the DOM element attribute value (whose name matches <code>attribute name</code>), 1943 * with <code>attribute value</code>.</li> 1944 * </ul> 1945 * </li> 1946 * <p><i>JavaScript Processing</i></p> 1947 * <li>If an <code><eval></code> element is found in the response: 1948 * <pre><code><eval> 1949 * <![CDATA[...JavaScript...]]> 1950 * </eval></code></pre> 1951 * <ul> 1952 * <li>Extract this <code><eval></code> element's <code>CDATA</code> contents 1953 * from the response and execute it as if it were JavaScript code.</li> 1954 * </ul> 1955 * </li> 1956 * <p><i>Redirect Processing</i></p> 1957 * <li>If a <code><redirect></code> element is found in the response: 1958 * <pre><code><redirect url="redirect url"/></code></pre> 1959 * Cause a redirect to the url <code>redirect url</code>.</li> 1960 * <p><i>Error Processing</i></p> 1961 * <li>If an <code><error></code> element is found in the response: 1962 * <pre><code><error> 1963 * <error-name>..fully qualified class name string...<error-name> 1964 * <error-message><![CDATA[...]]><error-message> 1965 * </error></code></pre> 1966 * Extract this <code><error></code> element's <code>error-name</code> contents 1967 * and the <code>error-message</code> contents. Signal a <code>serverError</code> passing 1968 * the <code>errorName</code> and <code>errorMessage</code>. Refer to 1969 * section "Signaling Errors" in Chapter 13 of the spec prose document <a 1970 * href="../../javadocs/overview-summary.html#prose_document">linked in the 1971 * overview summary</a>.</li> 1972 * <p><i>Extensions</i></p> 1973 * <li>The <code><extensions></code> element provides a way for framework 1974 * implementations to provide their own information.</li> 1975 * </ul> 1976 * 1977 * </p> 1978 * 1979 * @param request The <code>XMLHttpRequest</code> instance that 1980 * contains the status code and response message from the server. 1981 * 1982 * @param context An object containing the request context, including the following properties: 1983 * the source element, per call onerror callback function, and per call onevent callback function. 1984 * 1985 * @throws Error if request contains no data 1986 * 1987 * @function jsf.ajax.response 1988 */ 1989 response: function response(request, context) { 1990 if (!request) { 1991 throw new Error("jsf.ajax.response: Request parameter is unset"); 1992 } 1993 1994 // ensure context source is the dom element and not the ID 1995 // per 14.4.1 of the 2.0 specification. We're doing it here 1996 // *before* any errors or events are propagated becasue the 1997 // DOM element may be removed after the update has been processed. 1998 if (typeof context.sourceid === 'string') { 1999 context.sourceid = document.getElementById(context.sourceid); 2000 } 2001 2002 var xml = request.responseXML; 2003 if (xml === null) { 2004 sendError(request, context, "emptyResponse"); 2005 return; 2006 } 2007 2008 if (getParseErrorText(xml) !== PARSED_OK) { 2009 sendError(request, context, "malformedXML"); 2010 return; 2011 } 2012 2013 var responseType = xml.getElementsByTagName("partial-response")[0].firstChild; 2014 2015 if (responseType.nodeName === "error") { // it's an error 2016 var errorName = responseType.firstChild.firstChild.nodeValue; 2017 var errorMessage = responseType.firstChild.nextSibling.firstChild.nodeValue; 2018 sendError(request, context, "serverError", null, errorName, errorMessage); 2019 return; 2020 } 2021 2022 2023 if (responseType.nodeName === "redirect") { 2024 window.location = responseType.getAttribute("url"); 2025 return; 2026 } 2027 2028 2029 if (responseType.nodeName !== "changes") { 2030 sendError(request, context, "malformedXML", "Top level node must be one of: changes, redirect, error, received: " + responseType.nodeName + " instead."); 2031 return; 2032 } 2033 2034 2035 var changes = responseType.childNodes; 2036 2037 try { 2038 for (var i = 0; i < changes.length; i++) { 2039 switch (changes[i].nodeName) { 2040 case "update": 2041 doUpdate(changes[i], context); 2042 break; 2043 case "delete": 2044 doDelete(changes[i]); 2045 break; 2046 case "insert": 2047 doInsert(changes[i]); 2048 break; 2049 case "attributes": 2050 doAttributes(changes[i]); 2051 break; 2052 case "eval": 2053 doEval(changes[i]); 2054 break; 2055 case "extension": 2056 // no action 2057 break; 2058 default: 2059 sendError(request, context, "malformedXML", "Changes allowed are: update, delete, insert, attributes, eval, extension. Received " + changes[i].nodeName + " instead."); 2060 return; 2061 } 2062 } 2063 } catch (ex) { 2064 sendError(request, context, "malformedXML", ex.message); 2065 return; 2066 } 2067 sendEvent(request, context, "success"); 2068 2069 } 2070 }; 2071 }(); 2072 2073 /** 2074 * 2075 * <p>Return the value of <code>Application.getProjectStage()</code> for 2076 * the currently running application instance. Calling this method must 2077 * not cause any network transaction to happen to the server.</p> 2078 * <p><b>Usage:</b></p> 2079 * <pre><code> 2080 * var stage = jsf.getProjectStage(); 2081 * if (stage === ProjectStage.Development) { 2082 * ... 2083 * } else if stage === ProjectStage.Production) { 2084 * ... 2085 * } 2086 * </code></pre> 2087 * 2088 * @returns String <code>String</code> representing the current state of the 2089 * running application in a typical product development lifecycle. Refer 2090 * to <code>javax.faces.application.Application.getProjectStage</code> and 2091 * <code>javax.faces.application.ProjectStage</code>. 2092 * @function jsf.getProjectStage 2093 */ 2094 jsf.getProjectStage = function() { 2095 // First, return cached value if available 2096 if (typeof mojarra !== 'undefined' && typeof mojarra.projectStageCache !== 'undefined') { 2097 return mojarra.projectStageCache; 2098 } 2099 var scripts = document.getElementsByTagName("script"); // nodelist of scripts 2100 var script; // jsf.js script 2101 var s = 0; // incremental variable for for loop 2102 var stage; // temp value for stage 2103 var match; // temp value for match 2104 while (s < scripts.length) { 2105 if (typeof scripts[s].src === 'string' && scripts[s].src.match('\/javax\.faces\.resource\/jsf\.js\?.*ln=javax\.faces')) { 2106 script = scripts[s].src; 2107 break; 2108 } 2109 s++; 2110 } 2111 if (typeof script == "string") { 2112 match = script.match("stage=(.*)"); 2113 if (match) { 2114 stage = match[1]; 2115 } 2116 } 2117 if (typeof stage === 'undefined' || !stage) { 2118 stage = "Production"; 2119 } 2120 2121 mojarra = mojarra || {}; 2122 mojarra.projectStageCache = stage; 2123 2124 return mojarra.projectStageCache; 2125 }; 2126 2127 2128 /** 2129 * <p>Collect and encode state for input controls associated 2130 * with the specified <code>form</code> element. This will include 2131 * all input controls of type <code>hidden</code>.</p> 2132 * <p><b>Usage:</b></p> 2133 * <pre><code> 2134 * var state = jsf.getViewState(form); 2135 * </pre></code> 2136 * 2137 * @param form The <code>form</code> element whose contained 2138 * <code>input</code> controls will be collected and encoded. 2139 * Only successful controls will be collected and encoded in 2140 * accordance with: <a href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2"> 2141 * Section 17.13.2 of the HTML Specification</a>. 2142 * 2143 * @returns String The encoded state for the specified form's input controls. 2144 * @function jsf.getViewState 2145 */ 2146 jsf.getViewState = function(form) { 2147 if (!form) { 2148 throw new Error("jsf.getViewState: form must be set"); 2149 } 2150 var els = form.elements; 2151 var len = els.length; 2152 // create an array which we'll use to hold all the intermediate strings 2153 // this bypasses a problem in IE when repeatedly concatenating very 2154 // large strings - we'll perform the concatenation once at the end 2155 var qString = []; 2156 var addField = function(name, value) { 2157 var tmpStr = ""; 2158 if (qString.length > 0) { 2159 tmpStr = "&"; 2160 } 2161 tmpStr += encodeURIComponent(name) + "=" + encodeURIComponent(value); 2162 qString.push(tmpStr); 2163 }; 2164 for (var i = 0; i < len; i++) { 2165 var el = els[i]; 2166 if (!el.disabled) { 2167 switch (el.type) { 2168 case 'text': 2169 case 'password': 2170 case 'hidden': 2171 case 'textarea': 2172 addField(el.name, el.value); 2173 break; 2174 case 'select-one': 2175 if (el.selectedIndex >= 0) { 2176 addField(el.name, el.options[el.selectedIndex].value); 2177 } 2178 break; 2179 case 'select-multiple': 2180 for (var j = 0; j < el.options.length; j++) { 2181 if (el.options[j].selected) { 2182 addField(el.name, el.options[j].value); 2183 } 2184 } 2185 break; 2186 case 'checkbox': 2187 case 'radio': 2188 if (el.checked) { 2189 addField(el.name, el.value); 2190 } 2191 break; 2192 } 2193 } 2194 } 2195 // concatenate the array 2196 return qString.join(""); 2197 }; 2198 2199 /** 2200 * The namespace for JavaServer Faces JavaScript utilities. 2201 * @name jsf.util 2202 * @namespace 2203 */ 2204 jsf.util = {}; 2205 2206 /** 2207 * <p>A varargs function that invokes an arbitrary number of scripts. 2208 * If any script in the chain returns false, the chain is short-circuited 2209 * and subsequent scripts are not invoked. Any number of scripts may 2210 * specified after the <code>event</code> argument.</p> 2211 * 2212 * @param source The DOM element that triggered this Ajax request, or an 2213 * id string of the element to use as the triggering element. 2214 * @param event The DOM event that triggered this Ajax request. The 2215 * <code>event</code> argument is optional. 2216 * 2217 * @returns boolean <code>false</code> if any scripts in the chain return <code>false</code>, 2218 * otherwise returns <code>true</code> 2219 * 2220 * @function jsf.util.chain 2221 */ 2222 jsf.util.chain = function(source, event) { 2223 2224 if (arguments.length < 3) { 2225 return true; 2226 } 2227 2228 // RELEASE_PENDING rogerk - shouldn't this be getElementById instead of null 2229 var thisArg = (typeof source === 'object') ? source : null; 2230 2231 // Call back any scripts that were passed in 2232 for (var i = 2; i < arguments.length; i++) { 2233 2234 var f = new Function("event", arguments[i]); 2235 var returnValue = f.call(thisArg, event); 2236 2237 if (returnValue === false) { 2238 return false; 2239 } 2240 } 2241 return true; 2242 2243 }; 2244 2245 /** 2246 * <p>An integer specifying the specification version that this file implements. 2247 * It's format is: rightmost two digits, bug release number, next two digits, 2248 * minor release number, leftmost digits, major release number. 2249 * This number may only be incremented by a new release of the specification.</p> 2250 */ 2251 jsf.specversion = 20000; 2252 2253 /** 2254 * <p>An integer specifying the implementation version that this file implements. 2255 * It's a monotonically increasing number, reset with every increment of 2256 * <code>jsf.specversion</code> 2257 * This number is implementation dependent.</p> 2258 */ 2259 jsf.implversion = 3; 2260 2261 2262 } //end if version detection block 2263