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