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 */ 37TABLE_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 */ 53constructor_:function(){ 54}, 55 56runCss:function(item/*, xmlData*/){ 57 58varUDEF="undefined", 59_RT=this._RT, 60_Lang=this._Lang, 61applyStyle=function(item,style){ 62varnewSS=document.createElement("style"); 63 64newSS.setAttribute("rel",item.getAttribute("rel")||"stylesheet"); 65newSS.setAttribute("type",item.getAttribute("type")||"text/css"); 66document.getElementsByTagName("head")[0].appendChild(newSS); 67//ie merrily again goes its own way 68if(window.attachEvent&&!_RT.isOpera&&UDEF!=typeofnewSS.styleSheet&&UDEF!=newSS.styleSheet.cssText)newSS.styleSheet.cssText=style; 69elsenewSS.appendChild(document.createTextNode(style)); 70}, 71 72execCss=function(item){ 73varequalsIgnoreCase=_Lang.equalsIgnoreCase; 74vartagName=item.tagName; 75if(tagName&&equalsIgnoreCase(tagName,"link")&&equalsIgnoreCase(item.getAttribute("type"),"text/css")){ 76applyStyle(item,"@import url('"+item.getAttribute("href")+"');"); 77}elseif(tagName&&equalsIgnoreCase(tagName,"style")&&equalsIgnoreCase(item.getAttribute("type"),"text/css")){ 78varinnerText=[]; 79//compliant browsers know child nodes 80varchildNodes=item.childNodes; 81if(childNodes){ 82varlen=childNodes.length; 83for(varcnt=0;cnt<len;cnt++){ 84innerText.push(childNodes[cnt].innerHTML||childNodes[cnt].data); 85} 86//non compliant ones innerHTML 87}elseif(item.innerHTML){ 88innerText.push(item.innerHTML); 89} 90 91applyStyle(item,innerText.join("")); 92} 93}; 94 95try{ 96varscriptElements=this.findByTagNames(item,{"link":1,"style":1},true); 97if(scriptElements==null)return; 98for(varcnt=0;cnt<scriptElements.length;cnt++){ 99execCss(scriptElements[cnt]);100}101102}finally{103//the usual ie6 fix code104//the IE6 garbage collector is broken105//nulling closures helps somewhat to reduce106//mem leaks, which are impossible to avoid107//at this browser108execCss=null;109applyStyle=null;110}111},112113114/**
115 * Run through the given Html item and execute the inline scripts
116 * (IE doesn't do this by itself)
117 * @param {Node} item
118 */119runScripts:function(item,xmlData){120var_Lang=this._Lang,121_RT=this._RT,122finalScripts=[],123execScrpt=function(item){124vartagName=item.tagName;125varitemType=item.type||"";126if(tagName&&_Lang.equalsIgnoreCase(tagName,"script")&&127(itemType===""||_Lang.equalsIgnoreCase(itemType,"text/javascript")||128_Lang.equalsIgnoreCase(itemType,"javascript")||129_Lang.equalsIgnoreCase(itemType,"text/ecmascript")||130_Lang.equalsIgnoreCase(itemType,"ecmascript"))){131varsrc=item.getAttribute('src');132if('undefined'!=typeofsrc133&&null!=src134&&src.length>0135){136//we have to move this into an inner if because chrome otherwise chokes137//due to changing the and order instead of relying on left to right138//if jsf.js is already registered we do not replace it anymore139if((src.indexOf("ln=scripts")==-1&&src.indexOf("ln=javax.faces")==-1)||(src.indexOf("/jsf.js")==-1140&&src.indexOf("/jsf-uncompressed.js")==-1)){141if(finalScripts.length){142//script source means we have to eval the existing143//scripts before running the include144_RT.globalEval(finalScripts.join("\n"));145146finalScripts=[];147}148_RT.loadScriptEval(src,item.getAttribute('type'),false,"UTF-8",false);149}150151}else{152// embedded script auto eval153vartest=(!xmlData)?item.text:_Lang.serializeChilds(item);154vargo=true;155while(go){156go=false;157if(test.substring(0,1)==" "){158test=test.substring(1);159go=true;160}161if(test.substring(0,4)=="<!--"){162test=test.substring(4);163go=true;164}165if(test.substring(0,11)=="//<![CDATA["){166test=test.substring(11);167go=true;168}169}170// we have to run the script under a global context171//we store the script for less calls to eval172finalScripts.push(test);173174}175}176};177try{178varscriptElements=this.findByTagName(item,"script",true);179if(scriptElements==null)return;180for(varcnt=0;cnt<scriptElements.length;cnt++){181execScrpt(scriptElements[cnt]);182}183if(finalScripts.length){184_RT.globalEval(finalScripts.join("\n"));185}186}catch(e){187//we are now in accordance with the rest of the system of showing errors only in development mode188//the default error output is alert we always can override it with189//window.myfaces = window.myfaces || {};190//myfaces.config = myfaces.config || {};191//myfaces.config.defaultErrorOutput = console.error;192if(jsf.getProjectStage()==="Development"){193vardefaultErrorOutput=myfaces._impl.core._Runtime.getGlobalConfig("defaultErrorOutput",alert);194defaultErrorOutput("Error in evaluated javascript:"+(e.message||e.description||e));195}196}finally{197//the usual ie6 fix code198//the IE6 garbage collector is broken199//nulling closures helps somewhat to reduce200//mem leaks, which are impossible to avoid201//at this browser202execScrpt=null;203}204},205206207/**
208 * determines to fetch a node
209 * from its id or name, the name case
210 * only works if the element is unique in its name
211 * @param {String} elem
212 */213byIdOrName:function(elem){214if(!elem)returnnull;215if(!this._Lang.isString(elem))returnelem;216217varret=this.byId(elem);218if(ret)returnret;219//we try the unique name fallback220varitems=document.getElementsByName(elem);221return((items.length==1)?items[0]:null);222},223224/**
225 * node id or name, determines the valid form identifier of a node
226 * depending on its uniqueness
227 *
228 * Usually the id is chosen for an elem, but if the id does not
229 * exist we try a name fallback. If the passed element has a unique
230 * name we can use that one as subsequent identifier.
231 *
232 *
233 * @param {String} elem
234 */235nodeIdOrName:function(elem){236if(elem){237//just to make sure that the pas238239elem=this.byId(elem);240if(!elem)returnnull;241//detached element handling, we also store the element name242//to get a fallback option in case the identifier is not determinable243// anymore, in case of a framework induced detachment the element.name should244// be shared if the identifier is not determinable anymore245//the downside of this method is the element name must be unique246//which in case of jsf it is247varelementId=elem.id||elem.name;248if((elem.id==null||elem.id=='')&&elem.name){249elementId=elem.name;250251//last check for uniqueness252if(this.getElementsByName(elementId).length>1){253//no unique element name so we need to perform254//a return null to let the caller deal with this issue255returnnull;256}257}258returnelementId;259}260returnnull;261},262263deleteItems:function(items){264if(!items||!items.length)return;265for(varcnt=0;cnt<items.length;cnt++){266this.deleteItem(items[cnt]);267}268},269270/**
271 * Simple delete on an existing item
272 */273deleteItem:function(itemIdToReplace){274varitem=this.byId(itemIdToReplace);275if(!item){276throwthis._Lang.makeException(newError(),null,null,this._nameSpace,"deleteItem","_Dom.deleteItem Unknown Html-Component-ID: "+itemIdToReplace);277}278279this._removeNode(item,false);280},281282/**
283 * creates a node upon a given node name
284 * @param nodeName {String} the node name to be created
285 * @param attrs {Array} a set of attributes to be set
286 */287createElement:function(nodeName,attrs){288varret=document.createElement(nodeName);289if(attrs){290for(varkeyinattrs){291if(!attrs.hasOwnProperty(key))continue;292this.setAttribute(ret,key,attrs[key]);293}294}295returnret;296},297298/**
299 * Checks whether the browser is dom compliant.
300 * Dom compliant means that it performs the basic dom operations safely
301 * without leaking and also is able to perform a native setAttribute
302 * operation without freaking out
303 *
304 *
305 * Not dom compliant browsers are all microsoft browsers in quirks mode
306 * and ie6 and ie7 to some degree in standards mode
307 * and pretty much every browser who cannot create ranges
308 * (older mobile browsers etc...)
309 *
310 * We dont do a full browser detection here because it probably is safer
311 * to test for existing features to make an assumption about the
312 * browsers capabilities
313 */314isDomCompliant:function(){315returntrue;316},317318/**
319 * proper insert before which takes tables into consideration as well as
320 * browser deficiencies
321 * @param item the node to insert before
322 * @param markup the markup to be inserted
323 */324insertBefore:function(item,markup){325this._assertStdParams(item,markup,"insertBefore");326327markup=this._Lang.trim(markup);328if(markup==="")returnnull;329330varevalNodes=this._buildEvalNodes(item,markup),331currentRef=item,332parentNode=item.parentNode,333ret=[];334for(varcnt=evalNodes.length-1;cnt>=0;cnt--){335currentRef=parentNode.insertBefore(evalNodes[cnt],currentRef);336ret.push(currentRef);337}338ret=ret.reverse();339this._eval(ret);340returnret;341},342343/**
344 * proper insert before which takes tables into consideration as well as
345 * browser deficiencies
346 * @param item the node to insert before
347 * @param markup the markup to be inserted
348 */349insertAfter:function(item,markup){350this._assertStdParams(item,markup,"insertAfter");351markup=this._Lang.trim(markup);352if(markup==="")returnnull;353354varevalNodes=this._buildEvalNodes(item,markup),355currentRef=item,356parentNode=item.parentNode,357ret=[];358359for(varcnt=0;cnt<evalNodes.length;cnt++){360if(currentRef.nextSibling){361//Winmobile 6 has problems with this strategy, but it is not really fixable362currentRef=parentNode.insertBefore(evalNodes[cnt],currentRef.nextSibling);363}else{364currentRef=parentNode.appendChild(evalNodes[cnt]);365}366ret.push(currentRef);367}368this._eval(ret);369returnret;370},371372propertyToAttribute:function(name){373if(name==='className'){374return'class';375}elseif(name==='xmllang'){376return'xml:lang';377}else{378returnname.toLowerCase();379}380},381382isFunctionNative:function(func){383return/^\s*function[^{]+{\s*\[native code\]\s*}\s*$/.test(String(func));384},385386detectAttributes:function(element){387//test if 'hasAttribute' method is present and its native code is intact388//for example, Prototype can add its own implementation if missing389if(element.hasAttribute&&this.isFunctionNative(element.hasAttribute)){390returnfunction(name){391returnelement.hasAttribute(name);392}393}else{394try{395//when accessing .getAttribute method without arguments does not throw an error then the method is not available396element.getAttribute;397398varhtml=element.outerHTML;399varstartTag=html.match(/^<[^>]*>/)[0];400returnfunction(name){401returnstartTag.indexOf(name+'=')>-1;402}403}catch(ex){404returnfunction(name){405returnelement.getAttribute(name);406}407}408}409},410411/**
412 * copy all attributes from one element to another - except id
413 * @param target element to copy attributes to
414 * @param source element to copy attributes from
415 * @ignore
416 */417cloneAttributes:function(target,source){418419// enumerate core element attributes - without 'dir' as special case420varcoreElementProperties=['className','title','lang','xmllang'];421// enumerate additional input element attributes422varinputElementProperties=[423'name','value','size','maxLength','src','alt','useMap','tabIndex','accessKey','accept','type'424];425// enumerate additional boolean input attributes426varinputElementBooleanProperties=[427'checked','disabled','readOnly'428];429430// Enumerate all the names of the event listeners431varlistenerNames=432['onclick','ondblclick','onmousedown','onmousemove','onmouseout',433'onmouseover','onmouseup','onkeydown','onkeypress','onkeyup',434'onhelp','onblur','onfocus','onchange','onload','onunload','onabort',435'onreset','onselect','onsubmit'436];437438varsourceAttributeDetector=this.detectAttributes(source);439vartargetAttributeDetector=this.detectAttributes(target);440441varisInputElement=target.nodeName.toLowerCase()==='input';442varpropertyNames=isInputElement?coreElementProperties.concat(inputElementProperties):coreElementProperties;443varisXML=!source.ownerDocument.contentType||source.ownerDocument.contentType=='text/xml';444for(variIndex=0,iLength=propertyNames.length;iIndex<iLength;iIndex++){445varpropertyName=propertyNames[iIndex];446varattributeName=this.propertyToAttribute(propertyName);447if(sourceAttributeDetector(attributeName)){448449//With IE 7 (quirks or standard mode) and IE 8/9 (quirks mode only),450//you cannot get the attribute using 'class'. You must use 'className'451//which is the same value you use to get the indexed property. The only452//reliable way to detect this (without trying to evaluate the browser453//mode and version) is to compare the two return values using 'className'454//to see if they exactly the same. If they are, then use the property455//name when using getAttribute.456if(attributeName=='class'){457if(this._RT.browser.isIE&&(source.getAttribute(propertyName)===source[propertyName])){458attributeName=propertyName;459}460}461462varnewValue=isXML?source.getAttribute(attributeName):source[propertyName];463varoldValue=target[propertyName];464if(oldValue!=newValue){465target[propertyName]=newValue;466}467}else{468target.removeAttribute(attributeName);469if(attributeName=="value"){470target[propertyName]='';471}472}473}474475varbooleanPropertyNames=isInputElement?inputElementBooleanProperties:[];476for(varjIndex=0,jLength=booleanPropertyNames.length;jIndex<jLength;jIndex++){477varbooleanPropertyName=booleanPropertyNames[jIndex];478varnewBooleanValue=source[booleanPropertyName];479varoldBooleanValue=target[booleanPropertyName];480if(oldBooleanValue!=newBooleanValue){481target[booleanPropertyName]=newBooleanValue;482}483}484485//'style' attribute special case486if(sourceAttributeDetector('style')){487varnewStyle;488varoldStyle;489if(this._RT.browser.isIE){490newStyle=source.style.cssText;491oldStyle=target.style.cssText;492if(newStyle!=oldStyle){493target.style.cssText=newStyle;494}495}else{496newStyle=source.getAttribute('style');497oldStyle=target.getAttribute('style');498if(newStyle!=oldStyle){499target.setAttribute('style',newStyle);500}501}502}elseif(targetAttributeDetector('style')){503target.removeAttribute('style');504}505506// Special case for 'dir' attribute507if(!this._RT.browser.isIE&&source.dir!=target.dir){508if(sourceAttributeDetector('dir')){509target.dir=source.dir;510}elseif(targetAttributeDetector('dir')){511target.dir='';512}513}514515for(varlIndex=0,lLength=listenerNames.length;lIndex<lLength;lIndex++){516varname=listenerNames[lIndex];517target[name]=source[name]?source[name]:null;518if(source[name]){519source[name]=null;520}521}522523//clone HTML5 data-* attributes524try{525vartargetDataset=target.dataset;526varsourceDataset=source.dataset;527if(targetDataset||sourceDataset){528//cleanup the dataset529for(vartpintargetDataset){530deletetargetDataset[tp];531}532//copy dataset's properties533for(varspinsourceDataset){534targetDataset[sp]=sourceDataset[sp];535}536}537}catch(ex){538//most probably dataset properties are not supported539}540},541//from542// http://blog.vishalon.net/index.php/javascript-getting-and-setting-caret-position-in-textarea/543getCaretPosition:function(ctrl){544varcaretPos=0;545546try{547548// other browsers make it simpler by simply having a selection start element549if(ctrl.selectionStart||ctrl.selectionStart=='0')550caretPos=ctrl.selectionStart;551// ie 5 quirks mode as second option because552// this option is flakey in conjunction with text areas553// TODO move this into the quirks class554elseif(document.selection){555ctrl.focus();556varselection=document.selection.createRange();557//the selection now is start zero558selection.moveStart('character',-ctrl.value.length);559//the caretposition is the selection start560caretPos=selection.text.length;561}562}catch(e){563//now this is ugly, but not supported input types throw errors for selectionStart564//this way we are future proof by having not to define every selection enabled565//input in an if (which will be a lot in the near future with html5)566}567returncaretPos;568},569570setCaretPosition:function(ctrl,pos){571572if(ctrl.createTextRange){573varrange=ctrl.createTextRange();574range.collapse(true);575range.moveEnd('character',pos);576range.moveStart('character',pos);577range.select();578}579//IE quirks mode again, TODO move this into the quirks class580elseif(ctrl.setSelectionRange){581ctrl.focus();582//the selection range is our caret position583ctrl.setSelectionRange(pos,pos);584}585},586587/**
588 * outerHTML replacement which works cross browserlike
589 * but still is speed optimized
590 *
591 * @param item the item to be replaced
592 * @param markup the markup for the replacement
593 * @param preserveFocus, tries to preserve the focus within the outerhtml operation
594 * if set to true a focus preservation algorithm based on document.activeElement is
595 * used to preserve the focus at the exactly same location as it was
596 *
597 */598outerHTML:function(item,markup,preserveFocus){599this._assertStdParams(item,markup,"outerHTML");600// we can work on a single element in a cross browser fashion601// regarding the focus thanks to the602// icefaces team for providing the code603if(item.nodeName.toLowerCase()==='input'){604varreplacingInput=this._buildEvalNodes(item,markup)[0];605this.cloneAttributes(item,replacingInput);606returnitem;607}else{608markup=this._Lang.trim(markup);609if(markup!==""){610varret=null;611612varfocusElementId=null;613varcaretPosition=0;614if(preserveFocus&&'undefined'!=typeofdocument.activeElement){615focusElementId=(document.activeElement)?document.activeElement.id:null;616caretPosition=this.getCaretPosition(document.activeElement);617}618// we try to determine the browsers compatibility619// level to standards dom level 2 via various methods620if(this.isDomCompliant()){621ret=this._outerHTMLCompliant(item,markup);622}else{623//call into abstract method624ret=this._outerHTMLNonCompliant(item,markup);625}626if(focusElementId){627varnewFocusElement=this.byId(focusElementId);628if(newFocusElement&&newFocusElement.nodeName.toLowerCase()==='input'){629//just in case the replacement element is not focusable anymore630if("undefined"!=typeofnewFocusElement.focus){631newFocusElement.focus();632}633}634if(newFocusElement&&caretPosition){635//zero caret position is set automatically on focus636this.setCaretPosition(newFocusElement,caretPosition);637}638}639640// and remove the old item641//first we have to save the node newly insert for easier access in our eval part642this._eval(ret);643returnret;644}645// and remove the old item, in case of an empty newtag and do nothing else646this._removeNode(item,false);647returnnull;648}649},650651/**
652 * detaches a set of nodes from their parent elements
653 * in a browser independend manner
654 * @param {Object} items the items which need to be detached
655 * @return {Array} an array of nodes with the detached dom nodes
656 */657detach:function(items){658varret=[];659if('undefined'!=typeofitems.nodeType){660if(items.parentNode){661ret.push(items.parentNode.removeChild(items));662}else{663ret.push(items);664}665returnret;666}667//all ies treat node lists not as arrays so we have to take668//an intermediate step669varnodeArr=this._Lang.objToArray(items);670for(varcnt=0;cnt<nodeArr.length;cnt++){671ret.push(nodeArr[cnt].parentNode.removeChild(nodeArr[cnt]));672}673returnret;674},675676_outerHTMLCompliant:function(item,markup){677//table element replacements like thead, tbody etc... have to be treated differently678varevalNodes=this._buildEvalNodes(item,markup);679680if(evalNodes.length==1){681varret=evalNodes[0];682item.parentNode.replaceChild(ret,item);683returnret;684}else{685returnthis.replaceElements(item,evalNodes);686}687},688689/**
690 * checks if the provided element is a subelement of a table element
691 * @param item
692 */693_isTableElement:function(item){694return!!this.TABLE_ELEMS[(item.nodeName||item.tagName).toLowerCase()];695},696697/**
698 * non ie browsers do not have problems with embedded scripts or any other construct
699 * we simply can use an innerHTML in a placeholder
700 *
701 * @param markup the markup to be used
702 */703_buildNodesCompliant:function(markup){704vardummyPlaceHolder=this.getDummyPlaceHolder();//document.createElement("div");705dummyPlaceHolder.innerHTML=markup;706returnthis._Lang.objToArray(dummyPlaceHolder.childNodes);707},708709710711712/**
713 * builds up a correct dom subtree
714 * if the markup is part of table nodes
715 * The usecase for this is to allow subtable rendering
716 * like single rows thead or tbody
717 *
718 * @param item
719 * @param markup
720 */721_buildTableNodes:function(item,markup){722varitemNodeName=(item.nodeName||item.tagName).toLowerCase();723724vartmpNodeName=itemNodeName;725vardepth=0;726while(tmpNodeName!="table"){727item=item.parentNode;728tmpNodeName=(item.nodeName||item.tagName).toLowerCase();729depth++;730}731732vardummyPlaceHolder=this.getDummyPlaceHolder();733if(itemNodeName=="td"){734dummyPlaceHolder.innerHTML="<table><tbody><tr>"+markup+"</tr></tbody></table>";735}else{736dummyPlaceHolder.innerHTML="<table>"+markup+"</table>";737}738739for(varcnt=0;cnt<depth;cnt++){740dummyPlaceHolder=dummyPlaceHolder.childNodes[0];741}742743returnthis.detach(dummyPlaceHolder.childNodes);744},745746_removeChildNodes:function(node/*, breakEventsOpen */){747if(!node)return;748node.innerHTML="";749},750751752753_removeNode:function(node/*, breakEventsOpen*/){754if(!node)return;755varparentNode=node.parentNode;756if(parentNode)//if the node has a parent757parentNode.removeChild(node);758},759760761/**
762 * build up the nodes from html markup in a browser independend way
763 * so that it also works with table nodes
764 *
765 * @param item the parent item upon the nodes need to be processed upon after building
766 * @param markup the markup to be built up
767 */768_buildEvalNodes:function(item,markup){769varevalNodes=null;770if(this._isTableElement(item)){771evalNodes=this._buildTableNodes(item,markup);772}else{773varnonIEQuirks=(!this._RT.browser.isIE||this._RT.browser.isIE>8);774//ie8 has a special problem it still has the swallow scripts and other775//elements bug, but it is mostly dom compliant so we have to give it a special776//treatment, IE9 finally fixes that issue finally after 10 years777evalNodes=(this.isDomCompliant()&&nonIEQuirks)?778this._buildNodesCompliant(markup):779//ie8 or quirks mode browsers780this._buildNodesNonCompliant(markup);781}782returnevalNodes;783},784785/**
786 * we have lots of methods with just an item and a markup as params
787 * this method builds an assertion for those methods to reduce code
788 *
789 * @param item the item to be tested
790 * @param markup the markup
791 * @param caller caller function
792 * @param {optional} params array of assertion param names
793 */794_assertStdParams:function(item,markup,caller,params){795//internal error796if(!caller){797throwthis._Lang.makeException(newError(),null,null,this._nameSpace,"_assertStdParams","Caller must be set for assertion");798}799var_Lang=this._Lang,800ERR_PROV="ERR_MUST_BE_PROVIDED1",801DOM="myfaces._impl._util._Dom.",802finalParams=params||["item","markup"];803804if(!item||!markup){805_Lang.makeException(newError(),null,null,DOM,""+caller,_Lang.getMessage(ERR_PROV,null,DOM+"."+caller,(!item)?finalParams[0]:finalParams[1]));806//throw Error(_Lang.getMessage(ERR_PROV, null, DOM + caller, (!item) ? params[0] : params[1]));807}808},809810/**
811 * internal eval handler used by various functions
812 * @param _nodeArr
813 */814_eval:function(_nodeArr){815if(this.isManualScriptEval()){816varisArr=_nodeArrinstanceofArray;817if(isArr&&_nodeArr.length){818for(varcnt=0;cnt<_nodeArr.length;cnt++){819this.runScripts(_nodeArr[cnt]);820}821}elseif(!isArr){822this.runScripts(_nodeArr);823}824}825},826827/**
828 * for performance reasons we work with replaceElement and replaceElements here
829 * after measuring performance it has shown that passing down an array instead
830 * of a single node makes replaceElement twice as slow, however
831 * a single node case is the 95% case
832 *
833 * @param item
834 * @param evalNode
835 */836replaceElement:function(item,evalNode){837//browsers with defect garbage collection838item.parentNode.insertBefore(evalNode,item);839this._removeNode(item,false);840},841842843/**
844 * replaces an element with another element or a set of elements
845 *
846 * @param item the item to be replaced
847 *
848 * @param evalNodes the elements
849 */850replaceElements:function(item,evalNodes){851varevalNodesDefined=evalNodes&&'undefined'!=typeofevalNodes.length;852if(!evalNodesDefined){853throwthis._Lang.makeException(newError(),null,null,this._nameSpace,"replaceElements",this._Lang.getMessage("ERR_REPLACE_EL"));854}855856varparentNode=item.parentNode,857858sibling=item.nextSibling,859resultArr=this._Lang.objToArray(evalNodes);860861for(varcnt=0;cnt<resultArr.length;cnt++){862if(cnt==0){863this.replaceElement(item,resultArr[cnt]);864}else{865if(sibling){866parentNode.insertBefore(resultArr[cnt],sibling);867}else{868parentNode.appendChild(resultArr[cnt]);869}870}871}872returnresultArr;873},874875/**
876 * optimized search for an array of tag names
877 * deep scan will always be performed.
878 * @param fragment the fragment which should be searched for
879 * @param tagNames an map indx of tag names which have to be found
880 *
881 */882findByTagNames:function(fragment,tagNames){883this._assertStdParams(fragment,tagNames,"findByTagNames",["fragment","tagNames"]);884885varnodeType=fragment.nodeType;886if(nodeType!=1&&nodeType!=9&&nodeType!=11)returnnull;887888//we can use the shortcut889if(fragment.querySelectorAll){890varquery=[];891for(varkeyintagNames){892if(!tagNames.hasOwnProperty(key))continue;893query.push(key);894}895varres=[];896if(fragment.tagName&&tagNames[fragment.tagName.toLowerCase()]){897res.push(fragment);898}899returnres.concat(this._Lang.objToArray(fragment.querySelectorAll(query.join(", "))));900}901902//now the filter function checks case insensitively for the tag names needed903varfilter=function(node){904returnnode.tagName&&tagNames[node.tagName.toLowerCase()];905};906907//now we run an optimized find all on it908try{909returnthis.findAll(fragment,filter,true);910}finally{911//the usual IE6 is broken, fix code912filter=null;913}914},915916/**
917 * determines the number of nodes according to their tagType
918 *
919 * @param {Node} fragment (Node or fragment) the fragment to be investigated
920 * @param {String} tagName the tag name (lowercase)
921 * (the normal usecase is false, which means if the element is found only its
922 * adjacent elements will be scanned, due to the recursive descension
923 * this should work out with elements with different nesting depths but not being
924 * parent and child to each other
925 *
926 * @return the child elements as array or null if nothing is found
927 *
928 */929findByTagName:function(fragment,tagName){930this._assertStdParams(fragment,tagName,"findByTagName",["fragment","tagName"]);931var_Lang=this._Lang,932nodeType=fragment.nodeType;933if(nodeType!=1&&nodeType!=9&&nodeType!=11)returnnull;934935//remapping to save a few bytes936937varret=_Lang.objToArray(fragment.getElementsByTagName(tagName));938if(fragment.tagName&&_Lang.equalsIgnoreCase(fragment.tagName,tagName))ret.unshift(fragment);939returnret;940},941942findByName:function(fragment,name){943this._assertStdParams(fragment,name,"findByName",["fragment","name"]);944945varnodeType=fragment.nodeType;946if(nodeType!=1&&nodeType!=9&&nodeType!=11)returnnull;947948varret=this._Lang.objToArray(fragment.getElementsByName(name));949if(fragment.name==name)ret.unshift(fragment);950returnret;951},952953/**
954 * a filtered findAll for subdom treewalking
955 * (which uses browser optimizations wherever possible)
956 *
957 * @param {|Node|} rootNode the rootNode so start the scan
958 * @param filter filter closure with the syntax {boolean} filter({Node} node)
959 * @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)
960 */961findAll:function(rootNode,filter,deepScan){962this._Lang.assertType(filter,"function");963deepScan=!!deepScan;964965if(document.createTreeWalker&&NodeFilter){966returnthis._iteratorSearchAll(rootNode,filter,deepScan);967}else{968//will not be called in dom level3 compliant browsers969returnthis._recursionSearchAll(rootNode,filter,deepScan);970}971},972973/**
974 * the faster dom iterator based search, works on all newer browsers
975 * except ie8 which already have implemented the dom iterator functions
976 * of html 5 (which is pretty all standard compliant browsers)
977 *
978 * The advantage of this method is a faster tree iteration compared
979 * to the normal recursive tree walking.
980 *
981 * @param rootNode the root node to be iterated over
982 * @param filter the iteration filter
983 * @param deepScan if set to true a deep scan is performed
984 */985_iteratorSearchAll:function(rootNode,filter,deepScan){986varretVal=[];987//Works on firefox and webkit, opera and ie have to use the slower fallback mechanis988//we have a tree walker in place this allows for an optimized deep scan989if(filter(rootNode)){990991retVal.push(rootNode);992if(!deepScan){993returnretVal;994}995}996//we use the reject mechanism to prevent a deep scan reject means any997//child elements will be omitted from the scan998varFILTER_ACCEPT=NodeFilter.FILTER_ACCEPT,999FILTER_SKIP=NodeFilter.FILTER_SKIP,1000FILTER_REJECT=NodeFilter.FILTER_REJECT;10011002varwalkerFilter=function(node){1003varretCode=(filter(node))?FILTER_ACCEPT:FILTER_SKIP;1004retCode=(!deepScan&&retCode==FILTER_ACCEPT)?FILTER_REJECT:retCode;1005if(retCode==FILTER_ACCEPT||retCode==FILTER_REJECT){1006retVal.push(node);1007}1008returnretCode;1009};10101011vartreeWalker=document.createTreeWalker(rootNode,NodeFilter.SHOW_ELEMENT,walkerFilter,false);1012//noinspection StatementWithEmptyBodyJS1013while(treeWalker.nextNode());1014returnretVal;1015},10161017/**
1018 * bugfixing for ie6 which does not cope properly with setAttribute
1019 */1020setAttribute:function(node,attr,val){1021this._assertStdParams(node,attr,"setAttribute",["fragment","name"]);1022if(!node.setAttribute){1023return;1024}10251026if(attr==='disabled'){1027node.disabled=val==='disabled'||val==='true';1028}elseif(attr==='checked'){1029node.checked=val==='checked'||val==='on'||val==='true';1030}elseif(attr=='readonly'){1031node.readOnly=val==='readonly'||val==='true';1032}else{1033node.setAttribute(attr,val);1034}1035},10361037/**
1038 * fuzzy form detection which tries to determine the form
1039 * an item has been detached.
1040 *
1041 * The problem is some Javascript libraries simply try to
1042 * detach controls by reusing the names
1043 * of the detached input controls. Most of the times,
1044 * the name is unique in a jsf scenario, due to the inherent form mapping.
1045 * One way or the other, we will try to fix that by
1046 * identifying the proper form over the name
1047 *
1048 * We do it in several ways, in case of no form null is returned
1049 * in case of multiple forms we check all elements with a given name (which we determine
1050 * out of a name or id of the detached element) and then iterate over them
1051 * to find whether they are in a form or not.
1052 *
1053 * If only one element within a form and a given identifier found then we can pull out
1054 * and move on
1055 *
1056 * We cannot do much further because in case of two identical named elements
1057 * all checks must fail and the first elements form is served.
1058 *
1059 * Note, this method is only triggered in case of the issuer or an ajax request
1060 * is a detached element, otherwise already existing code has served the correct form.
1061 *
1062 * This method was added because of
1063 * https://issues.apache.org/jira/browse/MYFACES-2599
1064 * to support the integration of existing ajax libraries which do heavy dom manipulation on the
1065 * controls side (Dojos Dijit library for instance).
1066 *
1067 * @param {Node} elem - element as source, can be detached, undefined or null
1068 *
1069 * @return either null or a form node if it could be determined
1070 *
1071 * TODO move this into extended and replace it with a simpler algorithm
1072 */1073fuzzyFormDetection:function(elem){1074varforms=document.forms,_Lang=this._Lang;10751076if(!forms||!forms.length){1077returnnull;1078}10791080// This will not work well on portlet case, because we cannot be sure1081// the returned form is right one.1082//we can cover that case by simply adding one of our config params1083//the default is the weaker, but more correct portlet code1084//you can override it with myfaces_config.no_portlet_env = true globally1085elseif(1==forms.length&&this._RT.getGlobalConfig("no_portlet_env",false)){1086returnforms[0];1087}10881089//before going into the more complicated stuff we try the simple approach1090varfinalElem=this.byId(elem);1091varfetchForm=_Lang.hitch(this,function(elem){1092//element of type form then we are already1093//at form level for the issuing element1094//https://issues.apache.org/jira/browse/MYFACES-279310951096return(_Lang.equalsIgnoreCase(elem.tagName,"form"))?elem:1097(this.html5FormDetection(elem)||this.getParent(elem,"form"));1098});10991100if(finalElem){1101varelemForm=fetchForm(finalElem);1102if(elemForm)returnelemForm;1103}11041105/**
1106 * name check
1107 */1108varfoundElements=[];1109varname=(_Lang.isString(elem))?elem:elem.name;1110//id detection did not work1111if(!name)returnnull;1112/**
1113 * the lesser chance is the elements which have the same name
1114 * (which is the more likely case in case of a brute dom replacement)
1115 */1116varnameElems=document.getElementsByName(name);1117if(nameElems){1118for(varcnt=0;cnt<nameElems.length&&foundElements.length<2;cnt++){1119// we already have covered the identifier case hence we only can deal with names,1120varfoundForm=fetchForm(nameElems[cnt]);1121if(foundForm){1122foundElements.push(foundForm);1123}1124}1125}11261127return(1==foundElements.length)?foundElements[0]:null;1128},11291130html5FormDetection:function(/*item*/){1131returnnull;1132},113311341135/**
1136 * gets a parent of an item with a given tagname
1137 * @param {Node} item - child element
1138 * @param {String} tagName - TagName of parent element
1139 */1140getParent:function(item,tagName){11411142if(!item){1143throwthis._Lang.makeException(newError(),null,null,this._nameSpace,"getParent",1144this._Lang.getMessage("ERR_MUST_BE_PROVIDED1",null,"_Dom.getParent","item {DomNode}"));1145}11461147var_Lang=this._Lang;1148varsearchClosure=function(parentItem){1149returnparentItem&&parentItem.tagName1150&&_Lang.equalsIgnoreCase(parentItem.tagName,tagName);1151};1152try{1153returnthis.getFilteredParent(item,searchClosure);1154}finally{1155searchClosure=null;1156_Lang=null;1157}1158},11591160/**
1161 * A parent walker which uses
1162 * a filter closure for filtering
1163 *
1164 * @param {Node} item the root item to ascend from
1165 * @param {function} filter the filter closure
1166 */1167getFilteredParent:function(item,filter){1168this._assertStdParams(item,filter,"getFilteredParent",["item","filter"]);11691170//search parent tag parentName1171varparentItem=(item.parentNode)?item.parentNode:null;11721173while(parentItem&&!filter(parentItem)){1174parentItem=parentItem.parentNode;1175}1176return(parentItem)?parentItem:null;1177},11781179/**
1180 * cross ported from dojo
1181 * fetches an attribute from a node
1182 *
1183 * @param {String} node the node
1184 * @param {String} attr the attribute
1185 * @return the attributes value or null
1186 */1187getAttribute:function(/* HTMLElement */node,/* string */attr){1188returnnode.getAttribute(attr);1189},11901191/**
1192 * checks whether the given node has an attribute attached
1193 *
1194 * @param {String|Object} node the node to search for
1195 * @param {String} attr the attribute to search for
1196 * @true if the attribute was found
1197 */1198hasAttribute:function(/* HTMLElement */node,/* string */attr){1199// summary1200// Determines whether or not the specified node carries a value for the attribute in question.1201returnthis.getAttribute(node,attr)?true:false;// boolean1202},12031204/**
1205 * concatenation routine which concats all childnodes of a node which
1206 * contains a set of CDATA blocks to one big string
1207 * @param {Node} node the node to concat its blocks for
1208 */1209concatCDATABlocks:function(/*Node*/node){1210varcDataBlock=[];1211// response may contain several blocks1212for(vari=0;i<node.childNodes.length;i++){1213cDataBlock.push(node.childNodes[i].data);1214}1215returncDataBlock.join('');1216},12171218//all modern browsers evaluate the scripts1219//manually this is a w3d recommendation1220isManualScriptEval:function(){1221returntrue;1222},12231224isMultipartCandidate:function(/*executes*/){1225//implementation in the experimental part1226returnfalse;1227},12281229insertFirst:function(newNode){1230varbody=document.body;1231if(body.childNodes.length>0){1232body.insertBefore(newNode,body.firstChild);1233}else{1234body.appendChild(newNode);1235}1236},12371238byId:function(id){1239returnthis._Lang.byId(id);1240},12411242getDummyPlaceHolder:function(){1243this._dummyPlaceHolder=this._dummyPlaceHolder||this.createElement("div");1244returnthis._dummyPlaceHolder;1245},12461247/**
1248 * fetches the window id for the current request
1249 * note, this is a preparation method for jsf 2.2
1250 *
1251 */1252getWindowId:function(){1253//implementation in the experimental part1254returnnull;1255}1256});125712581259