diff options
author | Andrew Dolgov <[email protected]> | 2011-03-04 19:02:28 +0300 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2011-03-04 19:02:59 +0300 |
commit | a089699c8915636ba4f158d77dba9b012bc93208 (patch) | |
tree | b2d7d051f1f55d44a6be07d3ee137e5a7ccfcefb /lib/dojo/_base/html.js | |
parent | cfad9259a6feacfa8194b1312770ae6db1ecce50 (diff) |
build custom layer of Dojo to speed up loading of tt-rss (refs #293)
Diffstat (limited to 'lib/dojo/_base/html.js')
-rw-r--r-- | lib/dojo/_base/html.js | 2549 |
1 files changed, 1817 insertions, 732 deletions
diff --git a/lib/dojo/_base/html.js b/lib/dojo/_base/html.js index 050841531..be5fd2aaa 100644 --- a/lib/dojo/_base/html.js +++ b/lib/dojo/_base/html.js @@ -5,745 +5,1830 @@ */ -if(!dojo._hasResource["dojo._base.html"]){ -dojo._hasResource["dojo._base.html"]=true; +if(!dojo._hasResource["dojo._base.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo._base.html"] = true; dojo.require("dojo._base.lang"); dojo.provide("dojo._base.html"); + +// FIXME: need to add unit tests for all the semi-public methods + try{ -document.execCommand("BackgroundImageCache",false,true); -} -catch(e){ + document.execCommand("BackgroundImageCache", false, true); +}catch(e){ + // sane browsers don't have cache "issues" } -if(dojo.isIE||dojo.isOpera){ -dojo.byId=function(id,_1){ -if(typeof id!="string"){ -return id; -} -var _2=_1||dojo.doc,te=_2.getElementById(id); -if(te&&(te.attributes.id.value==id||te.id==id)){ -return te; + +// ============================= +// DOM Functions +// ============================= + +/*===== +dojo.byId = function(id, doc){ + // summary: + // Returns DOM node with matching `id` attribute or `null` + // if not found. If `id` is a DomNode, this function is a no-op. + // + // id: String|DOMNode + // A string to match an HTML id attribute or a reference to a DOM Node + // + // doc: Document? + // Document to work in. Defaults to the current value of + // dojo.doc. Can be used to retrieve + // node references from other documents. + // + // example: + // Look up a node by ID: + // | var n = dojo.byId("foo"); + // + // example: + // Check if a node exists, and use it. + // | var n = dojo.byId("bar"); + // | if(n){ doStuff() ... } + // + // example: + // Allow string or DomNode references to be passed to a custom function: + // | var foo = function(nodeOrId){ + // | nodeOrId = dojo.byId(nodeOrId); + // | // ... more stuff + // | } +=====*/ + +if(dojo.isIE || dojo.isOpera){ + dojo.byId = function(id, doc){ + if(typeof id != "string"){ + return id; + } + var _d = doc || dojo.doc, te = _d.getElementById(id); + // attributes.id.value is better than just id in case the + // user has a name=id inside a form + if(te && (te.attributes.id.value == id || te.id == id)){ + return te; + }else{ + var eles = _d.all[id]; + if(!eles || eles.nodeName){ + eles = [eles]; + } + // if more than 1, choose first with the correct id + var i=0; + while((te=eles[i++])){ + if((te.attributes && te.attributes.id && te.attributes.id.value == id) + || te.id == id){ + return te; + } + } + } + }; }else{ -var _3=_2.all[id]; -if(!_3||_3.nodeName){ -_3=[_3]; -} -var i=0; -while((te=_3[i++])){ -if((te.attributes&&te.attributes.id&&te.attributes.id.value==id)||te.id==id){ -return te; -} -} + dojo.byId = function(id, doc){ + // inline'd type check + return (typeof id == "string") ? (doc || dojo.doc).getElementById(id) : id; // DomNode + }; } +/*===== }; -}else{ -dojo.byId=function(id,_4){ -return (typeof id=="string")?(_4||dojo.doc).getElementById(id):id; -}; -} +=====*/ + (function(){ -var d=dojo; -var _5=d.byId; -var _6=null,_7; -d.addOnWindowUnload(function(){ -_6=null; -}); -dojo._destroyElement=dojo.destroy=function(_8){ -_8=_5(_8); -try{ -var _9=_8.ownerDocument; -if(!_6||_7!=_9){ -_6=_9.createElement("div"); -_7=_9; -} -_6.appendChild(_8.parentNode?_8.parentNode.removeChild(_8):_8); -_6.innerHTML=""; -} -catch(e){ -} -}; -dojo.isDescendant=function(_a,_b){ -try{ -_a=_5(_a); -_b=_5(_b); -while(_a){ -if(_a==_b){ -return true; -} -_a=_a.parentNode; -} -} -catch(e){ -} -return false; -}; -dojo.setSelectable=function(_c,_d){ -_c=_5(_c); -if(d.isMozilla){ -_c.style.MozUserSelect=_d?"":"none"; -}else{ -if(d.isKhtml||d.isWebKit){ -_c.style.KhtmlUserSelect=_d?"auto":"none"; -}else{ -if(d.isIE){ -var v=(_c.unselectable=_d?"":"on"); -d.query("*",_c).forEach("item.unselectable = '"+v+"'"); -} -} -} -}; -var _e=function(_f,ref){ -var _10=ref.parentNode; -if(_10){ -_10.insertBefore(_f,ref); -} -}; -var _11=function(_12,ref){ -var _13=ref.parentNode; -if(_13){ -if(_13.lastChild==ref){ -_13.appendChild(_12); -}else{ -_13.insertBefore(_12,ref.nextSibling); -} -} -}; -dojo.place=function(_14,_15,_16){ -_15=_5(_15); -if(typeof _14=="string"){ -_14=_14.charAt(0)=="<"?d._toDom(_14,_15.ownerDocument):_5(_14); -} -if(typeof _16=="number"){ -var cn=_15.childNodes; -if(!cn.length||cn.length<=_16){ -_15.appendChild(_14); -}else{ -_e(_14,cn[_16<0?0:_16]); -} -}else{ -switch(_16){ -case "before": -_e(_14,_15); -break; -case "after": -_11(_14,_15); -break; -case "replace": -_15.parentNode.replaceChild(_14,_15); -break; -case "only": -d.empty(_15); -_15.appendChild(_14); -break; -case "first": -if(_15.firstChild){ -_e(_14,_15.firstChild); -break; -} -default: -_15.appendChild(_14); -} -} -return _14; -}; -dojo.boxModel="content-box"; -if(d.isIE){ -d.boxModel=document.compatMode=="BackCompat"?"border-box":"content-box"; -} -var gcs; -if(d.isWebKit){ -gcs=function(_17){ -var s; -if(_17.nodeType==1){ -var dv=_17.ownerDocument.defaultView; -s=dv.getComputedStyle(_17,null); -if(!s&&_17.style){ -_17.style.display=""; -s=dv.getComputedStyle(_17,null); -} -} -return s||{}; -}; -}else{ -if(d.isIE){ -gcs=function(_18){ -return _18.nodeType==1?_18.currentStyle:{}; -}; -}else{ -gcs=function(_19){ -return _19.nodeType==1?_19.ownerDocument.defaultView.getComputedStyle(_19,null):{}; -}; -} -} -dojo.getComputedStyle=gcs; -if(!d.isIE){ -d._toPixelValue=function(_1a,_1b){ -return parseFloat(_1b)||0; -}; -}else{ -d._toPixelValue=function(_1c,_1d){ -if(!_1d){ -return 0; -} -if(_1d=="medium"){ -return 4; -} -if(_1d.slice&&_1d.slice(-2)=="px"){ -return parseFloat(_1d); -} -with(_1c){ -var _1e=style.left; -var _1f=runtimeStyle.left; -runtimeStyle.left=currentStyle.left; -try{ -style.left=_1d; -_1d=style.pixelLeft; -} -catch(e){ -_1d=0; -} -style.left=_1e; -runtimeStyle.left=_1f; -} -return _1d; -}; -} -var px=d._toPixelValue; -var _20="DXImageTransform.Microsoft.Alpha"; -var af=function(n,f){ -try{ -return n.filters.item(_20); -} -catch(e){ -return f?{}:null; -} -}; -dojo._getOpacity=d.isIE?function(_21){ -try{ -return af(_21).Opacity/100; -} -catch(e){ -return 1; -} -}:function(_22){ -return gcs(_22).opacity; -}; -dojo._setOpacity=d.isIE?function(_23,_24){ -var ov=_24*100,_25=_24==1; -_23.style.zoom=_25?"":1; -if(!af(_23)){ -if(_25){ -return _24; -} -_23.style.filter+=" progid:"+_20+"(Opacity="+ov+")"; -}else{ -af(_23,1).Opacity=ov; -} -af(_23,1).Enabled=!_25; -if(_23.nodeName.toLowerCase()=="tr"){ -d.query("> td",_23).forEach(function(i){ -d._setOpacity(i,_24); -}); -} -return _24; -}:function(_26,_27){ -return _26.style.opacity=_27; -}; -var _28={left:true,top:true}; -var _29=/margin|padding|width|height|max|min|offset/; -var _2a=function(_2b,_2c,_2d){ -_2c=_2c.toLowerCase(); -if(d.isIE){ -if(_2d=="auto"){ -if(_2c=="height"){ -return _2b.offsetHeight; -} -if(_2c=="width"){ -return _2b.offsetWidth; -} -} -if(_2c=="fontweight"){ -switch(_2d){ -case 700: -return "bold"; -case 400: -default: -return "normal"; -} -} -} -if(!(_2c in _28)){ -_28[_2c]=_29.test(_2c); -} -return _28[_2c]?px(_2b,_2d):_2d; -}; -var _2e=d.isIE?"styleFloat":"cssFloat",_2f={"cssFloat":_2e,"styleFloat":_2e,"float":_2e}; -dojo.style=function(_30,_31,_32){ -var n=_5(_30),_33=arguments.length,op=(_31=="opacity"); -_31=_2f[_31]||_31; -if(_33==3){ -return op?d._setOpacity(n,_32):n.style[_31]=_32; -} -if(_33==2&&op){ -return d._getOpacity(n); -} -var s=gcs(n); -if(_33==2&&typeof _31!="string"){ -for(var x in _31){ -d.style(_30,x,_31[x]); -} -return s; -} -return (_33==1)?s:_2a(n,_31,s[_31]||n.style[_31]); -}; -dojo._getPadExtents=function(n,_34){ -var s=_34||gcs(n),l=px(n,s.paddingLeft),t=px(n,s.paddingTop); -return {l:l,t:t,w:l+px(n,s.paddingRight),h:t+px(n,s.paddingBottom)}; -}; -dojo._getBorderExtents=function(n,_35){ -var ne="none",s=_35||gcs(n),bl=(s.borderLeftStyle!=ne?px(n,s.borderLeftWidth):0),bt=(s.borderTopStyle!=ne?px(n,s.borderTopWidth):0); -return {l:bl,t:bt,w:bl+(s.borderRightStyle!=ne?px(n,s.borderRightWidth):0),h:bt+(s.borderBottomStyle!=ne?px(n,s.borderBottomWidth):0)}; -}; -dojo._getPadBorderExtents=function(n,_36){ -var s=_36||gcs(n),p=d._getPadExtents(n,s),b=d._getBorderExtents(n,s); -return {l:p.l+b.l,t:p.t+b.t,w:p.w+b.w,h:p.h+b.h}; -}; -dojo._getMarginExtents=function(n,_37){ -var s=_37||gcs(n),l=px(n,s.marginLeft),t=px(n,s.marginTop),r=px(n,s.marginRight),b=px(n,s.marginBottom); -if(d.isWebKit&&(s.position!="absolute")){ -r=l; -} -return {l:l,t:t,w:l+r,h:t+b}; -}; -dojo._getMarginBox=function(_38,_39){ -var s=_39||gcs(_38),me=d._getMarginExtents(_38,s); -var l=_38.offsetLeft-me.l,t=_38.offsetTop-me.t,p=_38.parentNode; -if(d.isMoz){ -var sl=parseFloat(s.left),st=parseFloat(s.top); -if(!isNaN(sl)&&!isNaN(st)){ -l=sl,t=st; -}else{ -if(p&&p.style){ -var pcs=gcs(p); -if(pcs.overflow!="visible"){ -var be=d._getBorderExtents(p,pcs); -l+=be.l,t+=be.t; -} -} -} -}else{ -if(d.isOpera||(d.isIE>7&&!d.isQuirks)){ -if(p){ -be=d._getBorderExtents(p); -l-=be.l; -t-=be.t; -} -} -} -return {l:l,t:t,w:_38.offsetWidth+me.w,h:_38.offsetHeight+me.h}; -}; -dojo._getContentBox=function(_3a,_3b){ -var s=_3b||gcs(_3a),pe=d._getPadExtents(_3a,s),be=d._getBorderExtents(_3a,s),w=_3a.clientWidth,h; -if(!w){ -w=_3a.offsetWidth,h=_3a.offsetHeight; -}else{ -h=_3a.clientHeight,be.w=be.h=0; -} -if(d.isOpera){ -pe.l+=be.l; -pe.t+=be.t; -} -return {l:pe.l,t:pe.t,w:w-pe.w-be.w,h:h-pe.h-be.h}; -}; -dojo._getBorderBox=function(_3c,_3d){ -var s=_3d||gcs(_3c),pe=d._getPadExtents(_3c,s),cb=d._getContentBox(_3c,s); -return {l:cb.l-pe.l,t:cb.t-pe.t,w:cb.w+pe.w,h:cb.h+pe.h}; -}; -dojo._setBox=function(_3e,l,t,w,h,u){ -u=u||"px"; -var s=_3e.style; -if(!isNaN(l)){ -s.left=l+u; -} -if(!isNaN(t)){ -s.top=t+u; -} -if(w>=0){ -s.width=w+u; -} -if(h>=0){ -s.height=h+u; -} -}; -dojo._isButtonTag=function(_3f){ -return _3f.tagName=="BUTTON"||_3f.tagName=="INPUT"&&(_3f.getAttribute("type")||"").toUpperCase()=="BUTTON"; -}; -dojo._usesBorderBox=function(_40){ -var n=_40.tagName; -return d.boxModel=="border-box"||n=="TABLE"||d._isButtonTag(_40); -}; -dojo._setContentSize=function(_41,_42,_43,_44){ -if(d._usesBorderBox(_41)){ -var pb=d._getPadBorderExtents(_41,_44); -if(_42>=0){ -_42+=pb.w; -} -if(_43>=0){ -_43+=pb.h; -} -} -d._setBox(_41,NaN,NaN,_42,_43); -}; -dojo._setMarginBox=function(_45,_46,_47,_48,_49,_4a){ -var s=_4a||gcs(_45),bb=d._usesBorderBox(_45),pb=bb?_4b:d._getPadBorderExtents(_45,s); -if(d.isWebKit){ -if(d._isButtonTag(_45)){ -var ns=_45.style; -if(_48>=0&&!ns.width){ -ns.width="4px"; -} -if(_49>=0&&!ns.height){ -ns.height="4px"; -} -} -} -var mb=d._getMarginExtents(_45,s); -if(_48>=0){ -_48=Math.max(_48-pb.w-mb.w,0); -} -if(_49>=0){ -_49=Math.max(_49-pb.h-mb.h,0); -} -d._setBox(_45,_46,_47,_48,_49); -}; -var _4b={l:0,t:0,w:0,h:0}; -dojo.marginBox=function(_4c,box){ -var n=_5(_4c),s=gcs(n),b=box; -return !b?d._getMarginBox(n,s):d._setMarginBox(n,b.l,b.t,b.w,b.h,s); -}; -dojo.contentBox=function(_4d,box){ -var n=_5(_4d),s=gcs(n),b=box; -return !b?d._getContentBox(n,s):d._setContentSize(n,b.w,b.h,s); -}; -var _4e=function(_4f,_50){ -if(!(_4f=(_4f||0).parentNode)){ -return 0; -} -var val,_51=0,_52=d.body(); -while(_4f&&_4f.style){ -if(gcs(_4f).position=="fixed"){ -return 0; -} -val=_4f[_50]; -if(val){ -_51+=val-0; -if(_4f==_52){ -break; -} -} -_4f=_4f.parentNode; -} -return _51; -}; -dojo._docScroll=function(){ -var n=d.global; -return "pageXOffset" in n?{x:n.pageXOffset,y:n.pageYOffset}:(n=d.doc.documentElement,n.clientHeight?{x:d._fixIeBiDiScrollLeft(n.scrollLeft),y:n.scrollTop}:(n=d.body(),{x:n.scrollLeft||0,y:n.scrollTop||0})); -}; -dojo._isBodyLtr=function(){ -return "_bodyLtr" in d?d._bodyLtr:d._bodyLtr=(d.body().dir||d.doc.documentElement.dir||"ltr").toLowerCase()=="ltr"; -}; -dojo._getIeDocumentElementOffset=function(){ -var de=d.doc.documentElement; -if(d.isIE<8){ -var r=de.getBoundingClientRect(); -var l=r.left,t=r.top; -if(d.isIE<7){ -l+=de.clientLeft; -t+=de.clientTop; -} -return {x:l<0?0:l,y:t<0?0:t}; -}else{ -return {x:0,y:0}; -} -}; -dojo._fixIeBiDiScrollLeft=function(_53){ -var dd=d.doc; -if(d.isIE<8&&!d._isBodyLtr()){ -var de=d.isQuirks?dd.body:dd.documentElement; -return _53+de.clientWidth-de.scrollWidth; -} -return _53; -}; -dojo._abs=dojo.position=function(_54,_55){ -var db=d.body(),dh=db.parentNode,ret; -_54=_5(_54); -if(_54["getBoundingClientRect"]){ -ret=_54.getBoundingClientRect(); -ret={x:ret.left,y:ret.top,w:ret.right-ret.left,h:ret.bottom-ret.top}; -if(d.isIE){ -var _56=d._getIeDocumentElementOffset(); -ret.x-=_56.x+(d.isQuirks?db.clientLeft+db.offsetLeft:0); -ret.y-=_56.y+(d.isQuirks?db.clientTop+db.offsetTop:0); -}else{ -if(d.isFF==3){ -var cs=gcs(dh); -ret.x-=px(dh,cs.marginLeft)+px(dh,cs.borderLeftWidth); -ret.y-=px(dh,cs.marginTop)+px(dh,cs.borderTopWidth); -} -} -}else{ -ret={x:0,y:0,w:_54.offsetWidth,h:_54.offsetHeight}; -if(_54["offsetParent"]){ -ret.x-=_4e(_54,"scrollLeft"); -ret.y-=_4e(_54,"scrollTop"); -var _57=_54; -do{ -var n=_57.offsetLeft,t=_57.offsetTop; -ret.x+=isNaN(n)?0:n; -ret.y+=isNaN(t)?0:t; -cs=gcs(_57); -if(_57!=_54){ -if(d.isMoz){ -ret.x+=2*px(_57,cs.borderLeftWidth); -ret.y+=2*px(_57,cs.borderTopWidth); -}else{ -ret.x+=px(_57,cs.borderLeftWidth); -ret.y+=px(_57,cs.borderTopWidth); -} -} -if(d.isMoz&&cs.position=="static"){ -var _58=_57.parentNode; -while(_58!=_57.offsetParent){ -var pcs=gcs(_58); -if(pcs.position=="static"){ -ret.x+=px(_57,pcs.borderLeftWidth); -ret.y+=px(_57,pcs.borderTopWidth); -} -_58=_58.parentNode; -} -} -_57=_57.offsetParent; -}while((_57!=dh)&&_57); -}else{ -if(_54.x&&_54.y){ -ret.x+=isNaN(_54.x)?0:_54.x; -ret.y+=isNaN(_54.y)?0:_54.y; -} -} -} -if(_55){ -var _59=d._docScroll(); -ret.x+=_59.x; -ret.y+=_59.y; -} -return ret; -}; -dojo.coords=function(_5a,_5b){ -var n=_5(_5a),s=gcs(n),mb=d._getMarginBox(n,s); -var abs=d.position(n,_5b); -mb.x=abs.x; -mb.y=abs.y; -return mb; -}; -var _5c={"class":"className","for":"htmlFor",tabindex:"tabIndex",readonly:"readOnly",colspan:"colSpan",frameborder:"frameBorder",rowspan:"rowSpan",valuetype:"valueType"},_5d={classname:"class",htmlfor:"for",tabindex:"tabIndex",readonly:"readOnly"},_5e={innerHTML:1,className:1,htmlFor:d.isIE,value:1}; -var _5f=function(_60){ -return _5d[_60.toLowerCase()]||_60; -}; -var _61=function(_62,_63){ -var _64=_62.getAttributeNode&&_62.getAttributeNode(_63); -return _64&&_64.specified; -}; -dojo.hasAttr=function(_65,_66){ -var lc=_66.toLowerCase(); -return _5e[_5c[lc]||_66]||_61(_5(_65),_5d[lc]||_66); -}; -var _67={},_68=0,_69=dojo._scopeName+"attrid",_6a={col:1,colgroup:1,table:1,tbody:1,tfoot:1,thead:1,tr:1,title:1}; -dojo.attr=function(_6b,_6c,_6d){ -_6b=_5(_6b); -var _6e=arguments.length,_6f; -if(_6e==2&&typeof _6c!="string"){ -for(var x in _6c){ -d.attr(_6b,x,_6c[x]); -} -return _6b; -} -var lc=_6c.toLowerCase(),_70=_5c[lc]||_6c,_71=_5e[_70],_72=_5d[lc]||_6c; -if(_6e==3){ -do{ -if(_70=="style"&&typeof _6d!="string"){ -d.style(_6b,_6d); -break; -} -if(_70=="innerHTML"){ -if(d.isIE&&_6b.tagName.toLowerCase() in _6a){ -d.empty(_6b); -_6b.appendChild(d._toDom(_6d,_6b.ownerDocument)); -}else{ -_6b[_70]=_6d; -} -break; -} -if(d.isFunction(_6d)){ -var _73=d.attr(_6b,_69); -if(!_73){ -_73=_68++; -d.attr(_6b,_69,_73); -} -if(!_67[_73]){ -_67[_73]={}; -} -var h=_67[_73][_70]; -if(h){ -d.disconnect(h); -}else{ -try{ -delete _6b[_70]; -} -catch(e){ -} -} -_67[_73][_70]=d.connect(_6b,_70,_6d); -break; -} -if(_71||typeof _6d=="boolean"){ -_6b[_70]=_6d; -break; -} -_6b.setAttribute(_72,_6d); -}while(false); -return _6b; -} -_6d=_6b[_70]; -if(_71&&typeof _6d!="undefined"){ -return _6d; -} -if(_70!="href"&&(typeof _6d=="boolean"||d.isFunction(_6d))){ -return _6d; -} -return _61(_6b,_72)?_6b.getAttribute(_72):null; -}; -dojo.removeAttr=function(_74,_75){ -_5(_74).removeAttribute(_5f(_75)); -}; -dojo.getNodeProp=function(_76,_77){ -_76=_5(_76); -var lc=_77.toLowerCase(),_78=_5c[lc]||_77; -if((_78 in _76)&&_78!="href"){ -return _76[_78]; -} -var _79=_5d[lc]||_77; -return _61(_76,_79)?_76.getAttribute(_79):null; -}; -dojo.create=function(tag,_7a,_7b,pos){ -var doc=d.doc; -if(_7b){ -_7b=_5(_7b); -doc=_7b.ownerDocument; -} -if(typeof tag=="string"){ -tag=doc.createElement(tag); -} -if(_7a){ -d.attr(tag,_7a); -} -if(_7b){ -d.place(tag,_7b,pos); -} -return tag; -}; -d.empty=d.isIE?function(_7c){ -_7c=_5(_7c); -for(var c;c=_7c.lastChild;){ -d.destroy(c); -} -}:function(_7d){ -_5(_7d).innerHTML=""; -}; -var _7e={option:["select"],tbody:["table"],thead:["table"],tfoot:["table"],tr:["table","tbody"],td:["table","tbody","tr"],th:["table","thead","tr"],legend:["fieldset"],caption:["table"],colgroup:["table"],col:["table","colgroup"],li:["ul"]},_7f=/<\s*([\w\:]+)/,_80={},_81=0,_82="__"+d._scopeName+"ToDomId"; -for(var _83 in _7e){ -var tw=_7e[_83]; -tw.pre=_83=="option"?"<select multiple=\"multiple\">":"<"+tw.join("><")+">"; -tw.post="</"+tw.reverse().join("></")+">"; -} -d._toDom=function(_84,doc){ -doc=doc||d.doc; -var _85=doc[_82]; -if(!_85){ -doc[_82]=_85=++_81+""; -_80[_85]=doc.createElement("div"); -} -_84+=""; -var _86=_84.match(_7f),tag=_86?_86[1].toLowerCase():"",_87=_80[_85],_88,i,fc,df; -if(_86&&_7e[tag]){ -_88=_7e[tag]; -_87.innerHTML=_88.pre+_84+_88.post; -for(i=_88.length;i;--i){ -_87=_87.firstChild; -} -}else{ -_87.innerHTML=_84; -} -if(_87.childNodes.length==1){ -return _87.removeChild(_87.firstChild); -} -df=doc.createDocumentFragment(); -while(fc=_87.firstChild){ -df.appendChild(fc); -} -return df; -}; -var _89="className"; -dojo.hasClass=function(_8a,_8b){ -return ((" "+_5(_8a)[_89]+" ").indexOf(" "+_8b+" ")>=0); -}; -var _8c=/\s+/,a1=[""],_8d=function(s){ -if(typeof s=="string"||s instanceof String){ -if(s.indexOf(" ")<0){ -a1[0]=s; -return a1; -}else{ -return s.split(_8c); -} -} -return s||""; -}; -dojo.addClass=function(_8e,_8f){ -_8e=_5(_8e); -_8f=_8d(_8f); -var cls=_8e[_89],_90; -cls=cls?" "+cls+" ":" "; -_90=cls.length; -for(var i=0,len=_8f.length,c;i<len;++i){ -c=_8f[i]; -if(c&&cls.indexOf(" "+c+" ")<0){ -cls+=c+" "; -} -} -if(_90<cls.length){ -_8e[_89]=cls.substr(1,cls.length-2); -} -}; -dojo.removeClass=function(_91,_92){ -_91=_5(_91); -var cls; -if(_92!==undefined){ -_92=_8d(_92); -cls=" "+_91[_89]+" "; -for(var i=0,len=_92.length;i<len;++i){ -cls=cls.replace(" "+_92[i]+" "," "); -} -cls=d.trim(cls); -}else{ -cls=""; -} -if(_91[_89]!=cls){ -_91[_89]=cls; -} -}; -dojo.toggleClass=function(_93,_94,_95){ -if(_95===undefined){ -_95=!d.hasClass(_93,_94); -} -d[_95?"addClass":"removeClass"](_93,_94); -}; + var d = dojo; + var byId = d.byId; + + var _destroyContainer = null, + _destroyDoc; + d.addOnWindowUnload(function(){ + _destroyContainer = null; //prevent IE leak + }); + +/*===== + dojo._destroyElement = function(node){ + // summary: + // Existing alias for `dojo.destroy`. Deprecated, will be removed + // in 2.0 + } +=====*/ + dojo._destroyElement = dojo.destroy = function(/*String|DomNode*/node){ + // summary: + // Removes a node from its parent, clobbering it and all of its + // children. + // + // description: + // Removes a node from its parent, clobbering it and all of its + // children. Function only works with DomNodes, and returns nothing. + // + // node: + // A String ID or DomNode reference of the element to be destroyed + // + // example: + // Destroy a node byId: + // | dojo.destroy("someId"); + // + // example: + // Destroy all nodes in a list by reference: + // | dojo.query(".someNode").forEach(dojo.destroy); + + node = byId(node); + try{ + var doc = node.ownerDocument; + // cannot use _destroyContainer.ownerDocument since this can throw an exception on IE + if(!_destroyContainer || _destroyDoc != doc){ + _destroyContainer = doc.createElement("div"); + _destroyDoc = doc; + } + _destroyContainer.appendChild(node.parentNode ? node.parentNode.removeChild(node) : node); + // NOTE: see http://trac.dojotoolkit.org/ticket/2931. This may be a bug and not a feature + _destroyContainer.innerHTML = ""; + }catch(e){ + /* squelch */ + } + }; + + dojo.isDescendant = function(/*DomNode|String*/node, /*DomNode|String*/ancestor){ + // summary: + // Returns true if node is a descendant of ancestor + // node: string id or node reference to test + // ancestor: string id or node reference of potential parent to test against + // + // example: + // Test is node id="bar" is a descendant of node id="foo" + // | if(dojo.isDescendant("bar", "foo")){ ... } + try{ + node = byId(node); + ancestor = byId(ancestor); + while(node){ + if(node == ancestor){ + return true; // Boolean + } + node = node.parentNode; + } + }catch(e){ /* squelch, return false */ } + return false; // Boolean + }; + + dojo.setSelectable = function(/*DomNode|String*/node, /*Boolean*/selectable){ + // summary: + // Enable or disable selection on a node + // node: + // id or reference to node + // selectable: + // state to put the node in. false indicates unselectable, true + // allows selection. + // example: + // Make the node id="bar" unselectable + // | dojo.setSelectable("bar"); + // example: + // Make the node id="bar" selectable + // | dojo.setSelectable("bar", true); + node = byId(node); + if(d.isMozilla){ + node.style.MozUserSelect = selectable ? "" : "none"; + }else if(d.isKhtml || d.isWebKit){ + node.style.KhtmlUserSelect = selectable ? "auto" : "none"; + }else if(d.isIE){ + var v = (node.unselectable = selectable ? "" : "on"); + d.query("*", node).forEach("item.unselectable = '"+v+"'"); + } + //FIXME: else? Opera? + }; + + var _insertBefore = function(/*DomNode*/node, /*DomNode*/ref){ + var parent = ref.parentNode; + if(parent){ + parent.insertBefore(node, ref); + } + }; + + var _insertAfter = function(/*DomNode*/node, /*DomNode*/ref){ + // summary: + // Try to insert node after ref + var parent = ref.parentNode; + if(parent){ + if(parent.lastChild == ref){ + parent.appendChild(node); + }else{ + parent.insertBefore(node, ref.nextSibling); + } + } + }; + + dojo.place = function(node, refNode, position){ + // summary: + // Attempt to insert node into the DOM, choosing from various positioning options. + // Returns the first argument resolved to a DOM node. + // + // node: String|DomNode + // id or node reference, or HTML fragment starting with "<" to place relative to refNode + // + // refNode: String|DomNode + // id or node reference to use as basis for placement + // + // position: String|Number? + // string noting the position of node relative to refNode or a + // number indicating the location in the childNodes collection of refNode. + // Accepted string values are: + // | * before + // | * after + // | * replace + // | * only + // | * first + // | * last + // "first" and "last" indicate positions as children of refNode, "replace" replaces refNode, + // "only" replaces all children. position defaults to "last" if not specified + // + // returns: DomNode + // Returned values is the first argument resolved to a DOM node. + // + // .place() is also a method of `dojo.NodeList`, allowing `dojo.query` node lookups. + // + // example: + // Place a node by string id as the last child of another node by string id: + // | dojo.place("someNode", "anotherNode"); + // + // example: + // Place a node by string id before another node by string id + // | dojo.place("someNode", "anotherNode", "before"); + // + // example: + // Create a Node, and place it in the body element (last child): + // | dojo.place("<div></div>", dojo.body()); + // + // example: + // Put a new LI as the first child of a list by id: + // | dojo.place("<li></li>", "someUl", "first"); + + refNode = byId(refNode); + if(typeof node == "string"){ // inline'd type check + node = node.charAt(0) == "<" ? d._toDom(node, refNode.ownerDocument) : byId(node); + } + if(typeof position == "number"){ // inline'd type check + var cn = refNode.childNodes; + if(!cn.length || cn.length <= position){ + refNode.appendChild(node); + }else{ + _insertBefore(node, cn[position < 0 ? 0 : position]); + } + }else{ + switch(position){ + case "before": + _insertBefore(node, refNode); + break; + case "after": + _insertAfter(node, refNode); + break; + case "replace": + refNode.parentNode.replaceChild(node, refNode); + break; + case "only": + d.empty(refNode); + refNode.appendChild(node); + break; + case "first": + if(refNode.firstChild){ + _insertBefore(node, refNode.firstChild); + break; + } + // else fallthrough... + default: // aka: last + refNode.appendChild(node); + } + } + return node; // DomNode + } + + // Box functions will assume this model. + // On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode. + // Can be set to change behavior of box setters. + + // can be either: + // "border-box" + // "content-box" (default) + dojo.boxModel = "content-box"; + + // We punt per-node box mode testing completely. + // If anybody cares, we can provide an additional (optional) unit + // that overrides existing code to include per-node box sensitivity. + + // Opera documentation claims that Opera 9 uses border-box in BackCompat mode. + // but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box. + // IIRC, earlier versions of Opera did in fact use border-box. + // Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault. + + if(d.isIE /*|| dojo.isOpera*/){ + // client code may have to adjust if compatMode varies across iframes + d.boxModel = document.compatMode == "BackCompat" ? "border-box" : "content-box"; + } + + // ============================= + // Style Functions + // ============================= + + // getComputedStyle drives most of the style code. + // Wherever possible, reuse the returned object. + // + // API functions below that need to access computed styles accept an + // optional computedStyle parameter. + // If this parameter is omitted, the functions will call getComputedStyle themselves. + // This way, calling code can access computedStyle once, and then pass the reference to + // multiple API functions. + +/*===== + dojo.getComputedStyle = function(node){ + // summary: + // Returns a "computed style" object. + // + // description: + // Gets a "computed style" object which can be used to gather + // information about the current state of the rendered node. + // + // Note that this may behave differently on different browsers. + // Values may have different formats and value encodings across + // browsers. + // + // Note also that this method is expensive. Wherever possible, + // reuse the returned object. + // + // Use the dojo.style() method for more consistent (pixelized) + // return values. + // + // node: DOMNode + // A reference to a DOM node. Does NOT support taking an + // ID string for speed reasons. + // example: + // | dojo.getComputedStyle(dojo.byId('foo')).borderWidth; + // + // example: + // Reusing the returned object, avoiding multiple lookups: + // | var cs = dojo.getComputedStyle(dojo.byId("someNode")); + // | var w = cs.width, h = cs.height; + return; // CSS2Properties + } +=====*/ + + // Although we normally eschew argument validation at this + // level, here we test argument 'node' for (duck)type, + // by testing nodeType, ecause 'document' is the 'parentNode' of 'body' + // it is frequently sent to this function even + // though it is not Element. + var gcs; + if(d.isWebKit){ + gcs = function(/*DomNode*/node){ + var s; + if(node.nodeType == 1){ + var dv = node.ownerDocument.defaultView; + s = dv.getComputedStyle(node, null); + if(!s && node.style){ + node.style.display = ""; + s = dv.getComputedStyle(node, null); + } + } + return s || {}; + }; + }else if(d.isIE){ + gcs = function(node){ + // IE (as of 7) doesn't expose Element like sane browsers + return node.nodeType == 1 /* ELEMENT_NODE*/ ? node.currentStyle : {}; + }; + }else{ + gcs = function(node){ + return node.nodeType == 1 ? + node.ownerDocument.defaultView.getComputedStyle(node, null) : {}; + }; + } + dojo.getComputedStyle = gcs; + + if(!d.isIE){ + d._toPixelValue = function(element, value){ + // style values can be floats, client code may want + // to round for integer pixels. + return parseFloat(value) || 0; + }; + }else{ + d._toPixelValue = function(element, avalue){ + if(!avalue){ return 0; } + // on IE7, medium is usually 4 pixels + if(avalue == "medium"){ return 4; } + // style values can be floats, client code may + // want to round this value for integer pixels. + if(avalue.slice && avalue.slice(-2) == 'px'){ return parseFloat(avalue); } + with(element){ + var sLeft = style.left; + var rsLeft = runtimeStyle.left; + runtimeStyle.left = currentStyle.left; + try{ + // 'avalue' may be incompatible with style.left, which can cause IE to throw + // this has been observed for border widths using "thin", "medium", "thick" constants + // those particular constants could be trapped by a lookup + // but perhaps there are more + style.left = avalue; + avalue = style.pixelLeft; + }catch(e){ + avalue = 0; + } + style.left = sLeft; + runtimeStyle.left = rsLeft; + } + return avalue; + } + } + var px = d._toPixelValue; + + // FIXME: there opacity quirks on FF that we haven't ported over. Hrm. + /*===== + dojo._getOpacity = function(node){ + // summary: + // Returns the current opacity of the passed node as a + // floating-point value between 0 and 1. + // node: DomNode + // a reference to a DOM node. Does NOT support taking an + // ID string for speed reasons. + // returns: Number between 0 and 1 + return; // Number + } + =====*/ + + var astr = "DXImageTransform.Microsoft.Alpha"; + var af = function(n, f){ + try{ + return n.filters.item(astr); + }catch(e){ + return f ? {} : null; + } + }; + + dojo._getOpacity = + d.isIE ? function(node){ + try{ + return af(node).Opacity / 100; // Number + }catch(e){ + return 1; // Number + } + } : + function(node){ + return gcs(node).opacity; + }; + + /*===== + dojo._setOpacity = function(node, opacity){ + // summary: + // set the opacity of the passed node portably. Returns the + // new opacity of the node. + // node: DOMNode + // a reference to a DOM node. Does NOT support taking an + // ID string for performance reasons. + // opacity: Number + // A Number between 0 and 1. 0 specifies transparent. + // returns: Number between 0 and 1 + return; // Number + } + =====*/ + + dojo._setOpacity = + d.isIE ? function(/*DomNode*/node, /*Number*/opacity){ + var ov = opacity * 100, opaque = opacity == 1; + node.style.zoom = opaque ? "" : 1; + + if(!af(node)){ + if(opaque){ + return opacity; + } + node.style.filter += " progid:" + astr + "(Opacity=" + ov + ")"; + }else{ + af(node, 1).Opacity = ov; + } + + // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so disable it altogether (bug #2661), + //but still update the opacity value so we can get a correct reading if it is read later. + af(node, 1).Enabled = !opaque; + + if(node.nodeName.toLowerCase() == "tr"){ + d.query("> td", node).forEach(function(i){ + d._setOpacity(i, opacity); + }); + } + return opacity; + } : + function(node, opacity){ + return node.style.opacity = opacity; + }; + + var _pixelNamesCache = { + left: true, top: true + }; + var _pixelRegExp = /margin|padding|width|height|max|min|offset/; // |border + var _toStyleValue = function(node, type, value){ + type = type.toLowerCase(); // FIXME: should we really be doing string case conversion here? Should we cache it? Need to profile! + if(d.isIE){ + if(value == "auto"){ + if(type == "height"){ return node.offsetHeight; } + if(type == "width"){ return node.offsetWidth; } + } + if(type == "fontweight"){ + switch(value){ + case 700: return "bold"; + case 400: + default: return "normal"; + } + } + } + if(!(type in _pixelNamesCache)){ + _pixelNamesCache[type] = _pixelRegExp.test(type); + } + return _pixelNamesCache[type] ? px(node, value) : value; + }; + + var _floatStyle = d.isIE ? "styleFloat" : "cssFloat", + _floatAliases = { "cssFloat": _floatStyle, "styleFloat": _floatStyle, "float": _floatStyle } + ; + + // public API + + dojo.style = function( /*DomNode|String*/ node, + /*String?|Object?*/ style, + /*String?*/ value){ + // summary: + // Accesses styles on a node. If 2 arguments are + // passed, acts as a getter. If 3 arguments are passed, acts + // as a setter. + // description: + // Getting the style value uses the computed style for the node, so the value + // will be a calculated value, not just the immediate node.style value. + // Also when getting values, use specific style names, + // like "borderBottomWidth" instead of "border" since compound values like + // "border" are not necessarily reflected as expected. + // If you want to get node dimensions, use `dojo.marginBox()`, + // `dojo.contentBox()` or `dojo.position()`. + // node: + // id or reference to node to get/set style for + // style: + // the style property to set in DOM-accessor format + // ("borderWidth", not "border-width") or an object with key/value + // pairs suitable for setting each property. + // value: + // If passed, sets value on the node for style, handling + // cross-browser concerns. When setting a pixel value, + // be sure to include "px" in the value. For instance, top: "200px". + // Otherwise, in some cases, some browsers will not apply the style. + // example: + // Passing only an ID or node returns the computed style object of + // the node: + // | dojo.style("thinger"); + // example: + // Passing a node and a style property returns the current + // normalized, computed value for that property: + // | dojo.style("thinger", "opacity"); // 1 by default + // + // example: + // Passing a node, a style property, and a value changes the + // current display of the node and returns the new computed value + // | dojo.style("thinger", "opacity", 0.5); // == 0.5 + // + // example: + // Passing a node, an object-style style property sets each of the values in turn and returns the computed style object of the node: + // | dojo.style("thinger", { + // | "opacity": 0.5, + // | "border": "3px solid black", + // | "height": "300px" + // | }); + // + // example: + // When the CSS style property is hyphenated, the JavaScript property is camelCased. + // font-size becomes fontSize, and so on. + // | dojo.style("thinger",{ + // | fontSize:"14pt", + // | letterSpacing:"1.2em" + // | }); + // + // example: + // dojo.NodeList implements .style() using the same syntax, omitting the "node" parameter, calling + // dojo.style() on every element of the list. See: `dojo.query()` and `dojo.NodeList()` + // | dojo.query(".someClassName").style("visibility","hidden"); + // | // or + // | dojo.query("#baz > div").style({ + // | opacity:0.75, + // | fontSize:"13pt" + // | }); + + var n = byId(node), args = arguments.length, op = (style == "opacity"); + style = _floatAliases[style] || style; + if(args == 3){ + return op ? d._setOpacity(n, value) : n.style[style] = value; /*Number*/ + } + if(args == 2 && op){ + return d._getOpacity(n); + } + var s = gcs(n); + if(args == 2 && typeof style != "string"){ // inline'd type check + for(var x in style){ + d.style(node, x, style[x]); + } + return s; + } + return (args == 1) ? s : _toStyleValue(n, style, s[style] || n.style[style]); /* CSS2Properties||String||Number */ + } + + // ============================= + // Box Functions + // ============================= + + dojo._getPadExtents = function(/*DomNode*/n, /*Object*/computedStyle){ + // summary: + // Returns object with special values specifically useful for node + // fitting. + // description: + // Returns an object with `w`, `h`, `l`, `t` properties: + // | l/t = left/top padding (respectively) + // | w = the total of the left and right padding + // | h = the total of the top and bottom padding + // If 'node' has position, l/t forms the origin for child nodes. + // The w/h are used for calculating boxes. + // Normally application code will not need to invoke this + // directly, and will use the ...box... functions instead. + var + s = computedStyle||gcs(n), + l = px(n, s.paddingLeft), + t = px(n, s.paddingTop); + return { + l: l, + t: t, + w: l+px(n, s.paddingRight), + h: t+px(n, s.paddingBottom) + }; + } + + dojo._getBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){ + // summary: + // returns an object with properties useful for noting the border + // dimensions. + // description: + // * l/t = the sum of left/top border (respectively) + // * w = the sum of the left and right border + // * h = the sum of the top and bottom border + // + // The w/h are used for calculating boxes. + // Normally application code will not need to invoke this + // directly, and will use the ...box... functions instead. + var + ne = "none", + s = computedStyle||gcs(n), + bl = (s.borderLeftStyle != ne ? px(n, s.borderLeftWidth) : 0), + bt = (s.borderTopStyle != ne ? px(n, s.borderTopWidth) : 0); + return { + l: bl, + t: bt, + w: bl + (s.borderRightStyle!=ne ? px(n, s.borderRightWidth) : 0), + h: bt + (s.borderBottomStyle!=ne ? px(n, s.borderBottomWidth) : 0) + }; + } + + dojo._getPadBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){ + // summary: + // Returns object with properties useful for box fitting with + // regards to padding. + // description: + // * l/t = the sum of left/top padding and left/top border (respectively) + // * w = the sum of the left and right padding and border + // * h = the sum of the top and bottom padding and border + // + // The w/h are used for calculating boxes. + // Normally application code will not need to invoke this + // directly, and will use the ...box... functions instead. + var + s = computedStyle||gcs(n), + p = d._getPadExtents(n, s), + b = d._getBorderExtents(n, s); + return { + l: p.l + b.l, + t: p.t + b.t, + w: p.w + b.w, + h: p.h + b.h + }; + } + + dojo._getMarginExtents = function(n, computedStyle){ + // summary: + // returns object with properties useful for box fitting with + // regards to box margins (i.e., the outer-box). + // + // * l/t = marginLeft, marginTop, respectively + // * w = total width, margin inclusive + // * h = total height, margin inclusive + // + // The w/h are used for calculating boxes. + // Normally application code will not need to invoke this + // directly, and will use the ...box... functions instead. + var + s = computedStyle||gcs(n), + l = px(n, s.marginLeft), + t = px(n, s.marginTop), + r = px(n, s.marginRight), + b = px(n, s.marginBottom); + if(d.isWebKit && (s.position != "absolute")){ + // FIXME: Safari's version of the computed right margin + // is the space between our right edge and the right edge + // of our offsetParent. + // What we are looking for is the actual margin value as + // determined by CSS. + // Hack solution is to assume left/right margins are the same. + r = l; + } + return { + l: l, + t: t, + w: l+r, + h: t+b + }; + } + + // Box getters work in any box context because offsetWidth/clientWidth + // are invariant wrt box context + // + // They do *not* work for display: inline objects that have padding styles + // because the user agent ignores padding (it's bogus styling in any case) + // + // Be careful with IMGs because they are inline or block depending on + // browser and browser mode. + + // Although it would be easier to read, there are not separate versions of + // _getMarginBox for each browser because: + // 1. the branching is not expensive + // 2. factoring the shared code wastes cycles (function call overhead) + // 3. duplicating the shared code wastes bytes + + dojo._getMarginBox = function(/*DomNode*/node, /*Object*/computedStyle){ + // summary: + // returns an object that encodes the width, height, left and top + // positions of the node's margin box. + var s = computedStyle || gcs(node), me = d._getMarginExtents(node, s); + var l = node.offsetLeft - me.l, t = node.offsetTop - me.t, p = node.parentNode; + if(d.isMoz){ + // Mozilla: + // If offsetParent has a computed overflow != visible, the offsetLeft is decreased + // by the parent's border. + // We don't want to compute the parent's style, so instead we examine node's + // computed left/top which is more stable. + var sl = parseFloat(s.left), st = parseFloat(s.top); + if(!isNaN(sl) && !isNaN(st)){ + l = sl, t = st; + }else{ + // If child's computed left/top are not parseable as a number (e.g. "auto"), we + // have no choice but to examine the parent's computed style. + if(p && p.style){ + var pcs = gcs(p); + if(pcs.overflow != "visible"){ + var be = d._getBorderExtents(p, pcs); + l += be.l, t += be.t; + } + } + } + }else if(d.isOpera || (d.isIE > 7 && !d.isQuirks)){ + // On Opera and IE 8, offsetLeft/Top includes the parent's border + if(p){ + be = d._getBorderExtents(p); + l -= be.l; + t -= be.t; + } + } + return { + l: l, + t: t, + w: node.offsetWidth + me.w, + h: node.offsetHeight + me.h + }; + } + + dojo._getContentBox = function(node, computedStyle){ + // summary: + // Returns an object that encodes the width, height, left and top + // positions of the node's content box, irrespective of the + // current box model. + + // clientWidth/Height are important since the automatically account for scrollbars + // fallback to offsetWidth/Height for special cases (see #3378) + var s = computedStyle || gcs(node), + pe = d._getPadExtents(node, s), + be = d._getBorderExtents(node, s), + w = node.clientWidth, + h + ; + if(!w){ + w = node.offsetWidth, h = node.offsetHeight; + }else{ + h = node.clientHeight, be.w = be.h = 0; + } + // On Opera, offsetLeft includes the parent's border + if(d.isOpera){ pe.l += be.l; pe.t += be.t; }; + return { + l: pe.l, + t: pe.t, + w: w - pe.w - be.w, + h: h - pe.h - be.h + }; + } + + dojo._getBorderBox = function(node, computedStyle){ + var s = computedStyle || gcs(node), + pe = d._getPadExtents(node, s), + cb = d._getContentBox(node, s) + ; + return { + l: cb.l - pe.l, + t: cb.t - pe.t, + w: cb.w + pe.w, + h: cb.h + pe.h + }; + } + + // Box setters depend on box context because interpretation of width/height styles + // vary wrt box context. + // + // The value of dojo.boxModel is used to determine box context. + // dojo.boxModel can be set directly to change behavior. + // + // Beware of display: inline objects that have padding styles + // because the user agent ignores padding (it's a bogus setup anyway) + // + // Be careful with IMGs because they are inline or block depending on + // browser and browser mode. + // + // Elements other than DIV may have special quirks, like built-in + // margins or padding, or values not detectable via computedStyle. + // In particular, margins on TABLE do not seems to appear + // at all in computedStyle on Mozilla. + + dojo._setBox = function(/*DomNode*/node, /*Number?*/l, /*Number?*/t, /*Number?*/w, /*Number?*/h, /*String?*/u){ + // summary: + // sets width/height/left/top in the current (native) box-model + // dimentions. Uses the unit passed in u. + // node: + // DOM Node reference. Id string not supported for performance + // reasons. + // l: + // left offset from parent. + // t: + // top offset from parent. + // w: + // width in current box model. + // h: + // width in current box model. + // u: + // unit measure to use for other measures. Defaults to "px". + u = u || "px"; + var s = node.style; + if(!isNaN(l)){ s.left = l + u; } + if(!isNaN(t)){ s.top = t + u; } + if(w >= 0){ s.width = w + u; } + if(h >= 0){ s.height = h + u; } + } + + dojo._isButtonTag = function(/*DomNode*/node) { + // summary: + // True if the node is BUTTON or INPUT.type="button". + return node.tagName == "BUTTON" + || node.tagName=="INPUT" && (node.getAttribute("type")||'').toUpperCase() == "BUTTON"; // boolean + } + + dojo._usesBorderBox = function(/*DomNode*/node){ + // summary: + // True if the node uses border-box layout. + + // We could test the computed style of node to see if a particular box + // has been specified, but there are details and we choose not to bother. + + // TABLE and BUTTON (and INPUT type=button) are always border-box by default. + // If you have assigned a different box to either one via CSS then + // box functions will break. + + var n = node.tagName; + return d.boxModel=="border-box" || n=="TABLE" || d._isButtonTag(node); // boolean + } + + dojo._setContentSize = function(/*DomNode*/node, /*Number*/widthPx, /*Number*/heightPx, /*Object*/computedStyle){ + // summary: + // Sets the size of the node's contents, irrespective of margins, + // padding, or borders. + if(d._usesBorderBox(node)){ + var pb = d._getPadBorderExtents(node, computedStyle); + if(widthPx >= 0){ widthPx += pb.w; } + if(heightPx >= 0){ heightPx += pb.h; } + } + d._setBox(node, NaN, NaN, widthPx, heightPx); + } + + dojo._setMarginBox = function(/*DomNode*/node, /*Number?*/leftPx, /*Number?*/topPx, + /*Number?*/widthPx, /*Number?*/heightPx, + /*Object*/computedStyle){ + // summary: + // sets the size of the node's margin box and placement + // (left/top), irrespective of box model. Think of it as a + // passthrough to dojo._setBox that handles box-model vagaries for + // you. + + var s = computedStyle || gcs(node), + // Some elements have special padding, margin, and box-model settings. + // To use box functions you may need to set padding, margin explicitly. + // Controlling box-model is harder, in a pinch you might set dojo.boxModel. + bb = d._usesBorderBox(node), + pb = bb ? _nilExtents : d._getPadBorderExtents(node, s) + ; + if(d.isWebKit){ + // on Safari (3.1.2), button nodes with no explicit size have a default margin + // setting an explicit size eliminates the margin. + // We have to swizzle the width to get correct margin reading. + if(d._isButtonTag(node)){ + var ns = node.style; + if(widthPx >= 0 && !ns.width) { ns.width = "4px"; } + if(heightPx >= 0 && !ns.height) { ns.height = "4px"; } + } + } + var mb = d._getMarginExtents(node, s); + if(widthPx >= 0){ widthPx = Math.max(widthPx - pb.w - mb.w, 0); } + if(heightPx >= 0){ heightPx = Math.max(heightPx - pb.h - mb.h, 0); } + d._setBox(node, leftPx, topPx, widthPx, heightPx); + } + + var _nilExtents = { l:0, t:0, w:0, h:0 }; + + // public API + + dojo.marginBox = function(/*DomNode|String*/node, /*Object?*/box){ + // summary: + // Getter/setter for the margin-box of node. + // description: + // Getter/setter for the margin-box of node. + // Returns an object in the expected format of box (regardless + // if box is passed). The object might look like: + // `{ l: 50, t: 200, w: 300: h: 150 }` + // for a node offset from its parent 50px to the left, 200px from + // the top with a margin width of 300px and a margin-height of + // 150px. + // node: + // id or reference to DOM Node to get/set box for + // box: + // If passed, denotes that dojo.marginBox() should + // update/set the margin box for node. Box is an object in the + // above format. All properties are optional if passed. + // example: + // Retrieve the marginbox of a passed node + // | var box = dojo.marginBox("someNodeId"); + // | console.dir(box); + // + // example: + // Set a node's marginbox to the size of another node + // | var box = dojo.marginBox("someNodeId"); + // | dojo.marginBox("someOtherNode", box); + + var n = byId(node), s = gcs(n), b = box; + return !b ? d._getMarginBox(n, s) : d._setMarginBox(n, b.l, b.t, b.w, b.h, s); // Object + } + + dojo.contentBox = function(/*DomNode|String*/node, /*Object?*/box){ + // summary: + // Getter/setter for the content-box of node. + // description: + // Returns an object in the expected format of box (regardless if box is passed). + // The object might look like: + // `{ l: 50, t: 200, w: 300: h: 150 }` + // for a node offset from its parent 50px to the left, 200px from + // the top with a content width of 300px and a content-height of + // 150px. Note that the content box may have a much larger border + // or margin box, depending on the box model currently in use and + // CSS values set/inherited for node. + // While the getter will return top and left values, the + // setter only accepts setting the width and height. + // node: + // id or reference to DOM Node to get/set box for + // box: + // If passed, denotes that dojo.contentBox() should + // update/set the content box for node. Box is an object in the + // above format, but only w (width) and h (height) are supported. + // All properties are optional if passed. + var n = byId(node), s = gcs(n), b = box; + return !b ? d._getContentBox(n, s) : d._setContentSize(n, b.w, b.h, s); // Object + } + + // ============================= + // Positioning + // ============================= + + var _sumAncestorProperties = function(node, prop){ + if(!(node = (node||0).parentNode)){return 0} + var val, retVal = 0, _b = d.body(); + while(node && node.style){ + if(gcs(node).position == "fixed"){ + return 0; + } + val = node[prop]; + if(val){ + retVal += val - 0; + // opera and khtml #body & #html has the same values, we only + // need one value + if(node == _b){ break; } + } + node = node.parentNode; + } + return retVal; // integer + } + + dojo._docScroll = function(){ + var n = d.global; + return "pageXOffset" in n? { x:n.pageXOffset, y:n.pageYOffset } : + (n=d.doc.documentElement, n.clientHeight? { x:d._fixIeBiDiScrollLeft(n.scrollLeft), y:n.scrollTop } : + (n=d.body(), { x:n.scrollLeft||0, y:n.scrollTop||0 })); + }; + + dojo._isBodyLtr = function(){ + return "_bodyLtr" in d? d._bodyLtr : + d._bodyLtr = (d.body().dir || d.doc.documentElement.dir || "ltr").toLowerCase() == "ltr"; // Boolean + } + + dojo._getIeDocumentElementOffset = function(){ + // summary: + // returns the offset in x and y from the document body to the + // visual edge of the page + // description: + // The following values in IE contain an offset: + // | event.clientX + // | event.clientY + // | node.getBoundingClientRect().left + // | node.getBoundingClientRect().top + // But other position related values do not contain this offset, + // such as node.offsetLeft, node.offsetTop, node.style.left and + // node.style.top. The offset is always (2, 2) in LTR direction. + // When the body is in RTL direction, the offset counts the width + // of left scroll bar's width. This function computes the actual + // offset. + + //NOTE: assumes we're being called in an IE browser + + var de = d.doc.documentElement; // only deal with HTML element here, _abs handles body/quirks + + if(d.isIE < 8){ + var r = de.getBoundingClientRect(); // works well for IE6+ + //console.debug('rect left,top = ' + r.left+','+r.top + ', html client left/top = ' + de.clientLeft+','+de.clientTop + ', rtl = ' + (!d._isBodyLtr()) + ', quirks = ' + d.isQuirks); + var l = r.left, + t = r.top; + if(d.isIE < 7){ + l += de.clientLeft; // scrollbar size in strict/RTL, or, + t += de.clientTop; // HTML border size in strict + } + return { + x: l < 0? 0 : l, // FRAME element border size can lead to inaccurate negative values + y: t < 0? 0 : t + }; + }else{ + return { + x: 0, + y: 0 + }; + } + + }; + + dojo._fixIeBiDiScrollLeft = function(/*Integer*/ scrollLeft){ + // In RTL direction, scrollLeft should be a negative value, but IE < 8 + // returns a positive one. All codes using documentElement.scrollLeft + // must call this function to fix this error, otherwise the position + // will offset to right when there is a horizontal scrollbar. + + var dd = d.doc; + if(d.isIE < 8 && !d._isBodyLtr()){ + var de = d.isQuirks ? dd.body : dd.documentElement; + return scrollLeft + de.clientWidth - de.scrollWidth; // Integer + } + return scrollLeft; // Integer + } + + // FIXME: need a setter for coords or a moveTo!! + dojo._abs = dojo.position = function(/*DomNode*/node, /*Boolean?*/includeScroll){ + // summary: + // Gets the position and size of the passed element relative to + // the viewport (if includeScroll==false), or relative to the + // document root (if includeScroll==true). + // + // description: + // Returns an object of the form: + // { x: 100, y: 300, w: 20, h: 15 } + // If includeScroll==true, the x and y values will include any + // document offsets that may affect the position relative to the + // viewport. + // Uses the border-box model (inclusive of border and padding but + // not margin). Does not act as a setter. + + var db = d.body(), dh = db.parentNode, ret; + node = byId(node); + if(node["getBoundingClientRect"]){ + // IE6+, FF3+, super-modern WebKit, and Opera 9.6+ all take this branch + ret = node.getBoundingClientRect(); + ret = { x: ret.left, y: ret.top, w: ret.right - ret.left, h: ret.bottom - ret.top }; + if(d.isIE){ + // On IE there's a 2px offset that we need to adjust for, see _getIeDocumentElementOffset() + var offset = d._getIeDocumentElementOffset(); + + // fixes the position in IE, quirks mode + ret.x -= offset.x + (d.isQuirks ? db.clientLeft+db.offsetLeft : 0); + ret.y -= offset.y + (d.isQuirks ? db.clientTop+db.offsetTop : 0); + }else if(d.isFF == 3){ + // In FF3 you have to subtract the document element margins. + // Fixed in FF3.5 though. + var cs = gcs(dh); + ret.x -= px(dh, cs.marginLeft) + px(dh, cs.borderLeftWidth); + ret.y -= px(dh, cs.marginTop) + px(dh, cs.borderTopWidth); + } + }else{ + // FF2 and older WebKit + ret = { + x: 0, + y: 0, + w: node.offsetWidth, + h: node.offsetHeight + }; + if(node["offsetParent"]){ + ret.x -= _sumAncestorProperties(node, "scrollLeft"); + ret.y -= _sumAncestorProperties(node, "scrollTop"); + + var curnode = node; + do{ + var n = curnode.offsetLeft, + t = curnode.offsetTop; + ret.x += isNaN(n) ? 0 : n; + ret.y += isNaN(t) ? 0 : t; + + cs = gcs(curnode); + if(curnode != node){ + if(d.isMoz){ + // tried left+right with differently sized left/right borders + // it really is 2xleft border in FF, not left+right, even in RTL! + ret.x += 2 * px(curnode,cs.borderLeftWidth); + ret.y += 2 * px(curnode,cs.borderTopWidth); + }else{ + ret.x += px(curnode, cs.borderLeftWidth); + ret.y += px(curnode, cs.borderTopWidth); + } + } + // static children in a static div in FF2 are affected by the div's border as well + // but offsetParent will skip this div! + if(d.isMoz && cs.position=="static"){ + var parent=curnode.parentNode; + while(parent!=curnode.offsetParent){ + var pcs=gcs(parent); + if(pcs.position=="static"){ + ret.x += px(curnode,pcs.borderLeftWidth); + ret.y += px(curnode,pcs.borderTopWidth); + } + parent=parent.parentNode; + } + } + curnode = curnode.offsetParent; + }while((curnode != dh) && curnode); + }else if(node.x && node.y){ + ret.x += isNaN(node.x) ? 0 : node.x; + ret.y += isNaN(node.y) ? 0 : node.y; + } + } + // account for document scrolling + // if offsetParent is used, ret value already includes scroll position + // so we may have to actually remove that value if !includeScroll + if(includeScroll){ + var scroll = d._docScroll(); + ret.x += scroll.x; + ret.y += scroll.y; + } + + return ret; // Object + } + + dojo.coords = function(/*DomNode|String*/node, /*Boolean?*/includeScroll){ + // summary: + // Deprecated: Use position() for border-box x/y/w/h + // or marginBox() for margin-box w/h/l/t. + // Returns an object representing a node's size and position. + // + // description: + // Returns an object that measures margin-box (w)idth/(h)eight + // and absolute position x/y of the border-box. Also returned + // is computed (l)eft and (t)op values in pixels from the + // node's offsetParent as returned from marginBox(). + // Return value will be in the form: + //| { l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 } + // Does not act as a setter. If includeScroll is passed, the x and + // y params are affected as one would expect in dojo.position(). + var n = byId(node), s = gcs(n), mb = d._getMarginBox(n, s); + var abs = d.position(n, includeScroll); + mb.x = abs.x; + mb.y = abs.y; + return mb; + } + + // ============================= + // Element attribute Functions + // ============================= + + // dojo.attr() should conform to http://www.w3.org/TR/DOM-Level-2-Core/ + + var _propNames = { + // properties renamed to avoid clashes with reserved words + "class": "className", + "for": "htmlFor", + // properties written as camelCase + tabindex: "tabIndex", + readonly: "readOnly", + colspan: "colSpan", + frameborder: "frameBorder", + rowspan: "rowSpan", + valuetype: "valueType" + }, + _attrNames = { + // original attribute names + classname: "class", + htmlfor: "for", + // for IE + tabindex: "tabIndex", + readonly: "readOnly" + }, + _forcePropNames = { + innerHTML: 1, + className: 1, + htmlFor: d.isIE, + value: 1 + }; + + var _fixAttrName = function(/*String*/ name){ + return _attrNames[name.toLowerCase()] || name; + }; + + var _hasAttr = function(node, name){ + var attr = node.getAttributeNode && node.getAttributeNode(name); + return attr && attr.specified; // Boolean + }; + + // There is a difference in the presence of certain properties and their default values + // between browsers. For example, on IE "disabled" is present on all elements, + // but it is value is "false"; "tabIndex" of <div> returns 0 by default on IE, yet other browsers + // can return -1. + + dojo.hasAttr = function(/*DomNode|String*/node, /*String*/name){ + // summary: + // Returns true if the requested attribute is specified on the + // given element, and false otherwise. + // node: + // id or reference to the element to check + // name: + // the name of the attribute + // returns: + // true if the requested attribute is specified on the + // given element, and false otherwise + var lc = name.toLowerCase(); + return _forcePropNames[_propNames[lc] || name] || _hasAttr(byId(node), _attrNames[lc] || name); // Boolean + } + + var _evtHdlrMap = {}, _ctr = 0, + _attrId = dojo._scopeName + "attrid", + // the next dictionary lists elements with read-only innerHTML on IE + _roInnerHtml = {col: 1, colgroup: 1, + // frameset: 1, head: 1, html: 1, style: 1, + table: 1, tbody: 1, tfoot: 1, thead: 1, tr: 1, title: 1}; + + dojo.attr = function(/*DomNode|String*/node, /*String|Object*/name, /*String?*/value){ + // summary: + // Gets or sets an attribute on an HTML element. + // description: + // Handles normalized getting and setting of attributes on DOM + // Nodes. If 2 arguments are passed, and a the second argumnt is a + // string, acts as a getter. + // + // If a third argument is passed, or if the second argument is a + // map of attributes, acts as a setter. + // + // When passing functions as values, note that they will not be + // directly assigned to slots on the node, but rather the default + // behavior will be removed and the new behavior will be added + // using `dojo.connect()`, meaning that event handler properties + // will be normalized and that some caveats with regards to + // non-standard behaviors for onsubmit apply. Namely that you + // should cancel form submission using `dojo.stopEvent()` on the + // passed event object instead of returning a boolean value from + // the handler itself. + // node: + // id or reference to the element to get or set the attribute on + // name: + // the name of the attribute to get or set. + // value: + // The value to set for the attribute + // returns: + // when used as a getter, the value of the requested attribute + // or null if that attribute does not have a specified or + // default value; + // + // when used as a setter, the DOM node + // + // example: + // | // get the current value of the "foo" attribute on a node + // | dojo.attr(dojo.byId("nodeId"), "foo"); + // | // or we can just pass the id: + // | dojo.attr("nodeId", "foo"); + // + // example: + // | // use attr() to set the tab index + // | dojo.attr("nodeId", "tabIndex", 3); + // | + // + // example: + // Set multiple values at once, including event handlers: + // | dojo.attr("formId", { + // | "foo": "bar", + // | "tabIndex": -1, + // | "method": "POST", + // | "onsubmit": function(e){ + // | // stop submitting the form. Note that the IE behavior + // | // of returning true or false will have no effect here + // | // since our handler is connect()ed to the built-in + // | // onsubmit behavior and so we need to use + // | // dojo.stopEvent() to ensure that the submission + // | // doesn't proceed. + // | dojo.stopEvent(e); + // | + // | // submit the form with Ajax + // | dojo.xhrPost({ form: "formId" }); + // | } + // | }); + // + // example: + // Style is s special case: Only set with an object hash of styles + // | dojo.attr("someNode",{ + // | id:"bar", + // | style:{ + // | width:"200px", height:"100px", color:"#000" + // | } + // | }); + // + // example: + // Again, only set style as an object hash of styles: + // | var obj = { color:"#fff", backgroundColor:"#000" }; + // | dojo.attr("someNode", "style", obj); + // | + // | // though shorter to use `dojo.style()` in this case: + // | dojo.style("someNode", obj); + + node = byId(node); + var args = arguments.length, prop; + if(args == 2 && typeof name != "string"){ // inline'd type check + // the object form of setter: the 2nd argument is a dictionary + for(var x in name){ + d.attr(node, x, name[x]); + } + return node; // DomNode + } + var lc = name.toLowerCase(), + propName = _propNames[lc] || name, + forceProp = _forcePropNames[propName], + attrName = _attrNames[lc] || name; + if(args == 3){ + // setter + do{ + if(propName == "style" && typeof value != "string"){ // inline'd type check + // special case: setting a style + d.style(node, value); + break; + } + if(propName == "innerHTML"){ + // special case: assigning HTML + if(d.isIE && node.tagName.toLowerCase() in _roInnerHtml){ + d.empty(node); + node.appendChild(d._toDom(value, node.ownerDocument)); + }else{ + node[propName] = value; + } + break; + } + if(d.isFunction(value)){ + // special case: assigning an event handler + // clobber if we can + var attrId = d.attr(node, _attrId); + if(!attrId){ + attrId = _ctr++; + d.attr(node, _attrId, attrId); + } + if(!_evtHdlrMap[attrId]){ + _evtHdlrMap[attrId] = {}; + } + var h = _evtHdlrMap[attrId][propName]; + if(h){ + d.disconnect(h); + }else{ + try{ + delete node[propName]; + }catch(e){} + } + // ensure that event objects are normalized, etc. + _evtHdlrMap[attrId][propName] = d.connect(node, propName, value); + break; + } + if(forceProp || typeof value == "boolean"){ + // special case: forcing assignment to the property + // special case: setting boolean to a property instead of attribute + node[propName] = value; + break; + } + // node's attribute + node.setAttribute(attrName, value); + }while(false); + return node; // DomNode + } + // getter + // should we access this attribute via a property or + // via getAttribute()? + value = node[propName]; + if(forceProp && typeof value != "undefined"){ + // node's property + return value; // Anything + } + if(propName != "href" && (typeof value == "boolean" || d.isFunction(value))){ + // node's property + return value; // Anything + } + // node's attribute + // we need _hasAttr() here to guard against IE returning a default value + return _hasAttr(node, attrName) ? node.getAttribute(attrName) : null; // Anything + } + + dojo.removeAttr = function(/*DomNode|String*/ node, /*String*/ name){ + // summary: + // Removes an attribute from an HTML element. + // node: + // id or reference to the element to remove the attribute from + // name: + // the name of the attribute to remove + byId(node).removeAttribute(_fixAttrName(name)); + } + + dojo.getNodeProp = function(/*DomNode|String*/ node, /*String*/ name){ + // summary: + // Returns an effective value of a property or an attribute. + // node: + // id or reference to the element to remove the attribute from + // name: + // the name of the attribute + node = byId(node); + var lc = name.toLowerCase(), + propName = _propNames[lc] || name; + if((propName in node) && propName != "href"){ + // node's property + return node[propName]; // Anything + } + // node's attribute + var attrName = _attrNames[lc] || name; + return _hasAttr(node, attrName) ? node.getAttribute(attrName) : null; // Anything + } + + dojo.create = function(tag, attrs, refNode, pos){ + // summary: + // Create an element, allowing for optional attribute decoration + // and placement. + // + // description: + // A DOM Element creation function. A shorthand method for creating a node or + // a fragment, and allowing for a convenient optional attribute setting step, + // as well as an optional DOM placement reference. + //| + // Attributes are set by passing the optional object through `dojo.attr`. + // See `dojo.attr` for noted caveats and nuances, and API if applicable. + //| + // Placement is done via `dojo.place`, assuming the new node to be the action + // node, passing along the optional reference node and position. + // + // tag: String|DomNode + // A string of the element to create (eg: "div", "a", "p", "li", "script", "br"), + // or an existing DOM node to process. + // + // attrs: Object + // An object-hash of attributes to set on the newly created node. + // Can be null, if you don't want to set any attributes/styles. + // See: `dojo.attr` for a description of available attributes. + // + // refNode: String?|DomNode? + // Optional reference node. Used by `dojo.place` to place the newly created + // node somewhere in the dom relative to refNode. Can be a DomNode reference + // or String ID of a node. + // + // pos: String? + // Optional positional reference. Defaults to "last" by way of `dojo.place`, + // though can be set to "first","after","before","last", "replace" or "only" + // to further control the placement of the new node relative to the refNode. + // 'refNode' is required if a 'pos' is specified. + // + // returns: DomNode + // + // example: + // Create a DIV: + // | var n = dojo.create("div"); + // + // example: + // Create a DIV with content: + // | var n = dojo.create("div", { innerHTML:"<p>hi</p>" }); + // + // example: + // Place a new DIV in the BODY, with no attributes set + // | var n = dojo.create("div", null, dojo.body()); + // + // example: + // Create an UL, and populate it with LI's. Place the list as the first-child of a + // node with id="someId": + // | var ul = dojo.create("ul", null, "someId", "first"); + // | var items = ["one", "two", "three", "four"]; + // | dojo.forEach(items, function(data){ + // | dojo.create("li", { innerHTML: data }, ul); + // | }); + // + // example: + // Create an anchor, with an href. Place in BODY: + // | dojo.create("a", { href:"foo.html", title:"Goto FOO!" }, dojo.body()); + // + // example: + // Create a `dojo.NodeList()` from a new element (for syntatic sugar): + // | dojo.query(dojo.create('div')) + // | .addClass("newDiv") + // | .onclick(function(e){ console.log('clicked', e.target) }) + // | .place("#someNode"); // redundant, but cleaner. + + var doc = d.doc; + if(refNode){ + refNode = byId(refNode); + doc = refNode.ownerDocument; + } + if(typeof tag == "string"){ // inline'd type check + tag = doc.createElement(tag); + } + if(attrs){ d.attr(tag, attrs); } + if(refNode){ d.place(tag, refNode, pos); } + return tag; // DomNode + } + + /*===== + dojo.empty = function(node){ + // summary: + // safely removes all children of the node. + // node: DOMNode|String + // a reference to a DOM node or an id. + // example: + // Destroy node's children byId: + // | dojo.empty("someId"); + // + // example: + // Destroy all nodes' children in a list by reference: + // | dojo.query(".someNode").forEach(dojo.empty); + } + =====*/ + + d.empty = + d.isIE ? function(node){ + node = byId(node); + for(var c; c = node.lastChild;){ // intentional assignment + d.destroy(c); + } + } : + function(node){ + byId(node).innerHTML = ""; + }; + + /*===== + dojo._toDom = function(frag, doc){ + // summary: + // instantiates an HTML fragment returning the corresponding DOM. + // frag: String + // the HTML fragment + // doc: DocumentNode? + // optional document to use when creating DOM nodes, defaults to + // dojo.doc if not specified. + // returns: DocumentFragment + // + // example: + // Create a table row: + // | var tr = dojo._toDom("<tr><td>First!</td></tr>"); + } + =====*/ + + // support stuff for dojo._toDom + var tagWrap = { + option: ["select"], + tbody: ["table"], + thead: ["table"], + tfoot: ["table"], + tr: ["table", "tbody"], + td: ["table", "tbody", "tr"], + th: ["table", "thead", "tr"], + legend: ["fieldset"], + caption: ["table"], + colgroup: ["table"], + col: ["table", "colgroup"], + li: ["ul"] + }, + reTag = /<\s*([\w\:]+)/, + masterNode = {}, masterNum = 0, + masterName = "__" + d._scopeName + "ToDomId"; + + // generate start/end tag strings to use + // for the injection for each special tag wrap case. + for(var param in tagWrap){ + var tw = tagWrap[param]; + tw.pre = param == "option" ? '<select multiple="multiple">' : "<" + tw.join("><") + ">"; + tw.post = "</" + tw.reverse().join("></") + ">"; + // the last line is destructive: it reverses the array, + // but we don't care at this point + } + + d._toDom = function(frag, doc){ + // summary: + // converts HTML string into DOM nodes. + + doc = doc || d.doc; + var masterId = doc[masterName]; + if(!masterId){ + doc[masterName] = masterId = ++masterNum + ""; + masterNode[masterId] = doc.createElement("div"); + } + + // make sure the frag is a string. + frag += ""; + + // find the starting tag, and get node wrapper + var match = frag.match(reTag), + tag = match ? match[1].toLowerCase() : "", + master = masterNode[masterId], + wrap, i, fc, df; + if(match && tagWrap[tag]){ + wrap = tagWrap[tag]; + master.innerHTML = wrap.pre + frag + wrap.post; + for(i = wrap.length; i; --i){ + master = master.firstChild; + } + }else{ + master.innerHTML = frag; + } + + // one node shortcut => return the node itself + if(master.childNodes.length == 1){ + return master.removeChild(master.firstChild); // DOMNode + } + + // return multiple nodes as a document fragment + df = doc.createDocumentFragment(); + while(fc = master.firstChild){ // intentional assignment + df.appendChild(fc); + } + return df; // DOMNode + } + + // ============================= + // (CSS) Class Functions + // ============================= + var _className = "className"; + + dojo.hasClass = function(/*DomNode|String*/node, /*String*/classStr){ + // summary: + // Returns whether or not the specified classes are a portion of the + // class list currently applied to the node. + // + // node: + // String ID or DomNode reference to check the class for. + // + // classStr: + // A string class name to look for. + // + // example: + // Do something if a node with id="someNode" has class="aSillyClassName" present + // | if(dojo.hasClass("someNode","aSillyClassName")){ ... } + + return ((" "+ byId(node)[_className] +" ").indexOf(" " + classStr + " ") >= 0); // Boolean + }; + + var spaces = /\s+/, a1 = [""], + str2array = function(s){ + if(typeof s == "string" || s instanceof String){ + if(s.indexOf(" ") < 0){ + a1[0] = s; + return a1; + }else{ + return s.split(spaces); + } + } + // assumed to be an array + return s || ""; + }; + + dojo.addClass = function(/*DomNode|String*/node, /*String|Array*/classStr){ + // summary: + // Adds the specified classes to the end of the class list on the + // passed node. Will not re-apply duplicate classes. + // + // node: + // String ID or DomNode reference to add a class string too + // + // classStr: + // A String class name to add, or several space-separated class names, + // or an array of class names. + // + // example: + // Add a class to some node: + // | dojo.addClass("someNode", "anewClass"); + // + // example: + // Add two classes at once: + // | dojo.addClass("someNode", "firstClass secondClass"); + // + // example: + // Add two classes at once (using array): + // | dojo.addClass("someNode", ["firstClass", "secondClass"]); + // + // example: + // Available in `dojo.NodeList` for multiple additions + // | dojo.query("ul > li").addClass("firstLevel"); + + node = byId(node); + classStr = str2array(classStr); + var cls = node[_className], oldLen; + cls = cls ? " " + cls + " " : " "; + oldLen = cls.length; + for(var i = 0, len = classStr.length, c; i < len; ++i){ + c = classStr[i]; + if(c && cls.indexOf(" " + c + " ") < 0){ + cls += c + " "; + } + } + if(oldLen < cls.length){ + node[_className] = cls.substr(1, cls.length - 2); + } + }; + + dojo.removeClass = function(/*DomNode|String*/node, /*String|Array?*/classStr){ + // summary: + // Removes the specified classes from node. No `dojo.hasClass` + // check is required. + // + // node: + // String ID or DomNode reference to remove the class from. + // + // classStr: + // An optional String class name to remove, or several space-separated + // class names, or an array of class names. If omitted, all class names + // will be deleted. + // + // example: + // Remove a class from some node: + // | dojo.removeClass("someNode", "firstClass"); + // + // example: + // Remove two classes from some node: + // | dojo.removeClass("someNode", "firstClass secondClass"); + // + // example: + // Remove two classes from some node (using array): + // | dojo.removeClass("someNode", ["firstClass", "secondClass"]); + // + // example: + // Remove all classes from some node: + // | dojo.removeClass("someNode"); + // + // example: + // Available in `dojo.NodeList()` for multiple removal + // | dojo.query(".foo").removeClass("foo"); + + node = byId(node); + var cls; + if(classStr !== undefined){ + classStr = str2array(classStr); + cls = " " + node[_className] + " "; + for(var i = 0, len = classStr.length; i < len; ++i){ + cls = cls.replace(" " + classStr[i] + " ", " "); + } + cls = d.trim(cls); + }else{ + cls = ""; + } + if(node[_className] != cls){ node[_className] = cls; } + }; + + dojo.toggleClass = function(/*DomNode|String*/node, /*String|Array*/classStr, /*Boolean?*/condition){ + // summary: + // Adds a class to node if not present, or removes if present. + // Pass a boolean condition if you want to explicitly add or remove. + // condition: + // If passed, true means to add the class, false means to remove. + // + // example: + // | dojo.toggleClass("someNode", "hovered"); + // + // example: + // Forcefully add a class + // | dojo.toggleClass("someNode", "hovered", true); + // + // example: + // Available in `dojo.NodeList()` for multiple toggles + // | dojo.query(".toggleMe").toggleClass("toggleMe"); + + if(condition === undefined){ + condition = !d.hasClass(node, classStr); + } + d[condition ? "addClass" : "removeClass"](node, classStr); + }; + })(); + } |