1 /* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to you under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /** 18 * @class 19 * @name _Dom 20 * @memberOf myfaces._impl._util 21 * @extends myfaces._impl.core._Runtime 22 * @description Object singleton collection of dom helper routines 23 * (which in later incarnations will 24 * get browser specific speed optimizations) 25 * 26 * Since we have to be as tight as possible 27 * we will focus with our dom routines to only 28 * the parts which our impl uses. 29 * A jquery like query API would be nice 30 * but this would blow up our codebase significantly 31 */ 32 myfaces._impl.core._Runtime.singletonExtendClass("myfaces._impl._util._Dom", Object, 33 /** 34 * @lends myfaces._impl._util._Dom.prototype 35 */ 36 { 37 IE_QUIRKS_EVENTS : { 38 "onabort": true, 39 "onload":true, 40 "onunload":true, 41 "onchange": true, 42 "onsubmit": true, 43 "onreset": true, 44 "onselect": true, 45 "onblur": true, 46 "onfocus": true, 47 "onkeydown": true, 48 "onkeypress": true, 49 "onkeyup": true, 50 "onclick": true, 51 "ondblclick": true, 52 "onmousedown": true, 53 "onmousemove": true, 54 "onmouseout": true, 55 "onmouseover": true, 56 "onmouseup": true 57 }, 58 59 /*table elements which are used in various parts */ 60 TABLE_ELEMS: { 61 "thead": true, 62 "tbody": true, 63 "tr": true, 64 "th": true, 65 "td": true, 66 "tfoot" : true 67 }, 68 69 _Lang: myfaces._impl._util._Lang, 70 _RT: myfaces._impl.core._Runtime, 71 _dummyPlaceHolder:null, 72 73 /** 74 * standard constructor 75 */ 76 constructor_: function() { 77 //we have to trigger it upfront because mozilla runs the eval 78 //after the dom updates and hence causes a race conditon if used on demand 79 //under normal circumstances this works, if there are no normal ones 80 //then this also will work at the second time, but the onload handler 81 //should cover 99% of all use cases to avoid a loading race condition 82 var b = myfaces._impl.core._Runtime.browser; 83 84 if (b.isIE <= 6 && b.isIEMobile) { 85 //winmobile hates add onLoad, and checks on the construct 86 //it does not eval scripts anyway 87 myfaces.config = myfaces.config || {}; 88 myfaces.config._autoeval = false; 89 return; 90 } 91 this._RT.addOnLoad(window, function() { 92 myfaces._impl._util._Dom.isManualScriptEval(); 93 }); 94 //safety fallback if the window onload handler is overwritten and not chained 95 if (document.body) { 96 this._RT.addOnLoad(document.body, function() { 97 myfaces._impl._util._Dom.isManualScriptEval(); 98 }); 99 } 100 //now of the onload handler also is overwritten we have a problem 101 }, 102 103 /** 104 * Run through the given Html item and execute the inline scripts 105 * (IE doesn't do this by itself) 106 * @param {Node} item 107 */ 108 runScripts: function(item, xmlData) { 109 var finalScripts = []; 110 var execScrpt = this._Lang.hitch(this, function(item) { 111 if (item.tagName && this._Lang.equalsIgnoreCase(item.tagName, "script")) { 112 var src = item.getAttribute('src'); 113 if ('undefined' != typeof src 114 && null != src 115 && src.length > 0 116 ) { 117 //we have to move this into an inner if because chrome otherwise chokes 118 //due to changing the and order instead of relying on left to right 119 if ((src.indexOf("ln=scripts") == -1 && src.indexOf("ln=javax.faces") == -1) || (src.indexOf("/jsf.js") == -1 120 && src.indexOf("/jsf-uncompressed.js") == -1)) 121 if (finalScripts.length) { 122 //script source means we have to eval the existing 123 //scripts before running the include 124 this._RT.globalEval(finalScripts.join("\n")); 125 126 finalScripts = []; 127 } 128 this._RT.loadScriptEval(src, item.getAttribute('type'), false, "UTF-8", false); 129 //TODO handle embedded scripts 130 } else { 131 // embedded script auto eval 132 var test = (!xmlData) ? item.text : this._Lang.serializeChilds(item); 133 var go = true; 134 while (go) { 135 go = false; 136 if (test.substring(0, 1) == " ") { 137 test = test.substring(1); 138 go = true; 139 } 140 if (test.substring(0, 4) == "<!--") { 141 test = test.substring(4); 142 go = true; 143 } 144 if (test.substring(0, 11) == "//<![CDATA[") { 145 test = test.substring(11); 146 go = true; 147 } 148 } 149 // we have to run the script under a global context 150 //we store the script for less calls to eval 151 finalScripts.push(test); 152 153 } 154 } 155 }); 156 try { 157 var scriptElements = this.findByTagName(item, "script", true); 158 if (scriptElements == null) return; 159 for (var cnt = 0; cnt < scriptElements.length; cnt++) { 160 execScrpt(scriptElements[cnt]); 161 } 162 if (finalScripts.length) { 163 this._RT.globalEval(finalScripts.join("\n")); 164 } 165 } finally { 166 //the usual ie6 fix code 167 //the IE6 garbage collector is broken 168 //nulling closures helps somewhat to reduce 169 //mem leaks, which are impossible to avoid 170 //at this browser 171 execScrpt = null; 172 } 173 }, 174 175 176 /** 177 * determines to fetch a node 178 * from its id or name, the name case 179 * only works if the element is unique in its name 180 * @param {String} elem 181 */ 182 byIdOrName: function(elem) { 183 if(!this._Lang.isString(elem)) return elem; 184 if(!elem) return null; 185 var ret = this.byId(elem); 186 if(ret) return ret; 187 //we try the unique name fallback 188 var items = document.getElementsByName(elem); 189 return ((items.length == 1)? items[0]: null); 190 }, 191 192 /** 193 * node id or name, determines the valid form identifier of a node 194 * depending on its uniqueness 195 * 196 * Usually the id is chosen for an elem, but if the id does not 197 * exist we try a name fallback. If the passed element has a unique 198 * name we can use that one as subsequent identifier. 199 * 200 * 201 * @param {String} elem 202 */ 203 nodeIdOrName: function(elem) { 204 if (elem) { 205 //just to make sure that the pas 206 var origElemIdentifier = elem; 207 elem = this.byId(elem); 208 if(!elem) return null; 209 //detached element handling, we also store the element name 210 //to get a fallback option in case the identifier is not determinable 211 // anymore, in case of a framework induced detachment the element.name should 212 // be shared if the identifier is not determinable anymore 213 //the downside of this method is the element name must be unique 214 //which in case of jsf it is 215 var elementId = elem.id || elem.name; 216 if ((elem.id == null || elem.id == '') && elem.name) { 217 elementId = elem.name; 218 219 //last check for uniqueness 220 if(this.getElementsByName(elementId).length > 1) { 221 //no unique element name so we need to perform 222 //a return null to let the caller deal with this issue 223 return null; 224 } 225 } 226 return elementId; 227 } 228 return null; 229 }, 230 231 /** 232 * Simple delete on an existing item 233 */ 234 deleteItem: function(itemIdToReplace) { 235 var item = this.byId(itemIdToReplace); 236 if (!item) { 237 throw Error("_Dom.deleteItem Unknown Html-Component-ID: " + itemIdToReplace); 238 } 239 240 this._removeNode(item, false); 241 }, 242 243 244 /** 245 * Checks whether the browser is dom compliant. 246 * Dom compliant means that it performs the basic dom operations safely 247 * without leaking and also is able to perform a native setAttribute 248 * operation without freaking out 249 * 250 * 251 * Not dom compliant browsers are all microsoft browsers in quirks mode 252 * and ie6 and ie7 to some degree in standards mode 253 * and pretty much every browser who cannot create ranges 254 * (older mobile browsers etc...) 255 * 256 * We dont do a full browser detection here because it probably is safer 257 * to test for existing features to make an assumption about the 258 * browsers capabilities 259 */ 260 isDomCompliant: function() { 261 if('undefined' == typeof this._isCompliantBrowser) { 262 this._isCompliantBrowser = !! ((window.Range 263 && typeof Range.prototype.createContextualFragment == 'function') //createContextualFragment hints to a no quirks browser but we need more fallbacks 264 || document.querySelectoryAll //query selector all hints to html5 capabilities 265 || document.createTreeWalker); //treewalker is either firefox 3.5+ or ie9 standards mode 266 } 267 return this._isCompliantBrowser; 268 }, 269 270 /** 271 * outerHTML replacement which works cross browserlike 272 * but still is speed optimized 273 * 274 * @param item the item to be replaced 275 * @param markup the markup for the replacement 276 */ 277 outerHTML : function(item, markup) { 278 if (!item) { 279 throw Error(this._Lang.getMessage("ERR_MUST_BE_PROVIDED1",null,"myfaces._impl._util._Dom.outerHTML", "item")); 280 } 281 if (!markup) { 282 throw Error(this._Lang.getMessage("ERR_MUST_BE_PROVIDED1",null,"myfaces._impl._util._Dom.outerHTML", "markup")); 283 } 284 285 markup = this._Lang.trim(markup); 286 if (markup !== "") { 287 var ret = null; 288 289 //w3c compliant browsers with proper contextual fragments 290 var parentNode; 291 // we try to determine the browsers compatibility 292 // level to standards dom level 2 via various methods 293 if (this.isDomCompliant()) { 294 ret = this._outerHTMLCompliant(item, markup); 295 } else { 296 297 ret = this._outerHTMLNonCompliant(item, markup); 298 } 299 300 // and remove the old item 301 //first we have to save the node newly insert for easier access in our eval part 302 if (this.isManualScriptEval()) { 303 var isArr = ret instanceof Array; 304 if (isArr && ret.length) { 305 for (var cnt = 0; cnt < ret.length; cnt++) { 306 this.runScripts(ret[cnt]); 307 } 308 } else if (!isArr) { 309 this.runScripts(ret); 310 } 311 } 312 return ret; 313 } 314 // and remove the old item, in case of an empty newtag and do nothing else 315 this._removeNode(item, false); 316 return null; 317 }, 318 /** 319 * detchaes a set of nodes from their parent elements 320 * in a browser independend manner 321 * @param {Nodelist} items the items which need to be detached 322 * @return {Array} an array of nodes with the detached dom nodes 323 */ 324 detach: function(items) { 325 var ret = []; 326 if('undefined' != typeof items.nodeType) { 327 if(items.parentNode) { 328 ret.push(items.parentNode.removeChild(items)); 329 } else { 330 ret.push(items); 331 } 332 return ret; 333 } 334 //all ies treat node lists not as arrays so we have to take 335 //an intermediate step 336 var items = this._Lang.objToArray(items); 337 for(var cnt = 0; cnt < items.length; cnt++) { 338 ret.push(items[cnt].parentNode.removeChild(items[cnt])); 339 } 340 return ret; 341 }, 342 343 _outerHTMLCompliant: function(item, markup) { 344 var evalNodes; 345 //table element replacements like thead, tbody etc... have to be treated differently 346 if (this._isTableElement(item)) { 347 evalNodes = this._buildTableNodes(item, markup); 348 } else { 349 evalNodes = this._buildNodesCompliant(markup); 350 } 351 var evalNodeLen = evalNodes.length; 352 353 if (evalNodeLen == 1) { 354 var ret = evalNodes[0]; 355 item.parentNode.replaceChild(ret, item); 356 return ret; 357 } else { 358 return this.replaceElements(item, evalNodes); 359 } 360 }, 361 362 _isTable: function(item) { 363 var itemNodeName = (item.nodeName || item.tagName).toLowerCase(); 364 return itemNodeName == "table"; 365 }, 366 367 /** 368 * checks if the provided element is a subelement of a table element 369 * @param itemNodeName 370 */ 371 _isTableElement: function(item) { 372 var itemNodeName = (item.nodeName || item.tagName).toLowerCase(); 373 return !!this.TABLE_ELEMS[itemNodeName]; 374 }, 375 376 377 /** 378 * now to the evil browsers 379 * of what we are dealing with is various bugs 380 * first a simple replaceElement leaks memory 381 * secondly embedded scripts can be swallowed upon 382 * innerHTML, we probably could also use direct outerHTML 383 * but then we would run into the script swallow bug 384 * 385 * the entire mess is called IE6 and IE7 386 * 387 * @param item 388 * @param markup 389 */ 390 _outerHTMLNonCompliant: function(item, markup) { 391 392 var b = this._RT.browser; 393 var evalNodes = null; 394 395 try { 396 //check for a subtable rendering case 397 398 if (this._isTableElement(item)) { 399 evalNodes = this._buildTableNodes(item, markup); 400 } else { 401 //if no subtable element is given we simply work on our 402 //normal markup 403 evalNodes = this._buildNodesNonCompliant(markup); 404 } 405 406 if (evalNodes.length == 1) { 407 var ret = evalNodes[0]; 408 this.replaceElement(item, evalNodes[0]); 409 return ret; 410 } else { 411 return this.replaceElements(item, evalNodes); 412 } 413 414 } finally { 415 416 var dummyPlaceHolder = this.getDummyPlaceHolder(); 417 //now that Microsoft has finally given 418 //ie a working gc in 8 we can skip the costly operation 419 var b = myfaces._impl.core._Runtime.browser; 420 421 if (b.isIE && b.isIE < 8) { 422 this._removeChildNodes(dummyPlaceHolder, false); 423 } 424 dummyPlaceHolder.innerHTML = ""; 425 } 426 427 }, 428 429 /** 430 * non ie browsers do not have problems with embedded scripts or any other construct 431 * we simply can use an innerHTML in a placeholder 432 * 433 * @param markup the markup to be used 434 */ 435 _buildNodesCompliant: function(markup) { 436 var dummyPlaceHolder = this.getDummyPlaceHolder(); //document.createElement("div"); 437 dummyPlaceHolder.innerHTML = markup; 438 return this._Lang.objToArray(dummyPlaceHolder.childNodes); 439 }, 440 441 442 443 444 /** 445 * builds up a correct dom subtree 446 * if the markup is part of table nodes 447 * The usecase for this is to allow subtable rendering 448 * like single rows thead or tbody 449 * 450 * @param item 451 * @param markup 452 */ 453 _buildTableNodes: function(item, markup) { 454 var evalNodes; 455 var itemNodeName = (item.nodeName || item.tagName).toLowerCase(); 456 var probe = this.getDummyPlaceHolder(); //document.createElement("div"); 457 if (itemNodeName == "td") { 458 probe.innerHTML = "<table><tbody><tr><td></td></tr></tbody></table>"; 459 } else { 460 probe.innerHTML = "<table><" + itemNodeName + "></" + itemNodeName + ">" + "</table>"; 461 } 462 var depth = this._determineDepth(probe); 463 464 this._removeChildNodes(probe, false); 465 probe.innerHTML = ""; 466 467 var dummyPlaceHolder = this.getDummyPlaceHolder();//document.createElement("div"); 468 if (itemNodeName == "td") { 469 dummyPlaceHolder.innerHTML = "<table><tbody><tr>" + markup + "</tr></tbody></table>"; 470 } else { 471 dummyPlaceHolder.innerHTML = "<table>" + markup + "</table>"; 472 } 473 evalNodes = dummyPlaceHolder; 474 for (var cnt = 0; cnt < depth; cnt++) { 475 evalNodes = evalNodes.childNodes[0]; 476 } 477 evalNodes = (evalNodes.parentNode) ? evalNodes.parentNode.childNodes : null; 478 return this.detach(evalNodes); 479 } 480 , 481 482 /** 483 * builds the ie nodes properly in a placeholder 484 * and bypasses a non script insert bug that way 485 * @param markup the marku code 486 */ 487 _buildNodesNonCompliant: function(markup) { 488 489 var evalNodes = null; 490 491 //now to the non w3c compliant browsers 492 //http://blogs.perl.org/users/clinton_gormley/2010/02/forcing-ie-to-accept-script-tags-in-innerhtml.html 493 //we have to cope with deficiencies between ie and its simulations in this case 494 var probe = this.getDummyPlaceHolder();//document.createElement("div"); 495 496 probe.innerHTML = "<table><tbody><tr><td><div></div></td></tr></tbody></table>"; 497 498 //we have customers using html unit, this has a bug in the table resolution 499 //hence we determine the depth dynamically 500 var depth = this._determineDepth(probe); 501 var newProbe = probe; 502 this._removeChildNodes(probe, false); 503 probe.innerHTML = ""; 504 505 var dummyPlaceHolder = this.getDummyPlaceHolder();//document.createElement("div"); 506 507 //fortunately a table element also works which is less critical than form elements regarding 508 //the inner content 509 dummyPlaceHolder.innerHTML = "<table><tbody><tr><td>" + markup + "</td></tr></tbody></table>"; 510 evalNodes = dummyPlaceHolder; 511 512 for (var cnt = 0; cnt < depth; cnt++) { 513 evalNodes = evalNodes.childNodes[0]; 514 } 515 var ret = (evalNodes.parentNode) ? this.detach(evalNodes.parentNode.childNodes) : null; 516 517 if ('undefined' == typeof evalNodes || null == evalNodes) { 518 //fallback for htmlunit which should be good enough 519 //to run the tests, maybe we have to wrap it as well 520 dummyPlaceHolder.innerHTML = "<div>" + markup + "</div>"; 521 //note this is triggered only in htmlunit no other browser 522 //so we are save here 523 evalNodes = this.detach(dummyPlaceHolder.childNodes[0].childNodes); 524 } 525 526 this._removeChildNodes(dummyPlaceHolder, false); 527 //ie fix any version, ie does not return true javascript arrays so we have to perform 528 //a cross conversion 529 return ret; 530 531 }, 532 533 _determineDepth: function(probe) { 534 var depth = 0; 535 var newProbe = probe; 536 for (;newProbe && 537 newProbe.childNodes && 538 newProbe.childNodes.length && 539 newProbe.nodeType == 1; depth++) { 540 newProbe = newProbe.childNodes[0]; 541 } 542 return depth; 543 }, 544 545 546 //now to another nasty issue: 547 //for ie we have to walk recursively over all nodes: 548 //http://msdn.microsoft.com/en-us/library/bb250448%28VS.85%29.aspx 549 //http://weblogs.java.net/blog/driscoll/archive/2009/11/13/ie-memory-management-and-you 550 //http://home.orange.nl/jsrosman/ 551 //http://www.quirksmode.org/blog/archives/2005/10/memory_leaks_li.html 552 //http://www.josh-davis.org/node/7 553 _removeNode: function(node, breakEventsOpen) { 554 if (!node) return; 555 var b = this._RT.browser; 556 if (this.isDomCompliant()) { 557 //recursive descension only needed for old ie versions 558 //all newer browsers cleanup the garbage just fine without it 559 //thank you 560 if ('undefined' != typeof node.parentNode && null != node.parentNode) //if the node has a parent 561 node.parentNode.removeChild(node); 562 return; 563 } 564 565 //now to the browsers with non working garbage collection 566 this._removeChildNodes(node, breakEventsOpen); 567 568 try { 569 //outer HTML setting is only possible in earlier IE versions all modern browsers throw an exception here 570 //again to speed things up we precheck first 571 if(!this._isTableElement(childNode)) { 572 //we do not do a table structure innnerhtml on table elements except td 573 //htmlunit rightfully complains that we should not do it 574 node.innerHTML = ""; 575 } 576 if (b.isIE && 'undefined' != typeof node.outerHTML) {//ie8+ check done earlier we skip it here 577 node.outerHTML = ''; 578 } else { 579 node = this.detach(node)[0]; 580 } 581 if (!b.isIEMobile) { 582 delete node; 583 } 584 } catch (e) { 585 //on some elements we might not have covered by our table check on the outerHTML 586 // can fail we skip those in favor of stability 587 try { 588 // both innerHTML and outerHTML fails when <tr> is the node, but in that case 589 // we need to force node removal, otherwise it will be on the tree (IE 7 IE 6) 590 this.detach(node); 591 } catch (e1) { 592 } 593 } 594 } 595 , 596 597 598 599 /** 600 * recursive delete child nodes 601 * node, this method only makes sense in the context of IE6 + 7 hence 602 * it is not exposed to the public API, modern browsers 603 * can garbage collect the nodes just fine by doing the standard removeNode method 604 * from the dom API! 605 * 606 * @param node the node from which the childnodes have to be deletd 607 * @param breakEventsOpen if set to true a standard events breaking is performed 608 */ 609 _removeChildNodes: function(node, breakEventsOpen) { 610 if (!node) return; 611 612 //node types which cannot be cleared up by normal means 613 var disallowedNodes = this.TABLE_ELEMS; 614 615 //for now we do not enable it due to speed reasons 616 //normally the framework has to do some event detection 617 //which we cannot do yet, I will dig for options 618 //to enable it in a speedly manner 619 //ie7 fixes this area anyway 620 //this.breakEvents(node); 621 622 var b = this._RT.browser; 623 if (breakEventsOpen) { 624 this.breakEvents(node); 625 } 626 627 for (var cnt = node.childNodes.length - 1; cnt >= 0; cnt -= 1) { 628 var childNode = node.childNodes[cnt]; 629 //we cannot use our generic recursive tree walking due to the needed head recursion 630 //to clean it up bottom up, the tail recursion we were using in the search either would use more time 631 //because we had to walk and then clean bottom up, so we are going for a direct head recusion here 632 if ('undefined' != typeof childNode.childNodes && node.childNodes.length) 633 this._removeChildNodes(childNode); 634 try { 635 var nodeName = (childNode.nodeName || childNode.tagName) ? (childNode.nodeName || childNode.tagName).toLowerCase() : null; 636 //ie chokes on clearing out table inner elements, this is also covered by our empty 637 //catch block, but to speed things up it makes more sense to precheck that 638 if (!disallowedNodes[nodeName]) { 639 //outer HTML setting is only possible in earlier IE versions all modern browsers throw an exception here 640 //again to speed things up we precheck first 641 if(!this._isTableElement(childNode)) { //table elements cannot be deleted 642 childNode.innerHTML = ""; 643 } 644 if (b.isIE && b.isIE < 8 && 'undefined' != childNode.outerHTML) 645 childNode.outerHTML = ''; 646 else { 647 node.removeChild(childNode); 648 } 649 if (!b.isIEMobile) { 650 delete childNode; 651 } 652 } 653 } catch (e) { 654 //on some elements the outerHTML can fail we skip those in favor 655 //of stability 656 657 } 658 } 659 } 660 , 661 662 /** 663 * break the standard events from an existing dom node 664 * (note this method is not yet used, but can be used 665 * by framework authors to get rid of ie circular event references) 666 * 667 * another way probably would be to check all attributes of a node 668 * for a function and if one is present break it by nulling it 669 * I have to do some further investigation on this. 670 * 671 * The final fix is to move away from ie6 at all which is the root cause of 672 * this. 673 * 674 * @param node the node which has to be broken off its events 675 */ 676 breakEvents: function(node) { 677 if (!node) return; 678 var evtArr = this.IE_QUIRKS_EVENTS; 679 for (var key in evtArr) { 680 if (key != "onunload" && node[key]) { 681 node[key] = null; 682 } 683 } 684 } 685 , 686 687 688 /** 689 * for performance reasons we work with replaceElement and replaceElements here 690 * after measuring performance it has shown that passing down an array instead 691 * of a single node makes replaceElement twice as slow, however 692 * a single node case is the 95% case 693 * 694 * @param item 695 * @param evalNodes 696 */ 697 replaceElement: function(item, evalNode) { 698 699 var _Browser = this._RT.browser; 700 if (!_Browser.isIE || _Browser.isIE >= 8) { 701 //standards conform no leaking browser 702 item.parentNode.replaceChild(evalNode, item); 703 } else { 704 //browsers with defect garbage collection 705 item.parentNode.insertBefore(evalNode, item); 706 this._removeNode(item, false); 707 } 708 } 709 , 710 711 712 /** 713 * replaces an element with another element or a set of elements 714 * 715 * @param item the item to be replaced 716 * 717 * @param evalNodes the elements 718 */ 719 replaceElements: function (item, evalNodes) { 720 var evalNodesDefined = evalNodes && 'undefined' != typeof evalNodes.length; 721 if (!evalNodesDefined) { 722 throw new Error(this._Lang.getMessage("ERR_REPLACE_EL")); 723 } 724 725 var parentNode = item.parentNode; 726 727 var sibling = item.nextSibling; 728 var resultArr = this._Lang.objToArray(evalNodes); 729 730 for (var cnt = 0; cnt < resultArr.length; cnt++) { 731 if (cnt == 0) { 732 this.replaceElement(item, resultArr[cnt]); 733 } else { 734 if (sibling) { 735 parentNode.insertBefore(resultArr[cnt], sibling); 736 } else { 737 parentNode.appendChild(resultArr[cnt]); 738 739 } 740 } 741 } 742 743 return resultArr; 744 } 745 , 746 747 /** 748 * optimized search for an array of tag names 749 * 750 * @param fragment the fragment which should be searched for 751 * @param tagNames an map indx of tag names which have to be found 752 * @param deepScan if set to true a deep scan is performed otherwise a shallow scan 753 */ 754 findByTagNames: function(fragment, tagNames, deepScan) { 755 var nodeType = fragment.nodeType; 756 if(nodeType != 1 && nodeType != 9 && nodeType != 11) return null; 757 758 759 //shortcut for single components 760 if (!deepScan && tagNames[fragment.tagName.toLowerCase()]) { 761 return fragment; 762 } 763 764 //shortcut elementsByTagName 765 if (deepScan && this._Lang.exists(fragment, "getElementsByTagName")) { 766 var retArr = []; 767 for (var key in tagNames) { 768 var foundElems = this.findByTagName(fragment, key, deepScan); 769 if (foundElems) { 770 retArr = retArr.concat(foundElems); 771 } 772 } 773 return retArr; 774 } else if (deepScan) { 775 //no node type with child tags we can handle that without node type checking 776 return null; 777 } 778 779 //now the filter function checks case insensitively for the tag names needed 780 var filter = function(node) { 781 return node.tagName && tagNames[node.tagName.toLowerCase()]; 782 }; 783 784 //now we run an optimized find all on it 785 try { 786 return this.findAll(fragment, filter, deepScan); 787 } finally { 788 //the usual IE6 is broken, fix code 789 filter = null; 790 } 791 } 792 , 793 794 /** 795 * determines the number of nodes according to their tagType 796 * 797 * @param {Node} fragment (Node or fragment) the fragment to be investigated 798 * @param {String} tagName the tag name (lowercase) 799 * @param {Boolean} deepScan if set to true a found element does not prevent to scan deeper 800 * (the normal usecase is false, which means if the element is found only its 801 * adjacent elements will be scanned, due to the recursive descension 802 * this should work out with elements with different nesting depths but not being 803 * parent and child to each other 804 * 805 * @return the child elements as array or null if nothing is found 806 * 807 */ 808 findByTagName : function(fragment, tagName, deepScan) { 809 var nodeType = fragment.nodeType; 810 if(nodeType != 1 && nodeType != 9 && nodeType != 11) return null; 811 812 813 //remapping to save a few bytes 814 var _Lang = this._Lang; 815 816 deepScan = !!deepScan; 817 818 //elements by tagname is the fastest, ie throws an error on fragment.getElementsByTagName, the exists type 819 //via namespace array checking is safe 820 if (deepScan && _Lang.exists(fragment, "getElementsByTagName")) { 821 var ret = _Lang.objToArray(fragment.getElementsByTagName(tagName)); 822 if (fragment.tagName && _Lang.equalsIgnoreCase(fragment.tagName, tagName)) ret.unshift(fragment); 823 return ret; 824 } else if (deepScan) { 825 //no node type with child tags we can handle that without node type checking 826 return null; 827 } 828 //since getElementsByTagName is a standardized dom node function and ie also supports 829 //it since 5.5 830 //we need no fallback to the query api and the recursive filter 831 //also is only needed in case of no deep scan or non dom elements 832 833 var filter = function(node) { 834 return node.tagName && _Lang.equalsIgnoreCase(node.tagName, tagName); 835 }; 836 try { 837 return this.findAll(fragment, filter, deepScan); 838 } finally { 839 //the usual IE6 is broken, fix code 840 filter = null; 841 _Lang = null; 842 } 843 844 } 845 , 846 847 findByName : function(fragment, name, deepScan) { 848 var nodeType = fragment.nodeType; 849 if(nodeType != 1 && nodeType != 9 && nodeType != 11) return null; 850 851 var _Lang = this._Lang; 852 var filter = function(node) { 853 return node.name && _Lang.equalsIgnoreCase(node.name, name); 854 }; 855 try { 856 deepScan = !!deepScan; 857 858 //elements byName is the fastest 859 if (deepScan && _Lang.exists(fragment, "getElementsByName")) { 860 var ret = _Lang.objToArray(fragment.getElementsByName(name)); 861 if (fragment.name == name) ret.unshift(fragment); 862 return ret; 863 } 864 865 if (deepScan && _Lang.exists(fragment, "querySelectorAll")) { 866 try { 867 var newName = name; 868 if (_Lang.isString(newName)) { 869 newName = _Lang.escapeString(newName); 870 } 871 var result = fragment.querySelectorAll("[name=" + newName + "]"); 872 if (fragment.nodeType == 1 && filter(fragment)) { 873 result = (result == null) ? [] : _Lang.objToArray(result); 874 result.push(fragment); 875 } 876 return result; 877 } catch(e) { 878 //in case the selector bombs we retry manually 879 } 880 } 881 882 return this.findAll(fragment, filter, deepScan); 883 } finally { 884 //the usual IE6 is broken, fix code 885 filter = null; 886 _Lang = null; 887 } 888 } 889 , 890 891 892 893 /** 894 * a filtered findAll for subdom treewalking 895 * (which uses browser optimizations wherever possible) 896 * 897 * @param {|Node|} rootNode the rootNode so start the scan 898 * @param filter filter closure with the syntax {boolean} filter({Node} node) 899 * @param deepScan if set to true or not set at all a deep scan is performed (for form scans it does not make much sense to deeply scan) 900 */ 901 findAll : function(rootNode, filter, deepScan) { 902 this._Lang.assertType(filter, "function"); 903 deepScan = !!deepScan; 904 905 if (document.createTreeWalker && NodeFilter) { 906 return this._iteratorSearchAll(rootNode, filter, deepScan); 907 } else { 908 return this._recursionSearchAll(rootNode, filter, deepScan); 909 } 910 911 } 912 , 913 914 /** 915 * classical recursive way which definitely will work on all browsers 916 * including the IE6 917 * 918 * @param rootNode the root node 919 * @param filter the filter to be applied to 920 * @param deepScan if set to true a deep scan is performed 921 */ 922 _recursionSearchAll: function(rootNode, filter, deepScan) { 923 var ret = []; 924 //fix the value to prevent undefined errors 925 926 if (filter(rootNode)) { 927 ret.push(rootNode); 928 if (!deepScan) return ret; 929 } 930 931 // 932 if (!rootNode.childNodes) { 933 return ret; 934 } 935 936 //subfragment usecases 937 938 var retLen = ret.length; 939 var childLen = rootNode.childNodes.length; 940 for (var cnt = 0; (deepScan || retLen == 0) && cnt < childLen; cnt++) { 941 ret = ret.concat(this._recursionSearchAll(rootNode.childNodes[cnt], filter, deepScan)); 942 } 943 return ret; 944 } 945 , 946 947 /** 948 * the faster dom iterator based search, works on all newer browsers 949 * except ie8 which already have implemented the dom iterator functions 950 * of html 5 (which is pretty all standard compliant browsers) 951 * 952 * The advantage of this method is a faster tree iteration compared 953 * to the normal recursive tree walking. 954 * 955 * @param rootNode the root node to be iterated over 956 * @param filter the iteration filter 957 * @param deepScan if set to true a deep scan is performed 958 */ 959 _iteratorSearchAll: function(rootNode, filter, deepScan) { 960 var retVal = []; 961 //Works on firefox and webkit, opera and ie have to use the slower fallback mechanis 962 //we have a tree walker in place this allows for an optimized deep scan 963 if (filter(rootNode)) { 964 965 retVal.push(rootNode); 966 if (!deepScan) { 967 return retVal; 968 } 969 } 970 //we use the reject mechanism to prevent a deep scan reject means any 971 //child elements will be omitted from the scan 972 var walkerFilter = function (node) { 973 var retCode = (filter(node)) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; 974 retCode = (!deepScan && retCode == NodeFilter.FILTER_ACCEPT) ? NodeFilter.FILTER_REJECT : retCode; 975 if (retCode == NodeFilter.FILTER_ACCEPT || retCode == NodeFilter.FILTER_REJECT) { 976 retVal.push(node); 977 } 978 return retCode; 979 }; 980 var treeWalker = document.createTreeWalker(rootNode, NodeFilter.SHOW_ELEMENT, walkerFilter, false); 981 //noinspection StatementWithEmptyBodyJS 982 while (treeWalker.nextNode()); 983 return retVal; 984 } 985 , 986 987 /** 988 * bugfixing for ie6 which does not cope properly with setAttribute 989 */ 990 setAttribute : function(node, attr, val) { 991 992 if (!node) { 993 throw Error(this._Lang.getMessage("ERR_MUST_BE_PROVIDED1",null, "_Dom.setAttribute", "node {DomNode}")); 994 } 995 if (!attr) { 996 throw Error(this._Lang.getMessage("ERR_MUST_BE_PROVIDED1",null, "_Dom.setAttribute", "attr {String}")); 997 } 998 999 //quirks mode and ie7 mode has the attributes problems ie8 standards mode behaves like 1000 //a good citizen 1001 var _Browser = this._RT.browser; 1002 //in case of ie > ie7 we have to check for a quirks mode setting 1003 if (!_Browser.isIE || _Browser.isIE > 7 && this.isDomCompliant()) { 1004 if (!node.setAttribute) { 1005 return; 1006 } 1007 node.setAttribute(attr, val); 1008 return; 1009 } 1010 1011 /* 1012 Now to the broken browsers IE6+.... ie7 and ie8 quirks mode 1013 1014 we deal mainly with three problems here 1015 class and for are not handled correctly 1016 styles are arrays and cannot be set directly 1017 and javascript events cannot be set via setAttribute as well! 1018 1019 or in original words of quirksmode.org ... this is a mess! 1020 1021 Btw. thank you Microsoft for providing all necessary tools for free 1022 for being able to debug this entire mess in the ie rendering engine out 1023 (which is the Microsoft ie vms, developers toolbar, Visual Web Developer 2008 express 1024 and the ie8 8 developers toolset!) 1025 1026 also thank you http://www.quirksmode.org/ 1027 dojotoolkit.org and //http://delete.me.uk/2004/09/ieproto.html 1028 for additional information on this mess! 1029 1030 The lowest common denominator tested within this code 1031 is IE6, older browsers for now are legacy! 1032 */ 1033 attr = attr.toLowerCase(); 1034 1035 if (attr === "class") { 1036 //setAttribute does not work for winmobile browsers 1037 //firect calls work 1038 node.className = val; 1039 } else if (attr === "name") { 1040 //the ie debugger fails to assign the name via setAttr 1041 //in quirks mode 1042 node[attr] = val; 1043 } else if (attr === "for") { 1044 if (!_Browser.isIEMobile || _Browser.isIEMobile >= 7) { 1045 node.setAttribute("htmlFor", val); 1046 } else { 1047 node.htmlFor = val; 1048 } 1049 } else if (attr === "style") { 1050 //We have to split the styles here and assign them one by one 1051 var styles = val.split(";"); 1052 var stylesLen = styles.length; 1053 for (var loop = 0; loop < stylesLen; loop++) { 1054 var keyVal = styles[loop].split(":"); 1055 if (keyVal[0] != "" && keyVal[0] == "opacity") { 1056 //special ie quirks handling for opacity 1057 1058 var opacityVal = Math.max(100, Math.round(parseFloat(keyVal[1]) * 10)); 1059 //probably does not work in ie mobile anyway 1060 if (!_Browser.isIEMobile || _Browser.isIEMobile >= 7) { 1061 node.style.setAttribute("arrFilter", "alpha(opacity=" + opacityVal + ")"); 1062 } 1063 //if you need more hacks I would recommend 1064 //to use the class attribute and conditional ie includes! 1065 } else if (keyVal[0] != "") { 1066 if (!_Browser.isIEMobile || _Browser.isIEMobile >= 7) { 1067 node.style.setAttribute(keyVal[0], keyVal[1]); 1068 } else { 1069 node.style[keyVal[0]] = keyVal[1]; 1070 } 1071 } 1072 } 1073 } else { 1074 //check if the attribute is an event, since this applies only 1075 //to quirks mode of ie anyway we can live with the standard html4/xhtml 1076 //ie supported events 1077 if (this.IE_QUIRKS_EVENTS[attr]) { 1078 if (this._Lang.isString(attr)) { 1079 //event resolves to window.event in ie 1080 node.setAttribute(attr, function() { 1081 //event implicitly used 1082 return this._Lang.globalEval(val); 1083 }); 1084 } 1085 } else { 1086 //unknown cases we try to catch them via standard setAttributes 1087 if (!_Browser.isIEMobile || _Browser.isIEMobile >= 7) { 1088 node.setAttribute(attr, val); 1089 } else { 1090 node[attr] = val; 1091 } 1092 } 1093 } 1094 } 1095 , 1096 1097 1098 /** 1099 * fuzzy form detection which tries to determine the form 1100 * an item has been detached. 1101 * 1102 * The problem is some Javascript libraries simply try to 1103 * detach controls by reusing the names 1104 * of the detached input controls. Most of the times, 1105 * the name is unique in a jsf scenario, due to the inherent form mapping. 1106 * One way or the other, we will try to fix that by 1107 * identifying the proper form over the name 1108 * 1109 * We do it in several ways, in case of no form null is returned 1110 * in case of multiple forms we check all elements with a given name (which we determine 1111 * out of a name or id of the detached element) and then iterate over them 1112 * to find whether they are in a form or not. 1113 * 1114 * If only one element within a form and a given identifier found then we can pull out 1115 * and move on 1116 * 1117 * We cannot do much further because in case of two identical named elements 1118 * all checks must fail and the first elements form is served. 1119 * 1120 * Note, this method is only triggered in case of the issuer or an ajax request 1121 * is a detached element, otherwise already existing code has served the correct form. 1122 * 1123 * This method was added because of 1124 * https://issues.apache.org/jira/browse/MYFACES-2599 1125 * to support the integration of existing ajax libraries which do heavy dom manipulation on the 1126 * controls side (Dojos Dijit library for instance). 1127 * 1128 * @param {Node} elem - element as source, can be detached, undefined or null 1129 * 1130 * @return either null or a form node if it could be determined 1131 */ 1132 fuzzyFormDetection : function(elem) { 1133 if (!document.forms || !document.forms.length) { 1134 return null; 1135 } 1136 1137 // This will not work well on portlet case, because we cannot be sure 1138 // the returned form is right one. 1139 //we can cover that case by simply adding one of our config params 1140 //the default is the weaker, but more correct portlet code 1141 //you can override it with myfaces_config.no_portlet_env = true globally 1142 else if (1 == document.forms.length && this._RT.getGlobalConfig("no_portlet_env", false)) { 1143 return document.forms[0]; 1144 } 1145 if (!elem) { 1146 return null; 1147 } 1148 1149 //before going into the more complicated stuff we try the simple approach 1150 if (!this._Lang.isString(elem)) { 1151 1152 //html 5 allows finally the detachement of elements 1153 //by introducing a form attribute 1154 1155 var elemForm = this.html5FormDetection(elem); 1156 if (elemForm) { 1157 return elemForm; 1158 } 1159 1160 //element of type form then we are already 1161 //at form level for the issuing element 1162 //https://issues.apache.org/jira/browse/MYFACES-2793 1163 1164 if (this._Lang.equalsIgnoreCase(elem.tagName, "form")) { 1165 return elem; 1166 } 1167 var ret = this.getParent(elem, "form"); 1168 if (ret) return ret; 1169 } else { 1170 elem = this.byId(elem); 1171 // element might have removed from DOM in method processUpdate 1172 if (!elem){ 1173 return null; 1174 } 1175 var ret = this.getParent(elem, "form"); 1176 if (ret) return ret; 1177 } 1178 1179 var id = elem.id || null; 1180 var name = elem.name || null; 1181 //a framework in a detachment case also can replace an existing identifier element 1182 // with a name element 1183 name = name || id; 1184 var foundForm; 1185 1186 if (id && '' != id) { 1187 //we have to assert that the element passed down is detached 1188 var domElement = this.byId(id); 1189 var elemForm = this.html5FormDetection(domElement); 1190 if (elemForm) { 1191 return elemForm; 1192 } 1193 1194 if (domElement) { 1195 foundForm = this.getParent(domElement, "form"); 1196 if (null != foundForm) return foundForm; 1197 } 1198 } 1199 1200 /** 1201 * name check 1202 */ 1203 var foundElements = []; 1204 1205 /** 1206 * the lesser chance is the elements which have the same name 1207 * (which is the more likely case in case of a brute dom replacement) 1208 */ 1209 var nameElems = document.getElementsByName(name); 1210 if (nameElems) { 1211 for (var cnt = 0; cnt < nameElems.length && foundElements.length < 2; cnt++) { 1212 // we already have covered the identifier case hence we only can deal with names, 1213 foundForm = this.getParent(nameElems[cnt], "form"); 1214 if (null != foundForm) { 1215 foundElements.push(foundForm); 1216 } 1217 } 1218 } 1219 1220 return (1 == foundElements.length ) ? foundElements[0] : null; 1221 } 1222 , 1223 1224 html5FormDetection: function(item) { 1225 if (this._RT.browser.isIEMobile && this._RT.browser.isIEMobile <= 7) { 1226 return null; 1227 } 1228 var elemForm = this.getAttribute(item, "form"); 1229 if (elemForm) { 1230 return this.byId(elemForm); 1231 } 1232 return null; 1233 } 1234 , 1235 1236 /** 1237 * gets a parent of an item with a given tagname 1238 * @param {Node} item - child element 1239 * @param {String} tagName - TagName of parent element 1240 */ 1241 getParent : function(item, tagName) { 1242 1243 if (!item) { 1244 throw Error(this._Lang.getMessage("ERR_MUST_BE_PROVIDED1",null, "_Dom.getParent", "item {DomNode}")); 1245 } 1246 1247 var _Lang = this._Lang; 1248 var searchClosure = function(parentItem) { 1249 return parentItem && parentItem.tagName 1250 && _Lang.equalsIgnoreCase(parentItem.tagName, tagName); 1251 }; 1252 try { 1253 return this.getFilteredParent(item, searchClosure); 1254 } finally { 1255 searchClosure = null; 1256 _Lang = null; 1257 } 1258 } 1259 , 1260 1261 /** 1262 * A parent walker which uses 1263 * a filter closure for filtering 1264 * 1265 * @param {Node} item the root item to ascend from 1266 * @param {function} filter the filter closure 1267 */ 1268 getFilteredParent : function(item, filter) { 1269 if (!item) { 1270 throw Error(this._Lang.getMessage("ERR_MUST_BE_PROVIDED1",null, "_Dom.getFilteredParent", "item {DomNode}")); 1271 } 1272 if (!filter) { 1273 throw Error(this._Lang.getMessage("ERR_MUST_BE_PROVIDED1",null, "_Dom.getFilteredParent", "filter {function}")); 1274 } 1275 1276 //search parent tag parentName 1277 var parentItem = (item.parentNode) ? item.parentNode : null; 1278 1279 while (parentItem && !filter(parentItem)) { 1280 parentItem = parentItem.parentNode; 1281 } 1282 return (parentItem) ? parentItem : null; 1283 } 1284 , 1285 1286 /** 1287 * a closure based child filtering routine 1288 * which steps one level down the tree and 1289 * applies the filter closure 1290 * 1291 * @param item the node which has to be investigates 1292 * @param filter the filter closure 1293 */ 1294 getFilteredChild: function(item, filter) { 1295 if (!item) { 1296 throw Error(this._Lang.getMessage("ERR_MUST_BE_PROVIDED1",null, "_Dom.getFilteredParent", "item {DomNode}")); 1297 } 1298 if (!filter) { 1299 throw Error(this._Lang.getMessage("ERR_MUST_BE_PROVIDED1",null, "_Dom.getFilteredParent", "filter {function}")); 1300 } 1301 1302 var childs = item.childNodes; 1303 if (!childs) { 1304 return null; 1305 } 1306 for (var c = 0, cLen = childs.length; c < cLen; c++) { 1307 if (filter(childs[c])) { 1308 return childs[c]; 1309 } 1310 } 1311 return null; 1312 } 1313 , 1314 1315 /** 1316 * gets the child of an item with a given tag name 1317 * @param {Node} item - parent element 1318 * @param {String} childName - TagName of child element 1319 * @param {String} itemName - name attribute the child can have (can be null) 1320 * @Deprecated 1321 */ 1322 getChild: function(item, childName, itemName) { 1323 var _Lang = this._Lang; 1324 1325 function filter(node) { 1326 return node.tagName 1327 && _Lang.equalsIgnoreCase(node.tagName, childName) 1328 && (!itemName || (itemName && itemName == node.getAttribute("name"))); 1329 1330 } 1331 1332 return this.getFilteredChild(item, filter); 1333 } 1334 , 1335 1336 /** 1337 * cross ported from dojo 1338 * fetches an attribute from a node 1339 * 1340 * @param {String} node the node 1341 * @param {String} attr the attribute 1342 * @return the attributes value or null 1343 */ 1344 getAttribute : function(/* HTMLElement */node, /* string */attr) { 1345 // summary 1346 // Returns the value of attribute attr from node. 1347 node = this.byId(node); 1348 // FIXME: need to add support for attr-specific accessors 1349 if ((!node) || (!node.getAttribute)) { 1350 // if(attr !== 'nwType'){ 1351 // alert("getAttr of '" + attr + "' with bad node"); 1352 // } 1353 return null; 1354 } 1355 var ta = typeof attr == 'string' ? attr : new String(attr); 1356 1357 // first try the approach most likely to succeed 1358 var v = node.getAttribute(ta.toUpperCase()); 1359 if ((v) && (typeof v == 'string') && (v != "")) { 1360 return v; // string 1361 } 1362 1363 // try returning the attributes value, if we couldn't get it as a string 1364 if (v && v.value) { 1365 return v.value; // string 1366 } 1367 1368 // this should work on Opera 7, but it's a little on the crashy side 1369 if ((node.getAttributeNode) && (node.getAttributeNode(ta))) { 1370 return (node.getAttributeNode(ta)).value; // string 1371 } else if (node.getAttribute(ta)) { 1372 return node.getAttribute(ta); // string 1373 } else if (node.getAttribute(ta.toLowerCase())) { 1374 return node.getAttribute(ta.toLowerCase()); // string 1375 } 1376 return null; // string 1377 } 1378 , 1379 1380 /** 1381 * checks whether the given node has an attribute attached 1382 * 1383 * @param {String|Object} node the node to search for 1384 * @param {String} attr the attribute to search for 1385 * @true if the attribute was found 1386 */ 1387 hasAttribute : function(/* HTMLElement */node, /* string */attr) { 1388 // summary 1389 // Determines whether or not the specified node carries a value for the attribute in question. 1390 return this.getAttribute(node, attr) ? true : false; // boolean 1391 } 1392 , 1393 1394 /** 1395 * fetches the style class for the node 1396 * cross ported from the dojo toolkit 1397 * @param {String|Object} node the node to search 1398 * @returns the className or "" 1399 */ 1400 getClass : function(node) { 1401 node = this.byId(node); 1402 if (!node) { 1403 return ""; 1404 } 1405 var cs = ""; 1406 if (node.className) { 1407 cs = node.className; 1408 } else { 1409 if (this.hasAttribute(node, "class")) { 1410 cs = this.getAttribute(node, "class"); 1411 } 1412 } 1413 return cs.replace(/^\s+|\s+$/g, ""); 1414 } 1415 , 1416 /** 1417 * fetches the class for the node, 1418 * cross ported from the dojo toolkit 1419 * @param {String|Object}node the node to search 1420 */ 1421 getClasses : function(node) { 1422 var c = this.getClass(node); 1423 return (c == "") ? [] : c.split(/\s+/g); 1424 } 1425 , 1426 1427 /** 1428 * concatenation routine which concats all childnodes of a node which 1429 * contains a set of CDATA blocks to one big string 1430 * @param {Node} node the node to concat its blocks for 1431 */ 1432 concatCDATABlocks : function(/*Node*/ node) { 1433 var cDataBlock = []; 1434 // response may contain several blocks 1435 for (var i = 0; i < node.childNodes.length; i++) { 1436 cDataBlock.push(node.childNodes[i].data); 1437 } 1438 return cDataBlock.join(''); 1439 } 1440 , 1441 1442 isManualScriptEval: function() { 1443 1444 if (!this._Lang.exists(myfaces, "config._autoeval")) { 1445 var _Browser = this._RT.browser; 1446 //now we rely on the document being processed if called for the first time 1447 var evalDiv = document.createElement("div"); 1448 this._Lang.reserveNamespace("myfaces.config._autoeval"); 1449 //null not swallowed 1450 myfaces.config._autoeval = false; 1451 1452 var markup = "<script type='text/javascript'> myfaces.config._autoeval = true; </script>"; 1453 //now we rely on the same replacement mechanisms as outerhtml because 1454 //some browsers have different behavior of embedded scripts in the contextualfragment 1455 //or innerhtml case (opera for instance), this way we make sure the 1456 //eval detection is covered correctly 1457 this.setAttribute(evalDiv, "style", "display:none"); 1458 1459 //it is less critical in some browsers (old ie versions) 1460 //to append as first element than as last 1461 //it should not make any difference layoutwise since we are on display none anyway. 1462 this.insertFirst(evalDiv); 1463 1464 //we remap it into a real boolean value 1465 if (window.Range 1466 && typeof Range.prototype.createContextualFragment == 'function') { 1467 this._outerHTMLCompliant(evalDiv, markup); 1468 } else { 1469 this._outerHTMLNonCompliant(evalDiv, markup); 1470 } 1471 1472 } 1473 1474 return !myfaces.config._autoeval; 1475 /* var d = _this.browser; 1476 1477 1478 return (_this.exists(d, "isIE") && 1479 ( d.isIE > 5.5)) || 1480 //firefox at version 4 beginning has dropped 1481 //auto eval to be compliant with the rest 1482 (_this.exists(d, "isFF") && 1483 (d.isFF > 3.9)) || 1484 (_this.exists(d, "isKhtml") && 1485 (d.isKhtml > 0)) || 1486 (_this.exists(d, "isWebKit") && 1487 (d.isWebKit > 0)) || 1488 (_this.exists(d, "isSafari") && 1489 (d.isSafari > 0)); 1490 */ 1491 //another way to determine this without direct user agent parsing probably could 1492 //be to add an embedded script tag programmatically and check for the script variable 1493 //set by the script if existing, the add went through an eval if not then we 1494 //have to deal with it ourselves, this might be dangerous in case of the ie however 1495 //so in case of ie we have to parse for all other browsers we can make a dynamic 1496 //check if the browser does auto eval 1497 1498 } 1499 , 1500 1501 isMultipartCandidate: function(executes) { 1502 if (this._Lang.isString(executes)) { 1503 executes = this._Lang.strToArray(executes, /\s+/); 1504 } 1505 1506 for (var exec in executes) { 1507 var element = this.byId(executes[exec]); 1508 var inputs = this.findByTagName(element, "input", true); 1509 for (var key in inputs) { 1510 if (this.getAttribute(inputs[key], "type") == "file") return true; 1511 } 1512 } 1513 return false; 1514 } 1515 , 1516 1517 1518 insertFirst: function(newNode) { 1519 var body = document.body; 1520 if (body.childNodes.length > 0) { 1521 body.insertBefore(newNode, body.firstChild); 1522 } else { 1523 body.appendChild(newNode); 1524 } 1525 } 1526 , 1527 1528 byId: function(id) { 1529 return this._Lang.byId(id); 1530 } 1531 , 1532 1533 getDummyPlaceHolder: function(markup) { 1534 var created = false; 1535 if (!this._dummyPlaceHolder) { 1536 this._dummyPlaceHolder = document.createElement("div"); 1537 created = true; 1538 } 1539 1540 //ieMobile in its 6.1-- incarnation cannot handle innerHTML detached objects so we have 1541 //to attach the dummy placeholder, we try to avoid it for 1542 //better browsers so that we do not have unecessary dom operations 1543 if (this._RT.browser.isIEMobile && created) { 1544 this.insertFirst(this._dummyPlaceHolder); 1545 1546 this.setAttribute(this._dummyPlaceHolder, "style", "display: none"); 1547 1548 } 1549 1550 return this._dummyPlaceHolder; 1551 }, 1552 1553 /** 1554 * fetches the window id for the current request 1555 * note, this is a preparation method for jsf 2.2 1556 */ 1557 getWindowId: function() { 1558 var href = window.location.href; 1559 var windowId = "windowId"; 1560 var regex = new RegExp("[\\?&]" + windowId + "=([^\\;]*)"); 1561 var results = regex.exec(href); 1562 return (results != null) ? results[1] : null; 1563 } 1564 1565 }); 1566 1567 1568