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 increase up our codebase significantly
 31  *
 32  * <p>This class provides the proper fallbacks for ie8- and Firefox 3.6-</p>
 33  */
 34 _MF_SINGLTN(_PFX_UTIL + "_Dom", Object, /** @lends myfaces._impl._util._Dom.prototype */ {
 35 
 36     /*table elements which are used in various parts */
 37     TABLE_ELEMS:  {
 38         "thead": 1,
 39         "tbody": 1,
 40         "tr": 1,
 41         "th": 1,
 42         "td": 1,
 43         "tfoot" : 1
 44     },
 45 
 46     _Lang:  myfaces._impl._util._Lang,
 47     _RT:    myfaces._impl.core._Runtime,
 48     _dummyPlaceHolder:null,
 49 
 50     /**
 51      * standard constructor
 52      */
 53     constructor_: function() {
 54     },
 55 
 56     /**
 57      * Run through the given Html item and execute the inline scripts
 58      * (IE doesn't do this by itself)
 59      * @param {Node} item
 60      */
 61     runScripts: function(item, xmlData) {
 62         var _T = this;
 63         var finalScripts = [];
 64         var _RT = this._RT;
 65 
 66         var evalCollectedScripts = function (scriptsToProcess) {
 67             if (scriptsToProcess && scriptsToProcess.length) {
 68                 //script source means we have to eval the existing
 69                 //scripts before running the include
 70                 var joinedScripts = [];
 71                 for(var scrptCnt = 0; scrptCnt < scriptsToProcess.length; scrptCnt++) {
 72                     var item = scriptsToProcess[scrptCnt];
 73                     if (!item.cspMeta) {
 74                         joinedScripts.push(item.text)
 75                     } else {
 76                         if (joinedScripts.length) {
 77                             _RT.globalEval(joinedScripts.join("\n"));
 78                             joinedScripts.length = 0;
 79                         }
 80                         _RT.globalEval(item.text, item.cspMeta);
 81                     }
 82                 }
 83 
 84                 if (joinedScripts.length) {
 85                     _RT.globalEval(joinedScripts.join("\n"));
 86                     joinedScripts.length = 0;
 87                 }
 88             }
 89             return [];
 90         }
 91 
 92 
 93         var _Lang = this._Lang,
 94             execScrpt = function(item) {
 95                 var tagName = item.tagName;
 96                 var type = item.type || "";
 97                 //script type javascript has to be handled by eval, other types
 98                 //must be handled by the browser
 99                 if (tagName && _Lang.equalsIgnoreCase(tagName, "script") &&
100                     (type === "" ||
101                         _Lang.equalsIgnoreCase(type,"text/javascript") ||
102                         _Lang.equalsIgnoreCase(type,"javascript") ||
103                         _Lang.equalsIgnoreCase(type,"text/ecmascript") ||
104                         _Lang.equalsIgnoreCase(type,"ecmascript"))) {
105 
106                     //now given that scripts can embed nonce
107                     //we cannoit
108                     var nonce = _RT.resolveNonce(item);
109 
110                     var src = item.getAttribute('src');
111                     if ('undefined' != typeof src
112                         && null != src
113                         && src.length > 0
114                     ) {
115                         //we have to move this into an inner if because chrome otherwise chokes
116                         //due to changing the and order instead of relying on left to right
117                         //if jsf.js is already registered we do not replace it anymore
118                         if ((src.indexOf("ln=scripts") == -1 && src.indexOf("ln=javax.faces") == -1) ||
119                             (src.indexOf("/jsf.js") == -1
120                             && (src.indexOf("/jsf-uncompressed.js") == -1)
121                             && (src.indexOf("/jsf-development.js") == -1)
122                             )) {
123                             finalScripts = evalCollectedScripts(finalScripts);
124                             _RT.loadScriptEval(src, item.getAttribute('type'), false, "UTF-8", false, nonce ? {nonce: nonce} : null );
125                         }
126 
127                     } else {
128                         // embedded script auto eval
129                         var test = (!xmlData) ? item.text : _Lang.serializeChilds(item);
130                         var go = true;
131                         while (go) {
132                             go = false;
133                             if (test.substring(0, 1) == " ") {
134                                 test = test.substring(1);
135                                 go = true;
136                             }
137                             if (test.substring(0, 4) == "<!--") {
138                                 test = test.substring(4);
139                                 go = true;
140                             }
141                             if (test.substring(0, 11) == "//<![CDATA[") {
142                                 test = test.substring(11);
143                                 go = true;
144                             }
145                         }
146                         // we have to run the script under a global context
147                         //we store the script for less calls to eval
148                         finalScripts.push(nonce ? {
149                             cspMeta: {nonce: nonce},
150                             text: test
151                         }: {
152                             text: test
153                         });
154                     }
155                 }
156             };
157         try {
158             var scriptElements = this.findByTagName(item, "script", true);
159             if (scriptElements == null) return;
160             for (var cnt = 0; cnt < scriptElements.length; cnt++) {
161                 execScrpt(scriptElements[cnt]);
162             }
163             evalCollectedScripts(finalScripts);
164         } catch (e) {
165             //we are now in accordance with the rest of the system of showing errors only in development mode
166             //the default error output is alert we always can override it with
167             //window.myfaces = window.myfaces || {};
168             //myfaces.config =  myfaces.config || {};
169             //myfaces.config.defaultErrorOutput = console.error;
170             if(jsf.getProjectStage() === "Development") {
171                 var defaultErrorOutput = myfaces._impl.core._Runtime.getGlobalConfig("defaultErrorOutput", alert);
172                 defaultErrorOutput("Error in evaluated javascript:"+ (e.message || e.description || e));
173             }
174         } finally {
175             //the usual ie6 fix code
176             //the IE6 garbage collector is broken
177             //nulling closures helps somewhat to reduce
178             //mem leaks, which are impossible to avoid
179             //at this browser
180             execScrpt = null;
181         }
182     },
183 
184 
185     /**
186      * determines to fetch a node
187      * from its id or name, the name case
188      * only works if the element is unique in its name
189      * @param {String} elem
190      */
191     byIdOrName: function(elem) {
192         if (!elem) return null;
193         if (!this._Lang.isString(elem)) return elem;
194 
195         var ret = this.byId(elem);
196         if (ret) return ret;
197         //we try the unique name fallback
198         var items = document.getElementsByName(elem);
199         return ((items.length == 1) ? items[0] : null);
200     },
201 
202     /**
203      * node id or name, determines the valid form identifier of a node
204      * depending on its uniqueness
205      *
206      * Usually the id is chosen for an elem, but if the id does not
207      * exist we try a name fallback. If the passed element has a unique
208      * name we can use that one as subsequent identifier.
209      *
210      *
211      * @param {String} elem
212      */
213     nodeIdOrName: function(elem) {
214         if (elem) {
215             //just to make sure that the pas
216 
217             elem = this.byId(elem);
218             if (!elem) return null;
219             //detached element handling, we also store the element name
220             //to get a fallback option in case the identifier is not determinable
221             // anymore, in case of a framework induced detachment the element.name should
222             // be shared if the identifier is not determinable anymore
223             //the downside of this method is the element name must be unique
224             //which in case of jsf it is
225             var elementId = elem.id || elem.name;
226             if ((elem.id == null || elem.id == '') && elem.name) {
227                 elementId = elem.name;
228 
229                 //last check for uniqueness
230                 if (document.getElementsByName(elementId).length > 1) {
231                     //no unique element name so we need to perform
232                     //a return null to let the caller deal with this issue
233                     return null;
234                 }
235             }
236             return elementId;
237         }
238         return null;
239     },
240 
241     deleteItems: function(items) {
242         if (! items || ! items.length) return;
243         for (var cnt = 0; cnt < items.length; cnt++) {
244             this.deleteItem(items[cnt]);
245         }
246     },
247 
248     /**
249      * Simple delete on an existing item
250      */
251     deleteItem: function(itemIdToReplace) {
252         var item = this.byId(itemIdToReplace);
253         if (!item) {
254             throw this._Lang.makeException(new Error(),null, null, this._nameSpace, "deleteItem",  "_Dom.deleteItem  Unknown Html-Component-ID: " + itemIdToReplace);
255         }
256 
257         this._removeNode(item, false);
258     },
259 
260     /**
261      * creates a node upon a given node name
262      * @param nodeName {String} the node name to be created
263      * @param attrs {Array} a set of attributes to be set
264      */
265     createElement: function(nodeName, attrs) {
266         var ret = document.createElement(nodeName);
267         if (attrs) {
268             for (var key in attrs) {
269                 if(!attrs.hasOwnProperty(key)) continue;
270                 this.setAttribute(ret, key, attrs[key]);
271             }
272         }
273         return ret;
274     },
275 
276     /**
277      * Checks whether the browser is dom compliant.
278      * Dom compliant means that it performs the basic dom operations safely
279      * without leaking and also is able to perform a native setAttribute
280      * operation without freaking out
281      *
282      *
283      * Not dom compliant browsers are all microsoft browsers in quirks mode
284      * and ie6 and ie7 to some degree in standards mode
285      * and pretty much every browser who cannot create ranges
286      * (older mobile browsers etc...)
287      *
288      * We dont do a full browser detection here because it probably is safer
289      * to test for existing features to make an assumption about the
290      * browsers capabilities
291      */
292     isDomCompliant: function() {
293         return true;
294     },
295 
296     /**
297      * proper insert before which takes tables into consideration as well as
298      * browser deficiencies
299      * @param item the node to insert before
300      * @param markup the markup to be inserted
301      */
302     insertBefore: function(item, markup) {
303         this._assertStdParams(item, markup, "insertBefore");
304 
305         markup = this._Lang.trim(markup);
306         if (markup === "") return null;
307 
308         var evalNodes = this._buildEvalNodes(item, markup),
309             currentRef = item,
310             parentNode = item.parentNode,
311             ret = [];
312         for (var cnt = evalNodes.length - 1; cnt >= 0; cnt--) {
313             currentRef = parentNode.insertBefore(evalNodes[cnt], currentRef);
314             ret.push(currentRef);
315         }
316         ret = ret.reverse();
317         this._eval(ret);
318         return ret;
319     },
320 
321     /**
322      * proper insert before which takes tables into consideration as well as
323      * browser deficiencies
324      * @param item the node to insert before
325      * @param markup the markup to be inserted
326      */
327     insertAfter: function(item, markup) {
328         this._assertStdParams(item, markup, "insertAfter");
329         markup = this._Lang.trim(markup);
330         if (markup === "") return null;
331 
332         var evalNodes = this._buildEvalNodes(item, markup),
333             currentRef = item,
334             parentNode = item.parentNode,
335             ret = [];
336 
337         for (var cnt = 0; cnt < evalNodes.length; cnt++) {
338             if (currentRef.nextSibling) {
339                 //Winmobile 6 has problems with this strategy, but it is not really fixable
340                 currentRef = parentNode.insertBefore(evalNodes[cnt], currentRef.nextSibling);
341             } else {
342                 currentRef = parentNode.appendChild(evalNodes[cnt]);
343             }
344             ret.push(currentRef);
345         }
346         this._eval(ret);
347         return ret;
348     },
349 
350     propertyToAttribute: function(name) {
351         if (name === 'className') {
352             return 'class';
353         } else if (name === 'xmllang') {
354             return 'xml:lang';
355         } else {
356             return name.toLowerCase();
357         }
358     },
359 
360     isFunctionNative: function(func) {
361         return /^\s*function[^{]+{\s*\[native code\]\s*}\s*$/.test(String(func));
362     },
363 
364     detectAttributes: function(element) {
365         //test if 'hasAttribute' method is present and its native code is intact
366         //for example, Prototype can add its own implementation if missing
367         //JSF 2.4 we now can reduce the complexity here, one of the functions now
368         //is definitely implemented
369         if (element.hasAttribute && this.isFunctionNative(element.hasAttribute)) {
370             return function(name) {
371                 return element.hasAttribute(name);
372             }
373         } else {
374             return function (name) {
375                 return !!element.getAttribute(name);
376             }
377         }
378     },
379 
380     /**
381      * copy all attributes from one element to another - except id
382      * @param target element to copy attributes to
383      * @param source element to copy attributes from
384      * @ignore
385      */
386     cloneAttributes: function(target, source) {
387 
388         // enumerate core element attributes - without 'dir' as special case
389         var coreElementProperties = ['className', 'title', 'lang', 'xmllang', "href", "rel", "src"];
390         // enumerate additional input element attributes
391         var inputElementProperties = [
392             'name', 'value', 'size', 'maxLength', 'src', 'alt', 'useMap', 'tabIndex', 'accessKey', 'accept', 'type'
393         ];
394         // enumerate additional boolean input attributes
395         var inputElementBooleanProperties = [
396             'checked', 'disabled', 'readOnly'
397         ];
398 
399         // Enumerate all the names of the event listeners
400         var listenerNames =
401             [ 'onclick', 'ondblclick', 'onmousedown', 'onmousemove', 'onmouseout',
402                 'onmouseover', 'onmouseup', 'onkeydown', 'onkeypress', 'onkeyup',
403                 'onhelp', 'onblur', 'onfocus', 'onchange', 'onload', 'onunload', 'onabort',
404                 'onreset', 'onselect', 'onsubmit'
405             ];
406 
407         var sourceAttributeDetector = this.detectAttributes(source);
408         var targetAttributeDetector = this.detectAttributes(target);
409 
410         var isInputElement = target.nodeName.toLowerCase() === 'input';
411         var propertyNames = isInputElement ? coreElementProperties.concat(inputElementProperties) : coreElementProperties;
412         var isXML = !source.ownerDocument.contentType || source.ownerDocument.contentType == 'text/xml';
413         for (var iIndex = 0, iLength = propertyNames.length; iIndex < iLength; iIndex++) {
414             var propertyName = propertyNames[iIndex];
415             var attributeName = this.propertyToAttribute(propertyName);
416             if (sourceAttributeDetector(attributeName)) {
417 
418                 //With IE 7 (quirks or standard mode) and IE 8/9 (quirks mode only),
419                 //you cannot get the attribute using 'class'. You must use 'className'
420                 //which is the same value you use to get the indexed property. The only
421                 //reliable way to detect this (without trying to evaluate the browser
422                 //mode and version) is to compare the two return values using 'className'
423                 //to see if they exactly the same.  If they are, then use the property
424                 //name when using getAttribute.
425                 if( attributeName == 'class'){
426                     if( this._RT.browser.isIE && (source.getAttribute(propertyName) === source[propertyName]) ){
427                         attributeName = propertyName;
428                     }
429                 }
430 
431                 var newValue = isXML ? source.getAttribute(attributeName) : source[propertyName];
432                 var oldValue = target[propertyName];
433                 if (oldValue != newValue) {
434                     target[propertyName] = newValue;
435                 }
436             } else {
437                 target.removeAttribute(attributeName);
438                 if (attributeName == "value") {
439                     target[propertyName] = '';
440                 }
441             }
442         }
443 
444         var booleanPropertyNames = isInputElement ? inputElementBooleanProperties : [];
445         for (var jIndex = 0, jLength = booleanPropertyNames.length; jIndex < jLength; jIndex++) {
446             var booleanPropertyName = booleanPropertyNames[jIndex];
447             var newBooleanValue = source[booleanPropertyName];
448             var oldBooleanValue = target[booleanPropertyName];
449             if (oldBooleanValue != newBooleanValue) {
450                 target[booleanPropertyName] = newBooleanValue;
451             }
452         }
453 
454         //'style' attribute special case
455         if (sourceAttributeDetector('style')) {
456             var newStyle;
457             var oldStyle;
458             if (this._RT.browser.isIE) {
459                 newStyle = source.style.cssText;
460                 oldStyle = target.style.cssText;
461                 if (newStyle != oldStyle) {
462                     target.style.cssText = newStyle;
463                 }
464             } else {
465                 newStyle = source.getAttribute('style');
466                 oldStyle = target.getAttribute('style');
467                 if (newStyle != oldStyle) {
468                     target.setAttribute('style', newStyle);
469                 }
470             }
471         } else if (targetAttributeDetector('style')){
472             target.removeAttribute('style');
473         }
474 
475         // Special case for 'dir' attribute
476         if (!this._RT.browser.isIE && source.dir != target.dir) {
477             if (sourceAttributeDetector('dir')) {
478                 target.dir = source.dir;
479             } else if (targetAttributeDetector('dir')) {
480                 target.dir = '';
481             }
482         }
483 
484         for (var lIndex = 0, lLength = listenerNames.length; lIndex < lLength; lIndex++) {
485             var name = listenerNames[lIndex];
486             target[name] = source[name] ? source[name] : null;
487             if (source[name]) {
488                 source[name] = null;
489             }
490         }
491 
492         //clone HTML5 data-* attributes
493         try{
494             var targetDataset = target.dataset;
495             var sourceDataset = source.dataset;
496             if (targetDataset || sourceDataset) {
497                 //cleanup the dataset
498                 for (var tp in targetDataset) {
499                     delete targetDataset[tp];
500                 }
501                 //copy dataset's properties
502                 for (var sp in sourceDataset) {
503                     targetDataset[sp] = sourceDataset[sp];
504                 }
505             }
506         } catch (ex) {
507             //most probably dataset properties are not supported
508         }
509 
510         // still works in ie6
511         var attrs = source.hasAttributes() ? source.attributes: [];
512         var dataAttributes = this._Lang.arrFilter(attrs, function(attr) {
513             return attr.name && attr.name.indexOf("data-") == 0;
514         });
515         this._Lang.arrForEach(dataAttributes, function(name) {
516            if(target.setAttribute) {
517                var attrValue = source.getAttribute(name)  || source[name];
518                target.setAttribute(name, attrValue)
519            } else {
520                target[name] = attrValue;
521            }
522         });
523 
524         //special nonce handling
525         var nonce = this._RT.resolveNonce(source);
526         if(!!nonce) {
527             target["nonce"] = nonce;
528         }
529     },
530     //from
531     // http://blog.vishalon.net/index.php/javascript-getting-and-setting-caret-position-in-textarea/
532     getCaretPosition:function (ctrl) {
533         var caretPos = 0;
534 
535         try {
536 
537             // other browsers make it simpler by simply having a selection start element
538             if (ctrl.selectionStart || ctrl.selectionStart == '0')
539                 caretPos = ctrl.selectionStart;
540             // ie 5 quirks mode as second option because
541             // this option is flakey in conjunction with text areas
542             // TODO move this into the quirks class
543             else if (document.selection) {
544                 ctrl.focus();
545                 var selection = document.selection.createRange();
546                 //the selection now is start zero
547                 selection.moveStart('character', -ctrl.value.length);
548                 //the caretposition is the selection start
549                 caretPos = selection.text.length;
550             }
551         } catch (e) {
552             //now this is ugly, but not supported input types throw errors for selectionStart
553             //this way we are future proof by having not to define every selection enabled
554             //input in an if (which will be a lot in the near future with html5)
555         }
556         return caretPos;
557     },
558 
559     setCaretPosition:function (ctrl, pos) {
560 
561         if (ctrl.createTextRange) {
562             var range = ctrl.createTextRange();
563             range.collapse(true);
564             range.moveEnd('character', pos);
565             range.moveStart('character', pos);
566             range.select();
567         }
568         //IE quirks mode again, TODO move this into the quirks class
569         else if (ctrl.setSelectionRange) {
570             ctrl.focus();
571             //the selection range is our caret position
572             ctrl.setSelectionRange(pos, pos);
573         }
574     },
575 
576     /**
577      * outerHTML replacement which works cross browserlike
578      * but still is speed optimized
579      *
580      * @param item the item to be replaced
581      * @param markup the markup for the replacement
582      * @param preserveFocus, tries to preserve the focus within the outerhtml operation
583      * if set to true a focus preservation algorithm based on document.activeElement is
584      * used to preserve the focus at the exactly same location as it was
585      *
586      */
587     outerHTML : function(item, markup, preserveFocus) {
588         this._assertStdParams(item, markup, "outerHTML");
589         // we can work on a single element in a cross browser fashion
590         // regarding the focus thanks to the
591         // icefaces team for providing the code
592         if (item.nodeName.toLowerCase() === 'input') {
593             var replacingInput = this._buildEvalNodes(item, markup)[0];
594             this.cloneAttributes(item, replacingInput);
595             return item;
596         } else {
597             markup = this._Lang.trim(markup);
598             if (markup !== "") {
599                 var ret = null;
600 
601                 var focusElementId = null;
602                 var caretPosition = 0;
603                 if (preserveFocus && 'undefined' != typeof document.activeElement) {
604                     focusElementId = (document.activeElement) ? document.activeElement.id : null;
605                     caretPosition = this.getCaretPosition(document.activeElement);
606                 }
607                 // we try to determine the browsers compatibility
608                 // level to standards dom level 2 via various methods
609                 if (this.isDomCompliant()) {
610                     ret = this._outerHTMLCompliant(item, markup);
611                 } else {
612                     //call into abstract method
613                     ret = this._outerHTMLNonCompliant(item, markup);
614                 }
615                 if (focusElementId) {
616                     var newFocusElement = this.byId(focusElementId);
617                     if (newFocusElement && newFocusElement.nodeName.toLowerCase() === 'input') {
618                         //just in case the replacement element is not focusable anymore
619                         if ("undefined" != typeof newFocusElement.focus) {
620                             newFocusElement.focus();
621                         }
622                     }
623                     if (newFocusElement && caretPosition) {
624                         //zero caret position is set automatically on focus
625                         this.setCaretPosition(newFocusElement, caretPosition);
626                     }
627                 }
628 
629                 // and remove the old item
630                 //first we have to save the node newly insert for easier access in our eval part
631                 this._eval(ret);
632                 return ret;
633             }
634             // and remove the old item, in case of an empty newtag and do nothing else
635             this._removeNode(item, false);
636             return null;
637         }
638     },
639 
640     /**
641      * detaches a set of nodes from their parent elements
642      * in a browser independend manner
643      * @param {Object} items the items which need to be detached
644      * @return {Array} an array of nodes with the detached dom nodes
645      */
646     detach: function(items) {
647         var ret = [];
648         if ('undefined' != typeof items.nodeType) {
649             if (items.parentNode) {
650                 ret.push(items.parentNode.removeChild(items));
651             } else {
652                 ret.push(items);
653             }
654             return ret;
655         }
656         //all ies treat node lists not as arrays so we have to take
657         //an intermediate step
658         var nodeArr = this._Lang.objToArray(items);
659         for (var cnt = 0; cnt < nodeArr.length; cnt++) {
660             ret.push(nodeArr[cnt].parentNode.removeChild(nodeArr[cnt]));
661         }
662         return ret;
663     },
664 
665     _outerHTMLCompliant: function(item, markup) {
666         //table element replacements like thead, tbody etc... have to be treated differently
667         var evalNodes = this._buildEvalNodes(item, markup);
668 
669         if (evalNodes.length == 1) {
670             var ret = evalNodes[0];
671             item.parentNode.replaceChild(ret, item);
672             return ret;
673         } else {
674             return this.replaceElements(item, evalNodes);
675         }
676     },
677 
678     /**
679      * checks if the provided element is a subelement of a table element
680      * @param item
681      */
682     _isTableElement: function(item) {
683         return !!this.TABLE_ELEMS[(item.nodeName || item.tagName).toLowerCase()];
684     },
685 
686     /**
687      * non ie browsers do not have problems with embedded scripts or any other construct
688      * we simply can use an innerHTML in a placeholder
689      *
690      * @param markup the markup to be used
691      */
692     _buildNodesCompliant: function(markup) {
693         var dummyPlaceHolder = this.getDummyPlaceHolder(); //document.createElement("div");
694         dummyPlaceHolder.innerHTML = markup;
695         return this._Lang.objToArray(dummyPlaceHolder.childNodes);
696     },
697 
698 
699 
700 
701     /**
702      * builds up a correct dom subtree
703      * if the markup is part of table nodes
704      * The usecase for this is to allow subtable rendering
705      * like single rows thead or tbody
706      *
707      * @param item
708      * @param markup
709      */
710     _buildTableNodes: function(item, markup) {
711         var itemNodeName = (item.nodeName || item.tagName).toLowerCase();
712 
713         var tmpNodeName = itemNodeName;
714         var depth = 0;
715         while (tmpNodeName != "table") {
716             item = item.parentNode;
717             tmpNodeName = (item.nodeName || item.tagName).toLowerCase();
718             depth++;
719         }
720 
721         var dummyPlaceHolder = this.getDummyPlaceHolder();
722         if (itemNodeName == "td") {
723             dummyPlaceHolder.innerHTML = "<table><tbody><tr>" + markup + "</tr></tbody></table>";
724         } else {
725             dummyPlaceHolder.innerHTML = "<table>" + markup + "</table>";
726         }
727 
728         for (var cnt = 0; cnt < depth; cnt++) {
729             dummyPlaceHolder = dummyPlaceHolder.childNodes[0];
730         }
731 
732         return this.detach(dummyPlaceHolder.childNodes);
733     },
734 
735     _removeChildNodes: function(node /*, breakEventsOpen */) {
736         if (!node) return;
737         node.innerHTML = "";
738     },
739 
740 
741 
742     _removeNode: function(node /*, breakEventsOpen*/) {
743         if (!node) return;
744         var parentNode = node.parentNode;
745         if (parentNode) //if the node has a parent
746             parentNode.removeChild(node);
747     },
748 
749 
750     /**
751      * build up the nodes from html markup in a browser independend way
752      * so that it also works with table nodes
753      *
754      * @param item the parent item upon the nodes need to be processed upon after building
755      * @param markup the markup to be built up
756      */
757     _buildEvalNodes: function(item, markup) {
758         var evalNodes = null;
759         if (item && this._isTableElement(item)) {
760             evalNodes = this._buildTableNodes(item, markup);
761         } else {
762             var nonIEQuirks = (!this._RT.browser.isIE || this._RT.browser.isIE > 8);
763             //ie8 has a special problem it still has the swallow scripts and other
764             //elements bug, but it is mostly dom compliant so we have to give it a special
765             //treatment, IE9 finally fixes that issue finally after 10 years
766             evalNodes = (this.isDomCompliant() &&  nonIEQuirks) ?
767                 this._buildNodesCompliant(markup) :
768                 //ie8 or quirks mode browsers
769                 this._buildNodesNonCompliant(markup);
770         }
771         return evalNodes;
772     },
773 
774     /**
775      * we have lots of methods with just an item and a markup as params
776      * this method builds an assertion for those methods to reduce code
777      *
778      * @param item  the item to be tested
779      * @param markup the markup
780      * @param caller caller function
781      * @param {optional} params array of assertion param names
782      */
783     _assertStdParams: function(item, markup, caller, params) {
784         //internal error
785         if (!caller) {
786             throw this._Lang.makeException(new Error(), null, null, this._nameSpace, "_assertStdParams",  "Caller must be set for assertion");
787         }
788         var _Lang = this._Lang,
789             ERR_PROV = "ERR_MUST_BE_PROVIDED1",
790             DOM = "myfaces._impl._util._Dom.",
791             finalParams = params || ["item", "markup"];
792 
793         if (!item || !markup) {
794             _Lang.makeException(new Error(), null, null,DOM, ""+caller,  _Lang.getMessage(ERR_PROV, null, DOM +"."+ caller, (!item) ? finalParams[0] : finalParams[1]));
795             //throw Error(_Lang.getMessage(ERR_PROV, null, DOM + caller, (!item) ? params[0] : params[1]));
796         }
797     },
798 
799     /**
800      * internal eval handler used by various functions
801      * @param _nodeArr
802      */
803     _eval: function(_nodeArr) {
804         if (this.isManualScriptEval()) {
805             var isArr = _nodeArr instanceof Array;
806             if (isArr && _nodeArr.length) {
807                 for (var cnt = 0; cnt < _nodeArr.length; cnt++) {
808                     this.runScripts(_nodeArr[cnt]);
809                 }
810             } else if (!isArr) {
811                 this.runScripts(_nodeArr);
812             }
813         }
814     },
815 
816     /**
817      * for performance reasons we work with replaceElement and replaceElements here
818      * after measuring performance it has shown that passing down an array instead
819      * of a single node makes replaceElement twice as slow, however
820      * a single node case is the 95% case
821      *
822      * @param item
823      * @param evalNode
824      */
825     replaceElement: function(item, evalNode) {
826         //browsers with defect garbage collection
827         item.parentNode.insertBefore(evalNode, item);
828         this._removeNode(item, false);
829     },
830 
831 
832     /**
833      * replaces an element with another element or a set of elements
834      *
835      * @param item the item to be replaced
836      *
837      * @param evalNodes the elements
838      */
839     replaceElements: function (item, evalNodes) {
840         var evalNodesDefined = evalNodes && 'undefined' != typeof evalNodes.length;
841         if (!evalNodesDefined) {
842             throw this._Lang.makeException(new Error(), null, null, this._nameSpace, "replaceElements",  this._Lang.getMessage("ERR_REPLACE_EL"));
843         }
844 
845         var parentNode = item.parentNode,
846 
847             sibling = item.nextSibling,
848             resultArr = this._Lang.objToArray(evalNodes);
849 
850         for (var cnt = 0; cnt < resultArr.length; cnt++) {
851             if (cnt == 0) {
852                 this.replaceElement(item, resultArr[cnt]);
853             } else {
854                 if (sibling) {
855                     parentNode.insertBefore(resultArr[cnt], sibling);
856                 } else {
857                     parentNode.appendChild(resultArr[cnt]);
858                 }
859             }
860         }
861         return resultArr;
862     },
863 
864     /**
865      * optimized search for an array of tag names
866      * deep scan will always be performed.
867      * @param fragment the fragment which should be searched for
868      * @param tagNames an map indx of tag names which have to be found
869      *
870      */
871     findByTagNames: function(fragment, tagNames) {
872         this._assertStdParams(fragment, tagNames, "findByTagNames", ["fragment", "tagNames"]);
873 
874         var nodeType = fragment.nodeType;
875         if (nodeType != 1 && nodeType != 9 && nodeType != 11) return null;
876 
877         //we can use the shortcut
878         if (fragment.querySelectorAll) {
879             var query = [];
880             for (var key in tagNames) {
881                 if(!tagNames.hasOwnProperty(key)) continue;
882                 query.push(key);
883             }
884             var res = [];
885             if (fragment.tagName && tagNames[fragment.tagName.toLowerCase()]) {
886                 res.push(fragment);
887             }
888             return res.concat(this._Lang.objToArray(fragment.querySelectorAll(query.join(", "))));
889         }
890 
891         //now the filter function checks case insensitively for the tag names needed
892         var filter = function(node) {
893             return node.tagName && tagNames[node.tagName.toLowerCase()];
894         };
895 
896         //now we run an optimized find all on it
897         try {
898             return this.findAll(fragment, filter, true);
899         } finally {
900             //the usual IE6 is broken, fix code
901             filter = null;
902         }
903     },
904 
905     /**
906      * determines the number of nodes according to their tagType
907      *
908      * @param {Node} fragment (Node or fragment) the fragment to be investigated
909      * @param {String} tagName the tag name (lowercase)
910      * (the normal usecase is false, which means if the element is found only its
911      * adjacent elements will be scanned, due to the recursive descension
912      * this should work out with elements with different nesting depths but not being
913      * parent and child to each other
914      *
915      * @return the child elements as array or null if nothing is found
916      *
917      */
918     findByTagName : function(fragment, tagName) {
919         this._assertStdParams(fragment, tagName, "findByTagName", ["fragment", "tagName"]);
920         var _Lang = this._Lang,
921             nodeType = fragment.nodeType;
922         if (nodeType != 1 && nodeType != 9 && nodeType != 11) return null;
923 
924         //remapping to save a few bytes
925 
926         var ret = _Lang.objToArray(fragment.getElementsByTagName(tagName));
927         if (fragment.tagName && _Lang.equalsIgnoreCase(fragment.tagName, tagName)) ret.unshift(fragment);
928         return ret;
929     },
930 
931     findByName : function(fragment, name) {
932         this._assertStdParams(fragment, name, "findByName", ["fragment", "name"]);
933 
934         var nodeType = fragment.nodeType;
935         if (nodeType != 1 && nodeType != 9 && nodeType != 11) return null;
936 
937         var ret = this._Lang.objToArray(fragment.getElementsByName(name));
938         if (fragment.name == name) ret.unshift(fragment);
939         return ret;
940     },
941 
942     /**
943      * a filtered findAll for subdom treewalking
944      * (which uses browser optimizations wherever possible)
945      *
946      * @param {|Node|} rootNode the rootNode so start the scan
947      * @param filter filter closure with the syntax {boolean} filter({Node} node)
948      * @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)
949      */
950     findAll : function(rootNode, filter, deepScan) {
951         this._Lang.assertType(filter, "function");
952         deepScan = !!deepScan;
953 
954         if (document.createTreeWalker && NodeFilter) {
955             return this._iteratorSearchAll(rootNode, filter, deepScan);
956         } else {
957             //will not be called in dom level3 compliant browsers
958             return this._recursionSearchAll(rootNode, filter, deepScan);
959         }
960     },
961 
962     /**
963      * the faster dom iterator based search, works on all newer browsers
964      * except ie8 which already have implemented the dom iterator functions
965      * of html 5 (which is pretty all standard compliant browsers)
966      *
967      * The advantage of this method is a faster tree iteration compared
968      * to the normal recursive tree walking.
969      *
970      * @param rootNode the root node to be iterated over
971      * @param filter the iteration filter
972      * @param deepScan if set to true a deep scan is performed
973      */
974     _iteratorSearchAll: function(rootNode, filter, deepScan) {
975         var retVal = [];
976         //Works on firefox and webkit, opera and ie have to use the slower fallback mechanis
977         //we have a tree walker in place this allows for an optimized deep scan
978         if (filter(rootNode)) {
979 
980             retVal.push(rootNode);
981             if (!deepScan) {
982                 return retVal;
983             }
984         }
985         //we use the reject mechanism to prevent a deep scan reject means any
986         //child elements will be omitted from the scan
987         var FILTER_ACCEPT = NodeFilter.FILTER_ACCEPT,
988             FILTER_SKIP = NodeFilter.FILTER_SKIP,
989             FILTER_REJECT = NodeFilter.FILTER_REJECT;
990 
991         var walkerFilter = function (node) {
992             var retCode = (filter(node)) ? FILTER_ACCEPT : FILTER_SKIP;
993             retCode = (!deepScan && retCode == FILTER_ACCEPT) ? FILTER_REJECT : retCode;
994             if (retCode == FILTER_ACCEPT || retCode == FILTER_REJECT) {
995                 retVal.push(node);
996             }
997             return retCode;
998         };
999 
1000         var treeWalker = document.createTreeWalker(rootNode, NodeFilter.SHOW_ELEMENT, walkerFilter, false);
1001         //noinspection StatementWithEmptyBodyJS
1002         while (treeWalker.nextNode());
1003         return retVal;
1004     },
1005 
1006     /**
1007      * bugfixing for ie6 which does not cope properly with setAttribute
1008      */
1009     setAttribute : function(node, attr, val) {
1010         this._assertStdParams(node, attr, "setAttribute", ["fragment", "name"]);
1011         if (!node.setAttribute) {
1012             return;
1013         }
1014 
1015         if (attr === 'disabled') {
1016             node.disabled = val === 'disabled' || val === 'true';
1017         } else if (attr === 'checked') {
1018             node.checked = val === 'checked' || val === 'on' || val === 'true';
1019         } else if (attr == 'readonly') {
1020             node.readOnly = val === 'readonly' || val === 'true';
1021         } else {
1022             node.setAttribute(attr, val);
1023         }
1024     },
1025 
1026     /**
1027      * fuzzy form detection which tries to determine the form
1028      * an item has been detached.
1029      *
1030      * The problem is some Javascript libraries simply try to
1031      * detach controls by reusing the names
1032      * of the detached input controls. Most of the times,
1033      * the name is unique in a jsf scenario, due to the inherent form mapping.
1034      * One way or the other, we will try to fix that by
1035      * identifying the proper form over the name
1036      *
1037      * We do it in several ways, in case of no form null is returned
1038      * in case of multiple forms we check all elements with a given name (which we determine
1039      * out of a name or id of the detached element) and then iterate over them
1040      * to find whether they are in a form or not.
1041      *
1042      * If only one element within a form and a given identifier found then we can pull out
1043      * and move on
1044      *
1045      * We cannot do much further because in case of two identical named elements
1046      * all checks must fail and the first elements form is served.
1047      *
1048      * Note, this method is only triggered in case of the issuer or an ajax request
1049      * is a detached element, otherwise already existing code has served the correct form.
1050      *
1051      * This method was added because of
1052      * https://issues.apache.org/jira/browse/MYFACES-2599
1053      * to support the integration of existing ajax libraries which do heavy dom manipulation on the
1054      * controls side (Dojos Dijit library for instance).
1055      *
1056      * @param {Node} elem - element as source, can be detached, undefined or null
1057      *
1058      * @return either null or a form node if it could be determined
1059      *
1060      * TODO move this into extended and replace it with a simpler algorithm
1061      */
1062     fuzzyFormDetection : function(elem) {
1063         var forms = document.forms, _Lang = this._Lang;
1064 
1065         if (!forms || !forms.length) {
1066             return null;
1067         }
1068 
1069             // This will not work well on portlet case, because we cannot be sure
1070             // the returned form is right one.
1071             //we can cover that case by simply adding one of our config params
1072             //the default is the weaker, but more correct portlet code
1073         //you can override it with myfaces_config.no_portlet_env = true globally
1074         else if (1 == forms.length && this._RT.getGlobalConfig("no_portlet_env", false)) {
1075             return forms[0];
1076         }
1077 
1078         //before going into the more complicated stuff we try the simple approach
1079         var finalElem = this.byId(elem);
1080         var fetchForm = _Lang.hitch(this, function(elem) {
1081             //element of type form then we are already
1082             //at form level for the issuing element
1083             //https://issues.apache.org/jira/browse/MYFACES-2793
1084 
1085             return (_Lang.equalsIgnoreCase(elem.tagName, "form")) ? elem :
1086                 ( this.html5FormDetection(elem) || this.getParent(elem, "form"));
1087         });
1088 
1089         if (finalElem) {
1090             var elemForm = fetchForm(finalElem);
1091             if (elemForm) return elemForm;
1092         }
1093 
1094         /**
1095          * name check
1096          */
1097         var foundElements = [];
1098         var name = (_Lang.isString(elem)) ? elem : elem.name;
1099         //id detection did not work
1100         if (!name) return null;
1101         /**
1102          * the lesser chance is the elements which have the same name
1103          * (which is the more likely case in case of a brute dom replacement)
1104          */
1105         var nameElems = document.getElementsByName(name);
1106         if (nameElems) {
1107             for (var cnt = 0; cnt < nameElems.length && foundElements.length < 2; cnt++) {
1108                 // we already have covered the identifier case hence we only can deal with names,
1109                 var foundForm = fetchForm(nameElems[cnt]);
1110                 if (foundForm) {
1111                     foundElements.push(foundForm);
1112                 }
1113             }
1114         }
1115 
1116         return (1 == foundElements.length ) ? foundElements[0] : null;
1117     },
1118 
1119     html5FormDetection:function (item) {
1120         var elemForm = this.getAttribute(item, "form");
1121         return (elemForm) ? this.byId(elemForm) : null;
1122     },
1123 
1124 
1125     /**
1126      * gets a parent of an item with a given tagname
1127      * @param {Node} item - child element
1128      * @param {String} tagName - TagName of parent element
1129      */
1130     getParent : function(item, tagName) {
1131 
1132         if (!item) {
1133             throw this._Lang.makeException(new Error(), null, null, this._nameSpace, "getParent",
1134                 this._Lang.getMessage("ERR_MUST_BE_PROVIDED1", null, "_Dom.getParent", "item {DomNode}"));
1135         }
1136 
1137         var _Lang = this._Lang;
1138         var searchClosure = function(parentItem) {
1139             return parentItem && parentItem.tagName
1140                 && _Lang.equalsIgnoreCase(parentItem.tagName, tagName);
1141         };
1142         try {
1143             return this.getFilteredParent(item, searchClosure);
1144         } finally {
1145             searchClosure = null;
1146             _Lang = null;
1147         }
1148     },
1149 
1150     /**
1151      * A parent walker which uses
1152      * a filter closure for filtering
1153      *
1154      * @param {Node} item the root item to ascend from
1155      * @param {function} filter the filter closure
1156      */
1157     getFilteredParent : function(item, filter) {
1158         this._assertStdParams(item, filter, "getFilteredParent", ["item", "filter"]);
1159 
1160         //search parent tag parentName
1161         var parentItem = (item.parentNode) ? item.parentNode : null;
1162 
1163         while (parentItem && !filter(parentItem)) {
1164             parentItem = parentItem.parentNode;
1165         }
1166         return (parentItem) ? parentItem : null;
1167     },
1168 
1169     /**
1170      * cross ported from dojo
1171      * fetches an attribute from a node
1172      *
1173      * @param {String} node the node
1174      * @param {String} attr the attribute
1175      * @return the attributes value or null
1176      */
1177     getAttribute : function(/* HTMLElement */node, /* string */attr) {
1178         return node.getAttribute(attr);
1179     },
1180 
1181     /**
1182      * checks whether the given node has an attribute attached
1183      *
1184      * @param {String|Object} node the node to search for
1185      * @param {String} attr the attribute to search for
1186      * @true if the attribute was found
1187      */
1188     hasAttribute : function(/* HTMLElement */node, /* string */attr) {
1189         //	summary
1190         //	Determines whether or not the specified node carries a value for the attribute in question.
1191         return this.getAttribute(node, attr) ? true : false;	//	boolean
1192     },
1193 
1194     /**
1195      * concatenation routine which concats all childnodes of a node which
1196      * contains a set of CDATA blocks to one big string
1197      * @param {Node} node the node to concat its blocks for
1198      */
1199     concatCDATABlocks : function(/*Node*/ node) {
1200         var cDataBlock = [];
1201         // response may contain several blocks
1202         for (var i = 0; i < node.childNodes.length; i++) {
1203             cDataBlock.push(node.childNodes[i].data);
1204         }
1205         return cDataBlock.join('');
1206     },
1207 
1208     //all modern browsers evaluate the scripts
1209     //manually this is a w3d recommendation
1210     isManualScriptEval: function() {
1211         return true;
1212     },
1213 
1214     /**
1215      * jsf2.2
1216      * checks if there is a fileupload element within
1217      * the executes list
1218      *
1219      * @param executes the executes list
1220      * @return {Boolean} true if there is a fileupload element
1221      */
1222     isMultipartCandidate:function (executes) {
1223         if (this._Lang.isString(executes)) {
1224             executes = this._Lang.strToArray(executes, /\s+/);
1225         }
1226 
1227         for (var cnt = 0, len = executes.length; cnt < len ; cnt ++) {
1228             var element = this.byId(executes[cnt]);
1229             var inputs = this.findByTagName(element, "input", true);
1230             for (var cnt2 = 0, len2 = inputs.length; cnt2 < len2 ; cnt2++) {
1231                 if (this.getAttribute(inputs[cnt2], "type") == "file") return true;
1232             }
1233         }
1234         return false;
1235     },
1236 
1237     insertFirst: function(newNode) {
1238         var body = document.body;
1239         if (body.childNodes.length > 0) {
1240             body.insertBefore(newNode, body.firstChild);
1241         } else {
1242             body.appendChild(newNode);
1243         }
1244     },
1245 
1246     byId: function(id) {
1247         return this._Lang.byId(id);
1248     },
1249 
1250     getDummyPlaceHolder: function() {
1251         this._dummyPlaceHolder = this._dummyPlaceHolder ||this.createElement("div");
1252         return this._dummyPlaceHolder;
1253     },
1254 
1255     getNamedElementFromForm: function(form, elementId) {
1256         return form[elementId];
1257     },
1258 
1259     /**
1260      * backport new faces codebase, should work from ie9 onwards
1261      * (cutoff point)
1262      * builds the ie nodes properly in a placeholder
1263      * and bypasses a non script insert bug that way
1264      * @param markup the markup code to be executed from
1265      */
1266     fromMarkup: function(markup) {
1267 
1268         // https:// developer.mozilla.org/de/docs/Web/API/DOMParser license creative commons
1269         var doc = document.implementation.createHTMLDocument("");
1270         var lowerMarkup = markup.toLowerCase();
1271         if (lowerMarkup.indexOf('<!doctype') != -1 ||
1272             lowerMarkup.indexOf('<html') != -1 ||
1273             lowerMarkup.indexOf('<head') != -1 ||
1274             lowerMarkup.indexOf('<body') != -1) {
1275             doc.documentElement.innerHTML = markup;
1276             return doc.documentElement;
1277         } else {
1278             var dummyPlaceHolder = document.createElement("div");
1279             dummyPlaceHolder.innerHTML = markup;
1280             return dummyPlaceHolder;
1281         }
1282     },
1283 
1284     appendToHead: function(markup) {
1285 
1286         //we filter out only those evalNodes which do not match
1287         var _RT = this._RT;
1288         var _Lang = this._Lang;
1289         var _T = this;
1290 
1291         var doubleExistsFilter = function(item)  {
1292             switch((item.tagName || "").toLowerCase()) {
1293                 case "script":
1294                     var src = item.getAttribute("src");
1295                     var content = item.innerText;
1296                     var scripts = document.head.getElementsByTagName("script");
1297 
1298                     for(var cnt = 0; cnt < scripts.length; cnt++) {
1299                         if(src && _Lang.match(scripts[cnt].getAttribute("src"), src)) {
1300                             return false;
1301                         } else if(!src && _Lang.match(scripts[cnt].innerText, content)) {
1302                             return false;
1303                         }
1304                     }
1305                     break;
1306                 case "style":
1307                     var content = item.innerText;
1308                     var styles = document.head.getElementsByTagName("style");
1309                     for(var cnt = 0; cnt < styles.length; cnt++) {
1310                         if(content && _Lang.match(styles[cnt].innerText, content)) {
1311                             return false;
1312                         }
1313                     }
1314                     break;
1315                 case "link":
1316                     var href = item.getAttribute("href");
1317                     var content = item.innerText;
1318                     var links = document.head.getElementsByTagName("link");
1319                     for(var cnt = 0; cnt < links.length; cnt++) {
1320                         if(href && _Lang.match(links[cnt].getAttribute("href"), href)) {
1321                             return false;
1322                         } else if(!href && _Lang.match(links[cnt].innerText, content)) {
1323                             return false;
1324                         }
1325                     }
1326                     break;
1327                 default: break;
1328             }
1329             return true;
1330         };
1331 
1332         var appendElement = function (item) {
1333             var tagName = (item.tagName || "").toLowerCase();
1334             var nonce = _RT.resolveNonce(item);
1335             if (tagName === "script") {
1336                 var newItem = document.createElement("script");
1337                 newItem.textContent = item.textContent;
1338                 _T.cloneAttributes(newItem, item);
1339                 item = newItem;
1340             } else if (tagName === "link") {
1341                 var newItem = document.createElement("link");
1342                 newItem.textContent = item.textContent;
1343                 _T.cloneAttributes(newItem, item);
1344                 item = newItem;
1345             } else if (tagName === "style") {
1346                 var newItem = document.createElement("style");
1347                 newItem.textContent = item.textContent;
1348                 _T.cloneAttributes(newItem, item);
1349                 item = newItem;
1350             }
1351 
1352             document.head.appendChild(item);
1353         };
1354         var evalNodes = [];
1355         if(this._Lang.isString(markup)) {
1356             var lastHeadChildTag = document.getElementsByTagName("head")[0].lastChild;
1357             //resource requests only hav one item anyway
1358             evalNodes = this._buildEvalNodes(null, markup);
1359         } else {
1360             evalNodes = markup.childNodes;
1361         }
1362 
1363 
1364         //var evalNodes = this._buildEvalNodes(lastHeadChildTag, markup);
1365         var scripts = this._Lang.arrFilter(evalNodes, function(item) {
1366            return (item.tagName || "").toLowerCase() == "script";
1367         }, 0, this);
1368         var other = this._Lang.arrFilter(evalNodes, function(item) {
1369             return (item.tagName || "").toLowerCase() != "script";
1370         }, 0, this);
1371 
1372         var finalOther = this._Lang.arrFilter(other, doubleExistsFilter , 0, this);
1373         var finalScripts = this._Lang.arrFilter(scripts, doubleExistsFilter , 0, this);
1374         //var finalAll = this._Lang.arrFilter(evalNodes, doubleExistsFilter , 0, this);
1375 
1376         this._Lang.arrForEach(finalOther, appendElement);
1377         this._Lang.arrForEach(finalScripts, appendElement);
1378         //this._Lang.arrForEach(finalAll, appendElement);
1379     }
1380 });
1381 
1382 
1383