html.js 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojo._base.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojo._base.html"] = true;
  8. dojo.provide("dojo._base.html");
  9. dojo.require("dojo._base.lang");
  10. // FIXME: need to add unit tests for all the semi-public methods
  11. try{
  12. document.execCommand("BackgroundImageCache", false, true);
  13. }catch(e){
  14. // sane browsers don't have cache "issues"
  15. }
  16. // =============================
  17. // DOM Functions
  18. // =============================
  19. /*=====
  20. dojo.byId = function(id, doc){
  21. // summary:
  22. // Returns DOM node with matching `id` attribute or `null`
  23. // if not found. If `id` is a DomNode, this function is a no-op.
  24. //
  25. // id: String|DOMNode
  26. // A string to match an HTML id attribute or a reference to a DOM Node
  27. //
  28. // doc: Document?
  29. // Document to work in. Defaults to the current value of
  30. // dojo.doc. Can be used to retrieve
  31. // node references from other documents.
  32. //
  33. // example:
  34. // Look up a node by ID:
  35. // | var n = dojo.byId("foo");
  36. //
  37. // example:
  38. // Check if a node exists, and use it.
  39. // | var n = dojo.byId("bar");
  40. // | if(n){ doStuff() ... }
  41. //
  42. // example:
  43. // Allow string or DomNode references to be passed to a custom function:
  44. // | var foo = function(nodeOrId){
  45. // | nodeOrId = dojo.byId(nodeOrId);
  46. // | // ... more stuff
  47. // | }
  48. =====*/
  49. if(dojo.isIE){
  50. dojo.byId = function(id, doc){
  51. if(typeof id != "string"){
  52. return id;
  53. }
  54. var _d = doc || dojo.doc, te = _d.getElementById(id);
  55. // attributes.id.value is better than just id in case the
  56. // user has a name=id inside a form
  57. if(te && (te.attributes.id.value == id || te.id == id)){
  58. return te;
  59. }else{
  60. var eles = _d.all[id];
  61. if(!eles || eles.nodeName){
  62. eles = [eles];
  63. }
  64. // if more than 1, choose first with the correct id
  65. var i=0;
  66. while((te=eles[i++])){
  67. if((te.attributes && te.attributes.id && te.attributes.id.value == id)
  68. || te.id == id){
  69. return te;
  70. }
  71. }
  72. }
  73. };
  74. }else{
  75. dojo.byId = function(id, doc){
  76. // inline'd type check.
  77. // be sure to return null per documentation, to match IE branch.
  78. return ((typeof id == "string") ? (doc || dojo.doc).getElementById(id) : id) || null; // DomNode
  79. };
  80. }
  81. /*=====
  82. };
  83. =====*/
  84. (function(){
  85. var d = dojo;
  86. var byId = d.byId;
  87. /*=====
  88. dojo._destroyElement = function(node){
  89. // summary:
  90. // Existing alias for `dojo.destroy`. Deprecated, will be removed
  91. // in 2.0
  92. }
  93. =====*/
  94. function _destroy(/*DomNode*/ node, /*DomNode*/ parent){
  95. if(node.firstChild){
  96. _empty(node);
  97. }
  98. if(parent){
  99. // removeNode(false) doesn't leak in IE 6+, but removeChild() and removeNode(true) are known to leak under IE 8- while 9+ is TBD
  100. d.isIE && parent.canHaveChildren && 'removeNode' in node ? node.removeNode(false) :
  101. parent.removeChild(node);
  102. }
  103. }
  104. dojo._destroyElement = dojo.destroy = function(/*String|DomNode*/node){
  105. // summary:
  106. // Removes a node from its parent, clobbering it and all of its
  107. // children.
  108. //
  109. // description:
  110. // Removes a node from its parent, clobbering it and all of its
  111. // children. Function only works with DomNodes, and returns nothing.
  112. //
  113. // node:
  114. // A String ID or DomNode reference of the element to be destroyed
  115. //
  116. // example:
  117. // Destroy a node byId:
  118. // | dojo.destroy("someId");
  119. //
  120. // example:
  121. // Destroy all nodes in a list by reference:
  122. // | dojo.query(".someNode").forEach(dojo.destroy);
  123. node = byId(node);
  124. if(!node){ return; }
  125. _destroy(node, node.parentNode);
  126. };
  127. dojo.isDescendant = function(/*DomNode|String*/node, /*DomNode|String*/ancestor){
  128. // summary:
  129. // Returns true if node is a descendant of ancestor
  130. // node: string id or node reference to test
  131. // ancestor: string id or node reference of potential parent to test against
  132. //
  133. // example:
  134. // Test is node id="bar" is a descendant of node id="foo"
  135. // | if(dojo.isDescendant("bar", "foo")){ ... }
  136. try{
  137. node = byId(node);
  138. ancestor = byId(ancestor);
  139. while(node){
  140. if(node == ancestor){
  141. return true; // Boolean
  142. }
  143. node = node.parentNode;
  144. }
  145. }catch(e){ /* squelch, return false */ }
  146. return false; // Boolean
  147. };
  148. dojo.setSelectable = function(/*DomNode|String*/node, /*Boolean*/selectable){
  149. // summary:
  150. // Enable or disable selection on a node
  151. // node:
  152. // id or reference to node
  153. // selectable:
  154. // state to put the node in. false indicates unselectable, true
  155. // allows selection.
  156. // example:
  157. // Make the node id="bar" unselectable
  158. // | dojo.setSelectable("bar");
  159. // example:
  160. // Make the node id="bar" selectable
  161. // | dojo.setSelectable("bar", true);
  162. node = byId(node);
  163. if(d.isMozilla){
  164. node.style.MozUserSelect = selectable ? "" : "none";
  165. }else if(d.isKhtml || d.isWebKit){
  166. node.style.KhtmlUserSelect = selectable ? "auto" : "none";
  167. }else if(d.isIE){
  168. var v = (node.unselectable = selectable ? "" : "on");
  169. d.query("*", node).forEach("item.unselectable = '"+v+"'");
  170. }
  171. //FIXME: else? Opera?
  172. };
  173. var _insertBefore = function(/*DomNode*/node, /*DomNode*/ref){
  174. var parent = ref.parentNode;
  175. if(parent){
  176. parent.insertBefore(node, ref);
  177. }
  178. };
  179. var _insertAfter = function(/*DomNode*/node, /*DomNode*/ref){
  180. // summary:
  181. // Try to insert node after ref
  182. var parent = ref.parentNode;
  183. if(parent){
  184. if(parent.lastChild == ref){
  185. parent.appendChild(node);
  186. }else{
  187. parent.insertBefore(node, ref.nextSibling);
  188. }
  189. }
  190. };
  191. dojo.place = function(node, refNode, position){
  192. // summary:
  193. // Attempt to insert node into the DOM, choosing from various positioning options.
  194. // Returns the first argument resolved to a DOM node.
  195. //
  196. // node: String|DomNode
  197. // id or node reference, or HTML fragment starting with "<" to place relative to refNode
  198. //
  199. // refNode: String|DomNode
  200. // id or node reference to use as basis for placement
  201. //
  202. // position: String|Number?
  203. // string noting the position of node relative to refNode or a
  204. // number indicating the location in the childNodes collection of refNode.
  205. // Accepted string values are:
  206. // | * before
  207. // | * after
  208. // | * replace
  209. // | * only
  210. // | * first
  211. // | * last
  212. // "first" and "last" indicate positions as children of refNode, "replace" replaces refNode,
  213. // "only" replaces all children. position defaults to "last" if not specified
  214. //
  215. // returns: DomNode
  216. // Returned values is the first argument resolved to a DOM node.
  217. //
  218. // .place() is also a method of `dojo.NodeList`, allowing `dojo.query` node lookups.
  219. //
  220. // example:
  221. // Place a node by string id as the last child of another node by string id:
  222. // | dojo.place("someNode", "anotherNode");
  223. //
  224. // example:
  225. // Place a node by string id before another node by string id
  226. // | dojo.place("someNode", "anotherNode", "before");
  227. //
  228. // example:
  229. // Create a Node, and place it in the body element (last child):
  230. // | dojo.place("<div></div>", dojo.body());
  231. //
  232. // example:
  233. // Put a new LI as the first child of a list by id:
  234. // | dojo.place("<li></li>", "someUl", "first");
  235. refNode = byId(refNode);
  236. if(typeof node == "string"){ // inline'd type check
  237. node = /^\s*</.test(node) ? d._toDom(node, refNode.ownerDocument) : byId(node);
  238. }
  239. if(typeof position == "number"){ // inline'd type check
  240. var cn = refNode.childNodes;
  241. if(!cn.length || cn.length <= position){
  242. refNode.appendChild(node);
  243. }else{
  244. _insertBefore(node, cn[position < 0 ? 0 : position]);
  245. }
  246. }else{
  247. switch(position){
  248. case "before":
  249. _insertBefore(node, refNode);
  250. break;
  251. case "after":
  252. _insertAfter(node, refNode);
  253. break;
  254. case "replace":
  255. refNode.parentNode.replaceChild(node, refNode);
  256. break;
  257. case "only":
  258. d.empty(refNode);
  259. refNode.appendChild(node);
  260. break;
  261. case "first":
  262. if(refNode.firstChild){
  263. _insertBefore(node, refNode.firstChild);
  264. break;
  265. }
  266. // else fallthrough...
  267. default: // aka: last
  268. refNode.appendChild(node);
  269. }
  270. }
  271. return node; // DomNode
  272. };
  273. // Box functions will assume this model.
  274. // On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode.
  275. // Can be set to change behavior of box setters.
  276. // can be either:
  277. // "border-box"
  278. // "content-box" (default)
  279. dojo.boxModel = "content-box";
  280. // We punt per-node box mode testing completely.
  281. // If anybody cares, we can provide an additional (optional) unit
  282. // that overrides existing code to include per-node box sensitivity.
  283. // Opera documentation claims that Opera 9 uses border-box in BackCompat mode.
  284. // but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box.
  285. // IIRC, earlier versions of Opera did in fact use border-box.
  286. // Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault.
  287. if(d.isIE /*|| dojo.isOpera*/){
  288. // client code may have to adjust if compatMode varies across iframes
  289. d.boxModel = document.compatMode == "BackCompat" ? "border-box" : "content-box";
  290. }
  291. // =============================
  292. // Style Functions
  293. // =============================
  294. // getComputedStyle drives most of the style code.
  295. // Wherever possible, reuse the returned object.
  296. //
  297. // API functions below that need to access computed styles accept an
  298. // optional computedStyle parameter.
  299. // If this parameter is omitted, the functions will call getComputedStyle themselves.
  300. // This way, calling code can access computedStyle once, and then pass the reference to
  301. // multiple API functions.
  302. /*=====
  303. dojo.getComputedStyle = function(node){
  304. // summary:
  305. // Returns a "computed style" object.
  306. //
  307. // description:
  308. // Gets a "computed style" object which can be used to gather
  309. // information about the current state of the rendered node.
  310. //
  311. // Note that this may behave differently on different browsers.
  312. // Values may have different formats and value encodings across
  313. // browsers.
  314. //
  315. // Note also that this method is expensive. Wherever possible,
  316. // reuse the returned object.
  317. //
  318. // Use the dojo.style() method for more consistent (pixelized)
  319. // return values.
  320. //
  321. // node: DOMNode
  322. // A reference to a DOM node. Does NOT support taking an
  323. // ID string for speed reasons.
  324. // example:
  325. // | dojo.getComputedStyle(dojo.byId('foo')).borderWidth;
  326. //
  327. // example:
  328. // Reusing the returned object, avoiding multiple lookups:
  329. // | var cs = dojo.getComputedStyle(dojo.byId("someNode"));
  330. // | var w = cs.width, h = cs.height;
  331. return; // CSS2Properties
  332. }
  333. =====*/
  334. // Although we normally eschew argument validation at this
  335. // level, here we test argument 'node' for (duck)type,
  336. // by testing nodeType, ecause 'document' is the 'parentNode' of 'body'
  337. // it is frequently sent to this function even
  338. // though it is not Element.
  339. var gcs;
  340. if(d.isWebKit){
  341. gcs = function(/*DomNode*/node){
  342. var s;
  343. if(node.nodeType == 1){
  344. var dv = node.ownerDocument.defaultView;
  345. s = dv.getComputedStyle(node, null);
  346. if(!s && node.style){
  347. node.style.display = "";
  348. s = dv.getComputedStyle(node, null);
  349. }
  350. }
  351. return s || {};
  352. };
  353. }else if(d.isIE && (d.isIE < 9 || d.isQuirks)){
  354. gcs = function(node){
  355. // IE (as of 7) doesn't expose Element like sane browsers
  356. return node.nodeType == 1 /* ELEMENT_NODE*/ ? node.currentStyle : {};
  357. };
  358. }else{
  359. gcs = function(node){
  360. return node.nodeType == 1 ?
  361. node.ownerDocument.defaultView.getComputedStyle(node, null) : {};
  362. };
  363. }
  364. dojo.getComputedStyle = gcs;
  365. if(!d.isIE){
  366. d._toPixelValue = function(element, value){
  367. // style values can be floats, client code may want
  368. // to round for integer pixels.
  369. return parseFloat(value) || 0;
  370. };
  371. }else{
  372. d._toPixelValue = function(element, avalue){
  373. if(!avalue){ return 0; }
  374. // on IE7, medium is usually 4 pixels
  375. if(avalue == "medium"){ return 4; }
  376. // style values can be floats, client code may
  377. // want to round this value for integer pixels.
  378. if(avalue.slice && avalue.slice(-2) == 'px'){ return parseFloat(avalue); }
  379. with(element){
  380. var sLeft = style.left;
  381. var rsLeft = runtimeStyle.left;
  382. runtimeStyle.left = currentStyle.left;
  383. try{
  384. // 'avalue' may be incompatible with style.left, which can cause IE to throw
  385. // this has been observed for border widths using "thin", "medium", "thick" constants
  386. // those particular constants could be trapped by a lookup
  387. // but perhaps there are more
  388. style.left = avalue;
  389. avalue = style.pixelLeft;
  390. }catch(e){
  391. avalue = 0;
  392. }
  393. style.left = sLeft;
  394. runtimeStyle.left = rsLeft;
  395. }
  396. return avalue;
  397. };
  398. }
  399. var px = d._toPixelValue;
  400. // FIXME: there opacity quirks on FF that we haven't ported over. Hrm.
  401. /*=====
  402. dojo._getOpacity = function(node){
  403. // summary:
  404. // Returns the current opacity of the passed node as a
  405. // floating-point value between 0 and 1.
  406. // node: DomNode
  407. // a reference to a DOM node. Does NOT support taking an
  408. // ID string for speed reasons.
  409. // returns: Number between 0 and 1
  410. return; // Number
  411. }
  412. =====*/
  413. var astr = "DXImageTransform.Microsoft.Alpha";
  414. var af = function(n, f){
  415. try{
  416. return n.filters.item(astr);
  417. }catch(e){
  418. return f ? {} : null;
  419. }
  420. };
  421. dojo._getOpacity =
  422. d.isIE < 9 || (d.isIE < 10 && d.isQuirks) ? function(node){
  423. try{
  424. return af(node).Opacity / 100; // Number
  425. }catch(e){
  426. return 1; // Number
  427. }
  428. } :
  429. function(node){
  430. return gcs(node).opacity;
  431. };
  432. /*=====
  433. dojo._setOpacity = function(node, opacity){
  434. // summary:
  435. // set the opacity of the passed node portably. Returns the
  436. // new opacity of the node.
  437. // node: DOMNode
  438. // a reference to a DOM node. Does NOT support taking an
  439. // ID string for performance reasons.
  440. // opacity: Number
  441. // A Number between 0 and 1. 0 specifies transparent.
  442. // returns: Number between 0 and 1
  443. return; // Number
  444. }
  445. =====*/
  446. dojo._setOpacity =
  447. d.isIE < 9 || (d.isIE < 10 && d.isQuirks) ? function(/*DomNode*/node, /*Number*/opacity){
  448. var ov = opacity * 100, opaque = opacity == 1;
  449. node.style.zoom = opaque ? "" : 1;
  450. if(!af(node)){
  451. if(opaque){
  452. return opacity;
  453. }
  454. node.style.filter += " progid:" + astr + "(Opacity=" + ov + ")";
  455. }else{
  456. af(node, 1).Opacity = ov;
  457. }
  458. // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so disable it altogether (bug #2661),
  459. //but still update the opacity value so we can get a correct reading if it is read later.
  460. af(node, 1).Enabled = !opaque;
  461. if(node.nodeName.toLowerCase() == "tr"){
  462. d.query("> td", node).forEach(function(i){
  463. d._setOpacity(i, opacity);
  464. });
  465. }
  466. return opacity;
  467. } :
  468. function(node, opacity){
  469. return node.style.opacity = opacity;
  470. };
  471. var _pixelNamesCache = {
  472. left: true, top: true
  473. };
  474. var _pixelRegExp = /margin|padding|width|height|max|min|offset/; // |border
  475. var _toStyleValue = function(node, type, value){
  476. type = type.toLowerCase(); // FIXME: should we really be doing string case conversion here? Should we cache it? Need to profile!
  477. if(d.isIE){
  478. if(value == "auto"){
  479. if(type == "height"){ return node.offsetHeight; }
  480. if(type == "width"){ return node.offsetWidth; }
  481. }
  482. if(type == "fontweight"){
  483. switch(value){
  484. case 700: return "bold";
  485. case 400:
  486. default: return "normal";
  487. }
  488. }
  489. }
  490. if(!(type in _pixelNamesCache)){
  491. _pixelNamesCache[type] = _pixelRegExp.test(type);
  492. }
  493. return _pixelNamesCache[type] ? px(node, value) : value;
  494. };
  495. var _floatStyle = d.isIE ? "styleFloat" : "cssFloat",
  496. _floatAliases = { "cssFloat": _floatStyle, "styleFloat": _floatStyle, "float": _floatStyle }
  497. ;
  498. // public API
  499. dojo.style = function( /*DomNode|String*/ node,
  500. /*String?|Object?*/ style,
  501. /*String?*/ value){
  502. // summary:
  503. // Accesses styles on a node. If 2 arguments are
  504. // passed, acts as a getter. If 3 arguments are passed, acts
  505. // as a setter.
  506. // description:
  507. // Getting the style value uses the computed style for the node, so the value
  508. // will be a calculated value, not just the immediate node.style value.
  509. // Also when getting values, use specific style names,
  510. // like "borderBottomWidth" instead of "border" since compound values like
  511. // "border" are not necessarily reflected as expected.
  512. // If you want to get node dimensions, use `dojo.marginBox()`,
  513. // `dojo.contentBox()` or `dojo.position()`.
  514. // node:
  515. // id or reference to node to get/set style for
  516. // style:
  517. // the style property to set in DOM-accessor format
  518. // ("borderWidth", not "border-width") or an object with key/value
  519. // pairs suitable for setting each property.
  520. // value:
  521. // If passed, sets value on the node for style, handling
  522. // cross-browser concerns. When setting a pixel value,
  523. // be sure to include "px" in the value. For instance, top: "200px".
  524. // Otherwise, in some cases, some browsers will not apply the style.
  525. // example:
  526. // Passing only an ID or node returns the computed style object of
  527. // the node:
  528. // | dojo.style("thinger");
  529. // example:
  530. // Passing a node and a style property returns the current
  531. // normalized, computed value for that property:
  532. // | dojo.style("thinger", "opacity"); // 1 by default
  533. //
  534. // example:
  535. // Passing a node, a style property, and a value changes the
  536. // current display of the node and returns the new computed value
  537. // | dojo.style("thinger", "opacity", 0.5); // == 0.5
  538. //
  539. // example:
  540. // Passing a node, an object-style style property sets each of the values in turn and returns the computed style object of the node:
  541. // | dojo.style("thinger", {
  542. // | "opacity": 0.5,
  543. // | "border": "3px solid black",
  544. // | "height": "300px"
  545. // | });
  546. //
  547. // example:
  548. // When the CSS style property is hyphenated, the JavaScript property is camelCased.
  549. // font-size becomes fontSize, and so on.
  550. // | dojo.style("thinger",{
  551. // | fontSize:"14pt",
  552. // | letterSpacing:"1.2em"
  553. // | });
  554. //
  555. // example:
  556. // dojo.NodeList implements .style() using the same syntax, omitting the "node" parameter, calling
  557. // dojo.style() on every element of the list. See: `dojo.query()` and `dojo.NodeList()`
  558. // | dojo.query(".someClassName").style("visibility","hidden");
  559. // | // or
  560. // | dojo.query("#baz > div").style({
  561. // | opacity:0.75,
  562. // | fontSize:"13pt"
  563. // | });
  564. var n = byId(node), args = arguments.length, op = (style == "opacity");
  565. style = _floatAliases[style] || style;
  566. if(args == 3){
  567. return op ? d._setOpacity(n, value) : n.style[style] = value; /*Number*/
  568. }
  569. if(args == 2 && op){
  570. return d._getOpacity(n);
  571. }
  572. var s = gcs(n);
  573. if(args == 2 && typeof style != "string"){ // inline'd type check
  574. for(var x in style){
  575. d.style(node, x, style[x]);
  576. }
  577. return s;
  578. }
  579. return (args == 1) ? s : _toStyleValue(n, style, s[style] || n.style[style]); /* CSS2Properties||String||Number */
  580. };
  581. // =============================
  582. // Box Functions
  583. // =============================
  584. dojo._getPadExtents = function(/*DomNode*/n, /*Object*/computedStyle){
  585. // summary:
  586. // Returns object with special values specifically useful for node
  587. // fitting.
  588. // description:
  589. // Returns an object with `w`, `h`, `l`, `t` properties:
  590. // | l/t = left/top padding (respectively)
  591. // | w = the total of the left and right padding
  592. // | h = the total of the top and bottom padding
  593. // If 'node' has position, l/t forms the origin for child nodes.
  594. // The w/h are used for calculating boxes.
  595. // Normally application code will not need to invoke this
  596. // directly, and will use the ...box... functions instead.
  597. var
  598. s = computedStyle||gcs(n),
  599. l = px(n, s.paddingLeft),
  600. t = px(n, s.paddingTop);
  601. return {
  602. l: l,
  603. t: t,
  604. w: l+px(n, s.paddingRight),
  605. h: t+px(n, s.paddingBottom)
  606. };
  607. };
  608. dojo._getBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){
  609. // summary:
  610. // returns an object with properties useful for noting the border
  611. // dimensions.
  612. // description:
  613. // * l/t = the sum of left/top border (respectively)
  614. // * w = the sum of the left and right border
  615. // * h = the sum of the top and bottom border
  616. //
  617. // The w/h are used for calculating boxes.
  618. // Normally application code will not need to invoke this
  619. // directly, and will use the ...box... functions instead.
  620. var
  621. ne = "none",
  622. s = computedStyle||gcs(n),
  623. bl = (s.borderLeftStyle != ne ? px(n, s.borderLeftWidth) : 0),
  624. bt = (s.borderTopStyle != ne ? px(n, s.borderTopWidth) : 0);
  625. return {
  626. l: bl,
  627. t: bt,
  628. w: bl + (s.borderRightStyle!=ne ? px(n, s.borderRightWidth) : 0),
  629. h: bt + (s.borderBottomStyle!=ne ? px(n, s.borderBottomWidth) : 0)
  630. };
  631. };
  632. dojo._getPadBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){
  633. // summary:
  634. // Returns object with properties useful for box fitting with
  635. // regards to padding.
  636. // description:
  637. // * l/t = the sum of left/top padding and left/top border (respectively)
  638. // * w = the sum of the left and right padding and border
  639. // * h = the sum of the top and bottom padding and border
  640. //
  641. // The w/h are used for calculating boxes.
  642. // Normally application code will not need to invoke this
  643. // directly, and will use the ...box... functions instead.
  644. var
  645. s = computedStyle||gcs(n),
  646. p = d._getPadExtents(n, s),
  647. b = d._getBorderExtents(n, s);
  648. return {
  649. l: p.l + b.l,
  650. t: p.t + b.t,
  651. w: p.w + b.w,
  652. h: p.h + b.h
  653. };
  654. };
  655. dojo._getMarginExtents = function(n, computedStyle){
  656. // summary:
  657. // returns object with properties useful for box fitting with
  658. // regards to box margins (i.e., the outer-box).
  659. //
  660. // * l/t = marginLeft, marginTop, respectively
  661. // * w = total width, margin inclusive
  662. // * h = total height, margin inclusive
  663. //
  664. // The w/h are used for calculating boxes.
  665. // Normally application code will not need to invoke this
  666. // directly, and will use the ...box... functions instead.
  667. var
  668. s = computedStyle||gcs(n),
  669. l = px(n, s.marginLeft),
  670. t = px(n, s.marginTop),
  671. r = px(n, s.marginRight),
  672. b = px(n, s.marginBottom);
  673. if(d.isWebKit && (s.position != "absolute")){
  674. // FIXME: Safari's version of the computed right margin
  675. // is the space between our right edge and the right edge
  676. // of our offsetParent.
  677. // What we are looking for is the actual margin value as
  678. // determined by CSS.
  679. // Hack solution is to assume left/right margins are the same.
  680. r = l;
  681. }
  682. return {
  683. l: l,
  684. t: t,
  685. w: l+r,
  686. h: t+b
  687. };
  688. };
  689. // Box getters work in any box context because offsetWidth/clientWidth
  690. // are invariant wrt box context
  691. //
  692. // They do *not* work for display: inline objects that have padding styles
  693. // because the user agent ignores padding (it's bogus styling in any case)
  694. //
  695. // Be careful with IMGs because they are inline or block depending on
  696. // browser and browser mode.
  697. // Although it would be easier to read, there are not separate versions of
  698. // _getMarginBox for each browser because:
  699. // 1. the branching is not expensive
  700. // 2. factoring the shared code wastes cycles (function call overhead)
  701. // 3. duplicating the shared code wastes bytes
  702. dojo._getMarginBox = function(/*DomNode*/node, /*Object*/computedStyle){
  703. // summary:
  704. // returns an object that encodes the width, height, left and top
  705. // positions of the node's margin box.
  706. var s = computedStyle || gcs(node), me = d._getMarginExtents(node, s);
  707. var l = node.offsetLeft - me.l, t = node.offsetTop - me.t, p = node.parentNode;
  708. if(d.isMoz){
  709. // Mozilla:
  710. // If offsetParent has a computed overflow != visible, the offsetLeft is decreased
  711. // by the parent's border.
  712. // We don't want to compute the parent's style, so instead we examine node's
  713. // computed left/top which is more stable.
  714. var sl = parseFloat(s.left), st = parseFloat(s.top);
  715. if(!isNaN(sl) && !isNaN(st)){
  716. l = sl, t = st;
  717. }else{
  718. // If child's computed left/top are not parseable as a number (e.g. "auto"), we
  719. // have no choice but to examine the parent's computed style.
  720. if(p && p.style){
  721. var pcs = gcs(p);
  722. if(pcs.overflow != "visible"){
  723. var be = d._getBorderExtents(p, pcs);
  724. l += be.l, t += be.t;
  725. }
  726. }
  727. }
  728. }else if(d.isOpera || (d.isIE == 8 && !d.isQuirks)){
  729. // On Opera and IE 8, offsetLeft/Top includes the parent's border
  730. if(p){
  731. be = d._getBorderExtents(p);
  732. l -= be.l;
  733. t -= be.t;
  734. }
  735. }
  736. return {
  737. l: l,
  738. t: t,
  739. w: node.offsetWidth + me.w,
  740. h: node.offsetHeight + me.h
  741. };
  742. }
  743. dojo._getMarginSize = function(/*DomNode*/node, /*Object*/computedStyle){
  744. // summary:
  745. // returns an object that encodes the width and height of
  746. // the node's margin box
  747. node = byId(node);
  748. var me = d._getMarginExtents(node, computedStyle || gcs(node));
  749. var size = node.getBoundingClientRect();
  750. return {
  751. w: (size.right - size.left) + me.w,
  752. h: (size.bottom - size.top) + me.h
  753. }
  754. }
  755. dojo._getContentBox = function(node, computedStyle){
  756. // summary:
  757. // Returns an object that encodes the width, height, left and top
  758. // positions of the node's content box, irrespective of the
  759. // current box model.
  760. // clientWidth/Height are important since the automatically account for scrollbars
  761. // fallback to offsetWidth/Height for special cases (see #3378)
  762. var s = computedStyle || gcs(node),
  763. pe = d._getPadExtents(node, s),
  764. be = d._getBorderExtents(node, s),
  765. w = node.clientWidth,
  766. h
  767. ;
  768. if(!w){
  769. w = node.offsetWidth, h = node.offsetHeight;
  770. }else{
  771. h = node.clientHeight, be.w = be.h = 0;
  772. }
  773. // On Opera, offsetLeft includes the parent's border
  774. if(d.isOpera){ pe.l += be.l; pe.t += be.t; }
  775. return {
  776. l: pe.l,
  777. t: pe.t,
  778. w: w - pe.w - be.w,
  779. h: h - pe.h - be.h
  780. };
  781. };
  782. dojo._getBorderBox = function(node, computedStyle){
  783. var s = computedStyle || gcs(node),
  784. pe = d._getPadExtents(node, s),
  785. cb = d._getContentBox(node, s)
  786. ;
  787. return {
  788. l: cb.l - pe.l,
  789. t: cb.t - pe.t,
  790. w: cb.w + pe.w,
  791. h: cb.h + pe.h
  792. };
  793. };
  794. // Box setters depend on box context because interpretation of width/height styles
  795. // vary wrt box context.
  796. //
  797. // The value of dojo.boxModel is used to determine box context.
  798. // dojo.boxModel can be set directly to change behavior.
  799. //
  800. // Beware of display: inline objects that have padding styles
  801. // because the user agent ignores padding (it's a bogus setup anyway)
  802. //
  803. // Be careful with IMGs because they are inline or block depending on
  804. // browser and browser mode.
  805. //
  806. // Elements other than DIV may have special quirks, like built-in
  807. // margins or padding, or values not detectable via computedStyle.
  808. // In particular, margins on TABLE do not seems to appear
  809. // at all in computedStyle on Mozilla.
  810. dojo._setBox = function(/*DomNode*/node, /*Number?*/l, /*Number?*/t, /*Number?*/w, /*Number?*/h, /*String?*/u){
  811. // summary:
  812. // sets width/height/left/top in the current (native) box-model
  813. // dimentions. Uses the unit passed in u.
  814. // node:
  815. // DOM Node reference. Id string not supported for performance
  816. // reasons.
  817. // l:
  818. // left offset from parent.
  819. // t:
  820. // top offset from parent.
  821. // w:
  822. // width in current box model.
  823. // h:
  824. // width in current box model.
  825. // u:
  826. // unit measure to use for other measures. Defaults to "px".
  827. u = u || "px";
  828. var s = node.style;
  829. if(!isNaN(l)){ s.left = l + u; }
  830. if(!isNaN(t)){ s.top = t + u; }
  831. if(w >= 0){ s.width = w + u; }
  832. if(h >= 0){ s.height = h + u; }
  833. };
  834. dojo._isButtonTag = function(/*DomNode*/node) {
  835. // summary:
  836. // True if the node is BUTTON or INPUT.type="button".
  837. return node.tagName == "BUTTON"
  838. || node.tagName=="INPUT" && (node.getAttribute("type")||'').toUpperCase() == "BUTTON"; // boolean
  839. };
  840. dojo._usesBorderBox = function(/*DomNode*/node){
  841. // summary:
  842. // True if the node uses border-box layout.
  843. // We could test the computed style of node to see if a particular box
  844. // has been specified, but there are details and we choose not to bother.
  845. // TABLE and BUTTON (and INPUT type=button) are always border-box by default.
  846. // If you have assigned a different box to either one via CSS then
  847. // box functions will break.
  848. var n = node.tagName;
  849. return d.boxModel=="border-box" || n=="TABLE" || d._isButtonTag(node); // boolean
  850. };
  851. dojo._setContentSize = function(/*DomNode*/node, /*Number*/widthPx, /*Number*/heightPx, /*Object*/computedStyle){
  852. // summary:
  853. // Sets the size of the node's contents, irrespective of margins,
  854. // padding, or borders.
  855. if(d._usesBorderBox(node)){
  856. var pb = d._getPadBorderExtents(node, computedStyle);
  857. if(widthPx >= 0){ widthPx += pb.w; }
  858. if(heightPx >= 0){ heightPx += pb.h; }
  859. }
  860. d._setBox(node, NaN, NaN, widthPx, heightPx);
  861. };
  862. dojo._setMarginBox = function(/*DomNode*/node, /*Number?*/leftPx, /*Number?*/topPx,
  863. /*Number?*/widthPx, /*Number?*/heightPx,
  864. /*Object*/computedStyle){
  865. // summary:
  866. // sets the size of the node's margin box and placement
  867. // (left/top), irrespective of box model. Think of it as a
  868. // passthrough to dojo._setBox that handles box-model vagaries for
  869. // you.
  870. var s = computedStyle || gcs(node),
  871. // Some elements have special padding, margin, and box-model settings.
  872. // To use box functions you may need to set padding, margin explicitly.
  873. // Controlling box-model is harder, in a pinch you might set dojo.boxModel.
  874. bb = d._usesBorderBox(node),
  875. pb = bb ? _nilExtents : d._getPadBorderExtents(node, s)
  876. ;
  877. if(d.isWebKit){
  878. // on Safari (3.1.2), button nodes with no explicit size have a default margin
  879. // setting an explicit size eliminates the margin.
  880. // We have to swizzle the width to get correct margin reading.
  881. if(d._isButtonTag(node)){
  882. var ns = node.style;
  883. if(widthPx >= 0 && !ns.width) { ns.width = "4px"; }
  884. if(heightPx >= 0 && !ns.height) { ns.height = "4px"; }
  885. }
  886. }
  887. var mb = d._getMarginExtents(node, s);
  888. if(widthPx >= 0){ widthPx = Math.max(widthPx - pb.w - mb.w, 0); }
  889. if(heightPx >= 0){ heightPx = Math.max(heightPx - pb.h - mb.h, 0); }
  890. d._setBox(node, leftPx, topPx, widthPx, heightPx);
  891. };
  892. var _nilExtents = { l:0, t:0, w:0, h:0 };
  893. // public API
  894. dojo.marginBox = function(/*DomNode|String*/node, /*Object?*/box){
  895. // summary:
  896. // Getter/setter for the margin-box of node.
  897. // description:
  898. // Getter/setter for the margin-box of node.
  899. // Returns an object in the expected format of box (regardless
  900. // if box is passed). The object might look like:
  901. // `{ l: 50, t: 200, w: 300: h: 150 }`
  902. // for a node offset from its parent 50px to the left, 200px from
  903. // the top with a margin width of 300px and a margin-height of
  904. // 150px.
  905. // node:
  906. // id or reference to DOM Node to get/set box for
  907. // box:
  908. // If passed, denotes that dojo.marginBox() should
  909. // update/set the margin box for node. Box is an object in the
  910. // above format. All properties are optional if passed.
  911. // example:
  912. // Retrieve the marginbox of a passed node
  913. // | var box = dojo.marginBox("someNodeId");
  914. // | console.dir(box);
  915. //
  916. // example:
  917. // Set a node's marginbox to the size of another node
  918. // | var box = dojo.marginBox("someNodeId");
  919. // | dojo.marginBox("someOtherNode", box);
  920. var n = byId(node), s = gcs(n), b = box;
  921. return !b ? d._getMarginBox(n, s) : d._setMarginBox(n, b.l, b.t, b.w, b.h, s); // Object
  922. };
  923. dojo.contentBox = function(/*DomNode|String*/node, /*Object?*/box){
  924. // summary:
  925. // Getter/setter for the content-box of node.
  926. // description:
  927. // Returns an object in the expected format of box (regardless if box is passed).
  928. // The object might look like:
  929. // `{ l: 50, t: 200, w: 300: h: 150 }`
  930. // for a node offset from its parent 50px to the left, 200px from
  931. // the top with a content width of 300px and a content-height of
  932. // 150px. Note that the content box may have a much larger border
  933. // or margin box, depending on the box model currently in use and
  934. // CSS values set/inherited for node.
  935. // While the getter will return top and left values, the
  936. // setter only accepts setting the width and height.
  937. // node:
  938. // id or reference to DOM Node to get/set box for
  939. // box:
  940. // If passed, denotes that dojo.contentBox() should
  941. // update/set the content box for node. Box is an object in the
  942. // above format, but only w (width) and h (height) are supported.
  943. // All properties are optional if passed.
  944. var n = byId(node), s = gcs(n), b = box;
  945. return !b ? d._getContentBox(n, s) : d._setContentSize(n, b.w, b.h, s); // Object
  946. };
  947. // =============================
  948. // Positioning
  949. // =============================
  950. var _sumAncestorProperties = function(node, prop){
  951. if(!(node = (node||0).parentNode)){return 0;}
  952. var val, retVal = 0, _b = d.body();
  953. while(node && node.style){
  954. if(gcs(node).position == "fixed"){
  955. return 0;
  956. }
  957. val = node[prop];
  958. if(val){
  959. retVal += val - 0;
  960. // opera and khtml #body & #html has the same values, we only
  961. // need one value
  962. if(node == _b){ break; }
  963. }
  964. node = node.parentNode;
  965. }
  966. return retVal; // integer
  967. };
  968. dojo._docScroll = function(){
  969. var n = d.global;
  970. return "pageXOffset" in n
  971. ? { x:n.pageXOffset, y:n.pageYOffset }
  972. : (n = d.isQuirks? d.doc.body : d.doc.documentElement, { x:d._fixIeBiDiScrollLeft(n.scrollLeft || 0), y:n.scrollTop || 0 });
  973. };
  974. dojo._isBodyLtr = function(){
  975. return "_bodyLtr" in d? d._bodyLtr :
  976. d._bodyLtr = ((d.body() && d.body().dir) || d.doc.documentElement.dir || "ltr").toLowerCase() == "ltr"; // Boolean
  977. };
  978. dojo._getIeDocumentElementOffset = function(){
  979. // summary:
  980. // returns the offset in x and y from the document body to the
  981. // visual edge of the page
  982. // description:
  983. // The following values in IE contain an offset:
  984. // | event.clientX
  985. // | event.clientY
  986. // | node.getBoundingClientRect().left
  987. // | node.getBoundingClientRect().top
  988. // But other position related values do not contain this offset,
  989. // such as node.offsetLeft, node.offsetTop, node.style.left and
  990. // node.style.top. The offset is always (2, 2) in LTR direction.
  991. // When the body is in RTL direction, the offset counts the width
  992. // of left scroll bar's width. This function computes the actual
  993. // offset.
  994. //NOTE: assumes we're being called in an IE browser
  995. var de = d.doc.documentElement; // only deal with HTML element here, _abs handles body/quirks
  996. if(d.isIE < 8){
  997. var r = de.getBoundingClientRect(); // works well for IE6+
  998. //console.debug('rect left,top = ' + r.left+','+r.top + ', html client left/top = ' + de.clientLeft+','+de.clientTop + ', rtl = ' + (!d._isBodyLtr()) + ', quirks = ' + d.isQuirks);
  999. var l = r.left,
  1000. t = r.top;
  1001. if(d.isIE < 7){
  1002. l += de.clientLeft; // scrollbar size in strict/RTL, or,
  1003. t += de.clientTop; // HTML border size in strict
  1004. }
  1005. return {
  1006. x: l < 0? 0 : l, // FRAME element border size can lead to inaccurate negative values
  1007. y: t < 0? 0 : t
  1008. };
  1009. }else{
  1010. return {
  1011. x: 0,
  1012. y: 0
  1013. };
  1014. }
  1015. };
  1016. dojo._fixIeBiDiScrollLeft = function(/*Integer*/ scrollLeft){
  1017. // In RTL direction, scrollLeft should be a negative value, but IE
  1018. // returns a positive one. All codes using documentElement.scrollLeft
  1019. // must call this function to fix this error, otherwise the position
  1020. // will offset to right when there is a horizontal scrollbar.
  1021. var ie = d.isIE;
  1022. if(ie && !d._isBodyLtr()){
  1023. var qk = d.isQuirks,
  1024. de = qk ? d.doc.body : d.doc.documentElement;
  1025. if(ie == 6 && !qk && d.global.frameElement && de.scrollHeight > de.clientHeight){
  1026. scrollLeft += de.clientLeft; // workaround ie6+strict+rtl+iframe+vertical-scrollbar bug where clientWidth is too small by clientLeft pixels
  1027. }
  1028. return (ie < 8 || qk) ? (scrollLeft + de.clientWidth - de.scrollWidth) : -scrollLeft; // Integer
  1029. }
  1030. return scrollLeft; // Integer
  1031. };
  1032. // FIXME: need a setter for coords or a moveTo!!
  1033. dojo._abs = dojo.position = function(/*DomNode*/node, /*Boolean?*/includeScroll){
  1034. // summary:
  1035. // Gets the position and size of the passed element relative to
  1036. // the viewport (if includeScroll==false), or relative to the
  1037. // document root (if includeScroll==true).
  1038. //
  1039. // description:
  1040. // Returns an object of the form:
  1041. // { x: 100, y: 300, w: 20, h: 15 }
  1042. // If includeScroll==true, the x and y values will include any
  1043. // document offsets that may affect the position relative to the
  1044. // viewport.
  1045. // Uses the border-box model (inclusive of border and padding but
  1046. // not margin). Does not act as a setter.
  1047. node = byId(node);
  1048. var db = d.body(),
  1049. dh = db.parentNode,
  1050. ret = node.getBoundingClientRect();
  1051. ret = { x: ret.left, y: ret.top, w: ret.right - ret.left, h: ret.bottom - ret.top };
  1052. if(d.isIE < 9){
  1053. // On IE there's a 2px offset that we need to adjust for, see _getIeDocumentElementOffset()
  1054. var offset = d._getIeDocumentElementOffset();
  1055. // fixes the position in IE, quirks mode
  1056. ret.x -= offset.x + (d.isQuirks ? db.clientLeft+db.offsetLeft : 0);
  1057. ret.y -= offset.y + (d.isQuirks ? db.clientTop+db.offsetTop : 0);
  1058. }else if(d.isFF == 3){
  1059. // In FF3 you have to subtract the document element margins.
  1060. // Fixed in FF3.5 though.
  1061. var cs = gcs(dh);
  1062. ret.x -= px(dh, cs.marginLeft) + px(dh, cs.borderLeftWidth);
  1063. ret.y -= px(dh, cs.marginTop) + px(dh, cs.borderTopWidth);
  1064. }
  1065. // account for document scrolling
  1066. if(includeScroll){
  1067. var scroll = d._docScroll();
  1068. ret.x += scroll.x;
  1069. ret.y += scroll.y;
  1070. }
  1071. return ret; // Object
  1072. };
  1073. dojo.coords = function(/*DomNode|String*/node, /*Boolean?*/includeScroll){
  1074. // summary:
  1075. // Deprecated: Use position() for border-box x/y/w/h
  1076. // or marginBox() for margin-box w/h/l/t.
  1077. // Returns an object representing a node's size and position.
  1078. //
  1079. // description:
  1080. // Returns an object that measures margin-box (w)idth/(h)eight
  1081. // and absolute position x/y of the border-box. Also returned
  1082. // is computed (l)eft and (t)op values in pixels from the
  1083. // node's offsetParent as returned from marginBox().
  1084. // Return value will be in the form:
  1085. //| { l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 }
  1086. // Does not act as a setter. If includeScroll is passed, the x and
  1087. // y params are affected as one would expect in dojo.position().
  1088. var n = byId(node), s = gcs(n), mb = d._getMarginBox(n, s);
  1089. var abs = d.position(n, includeScroll);
  1090. mb.x = abs.x;
  1091. mb.y = abs.y;
  1092. return mb;
  1093. };
  1094. // =============================
  1095. // Element attribute Functions
  1096. // =============================
  1097. // dojo.attr() should conform to http://www.w3.org/TR/DOM-Level-2-Core/
  1098. var _propNames = {
  1099. // properties renamed to avoid clashes with reserved words
  1100. "class": "className",
  1101. "for": "htmlFor",
  1102. // properties written as camelCase
  1103. tabindex: "tabIndex",
  1104. readonly: "readOnly",
  1105. colspan: "colSpan",
  1106. frameborder: "frameBorder",
  1107. rowspan: "rowSpan",
  1108. valuetype: "valueType"
  1109. },
  1110. _attrNames = {
  1111. // original attribute names
  1112. classname: "class",
  1113. htmlfor: "for",
  1114. // for IE
  1115. tabindex: "tabIndex",
  1116. readonly: "readOnly"
  1117. },
  1118. _forcePropNames = {
  1119. innerHTML: 1,
  1120. className: 1,
  1121. htmlFor: d.isIE,
  1122. value: 1
  1123. };
  1124. var _fixAttrName = function(/*String*/ name){
  1125. return _attrNames[name.toLowerCase()] || name;
  1126. };
  1127. var _hasAttr = function(node, name){
  1128. var attr = node.getAttributeNode && node.getAttributeNode(name);
  1129. return attr && attr.specified; // Boolean
  1130. };
  1131. // There is a difference in the presence of certain properties and their default values
  1132. // between browsers. For example, on IE "disabled" is present on all elements,
  1133. // but it is value is "false"; "tabIndex" of <div> returns 0 by default on IE, yet other browsers
  1134. // can return -1.
  1135. dojo.hasAttr = function(/*DomNode|String*/node, /*String*/name){
  1136. // summary:
  1137. // Returns true if the requested attribute is specified on the
  1138. // given element, and false otherwise.
  1139. // node:
  1140. // id or reference to the element to check
  1141. // name:
  1142. // the name of the attribute
  1143. // returns:
  1144. // true if the requested attribute is specified on the
  1145. // given element, and false otherwise
  1146. var lc = name.toLowerCase();
  1147. return _forcePropNames[_propNames[lc] || name] || _hasAttr(byId(node), _attrNames[lc] || name); // Boolean
  1148. };
  1149. var _evtHdlrMap = {}, _ctr = 0,
  1150. _attrId = dojo._scopeName + "attrid",
  1151. // the next dictionary lists elements with read-only innerHTML on IE
  1152. _roInnerHtml = {col: 1, colgroup: 1,
  1153. // frameset: 1, head: 1, html: 1, style: 1,
  1154. table: 1, tbody: 1, tfoot: 1, thead: 1, tr: 1, title: 1};
  1155. dojo.attr = function(/*DomNode|String*/node, /*String|Object*/name, /*String?*/value){
  1156. // summary:
  1157. // Gets or sets an attribute on an HTML element.
  1158. // description:
  1159. // Handles normalized getting and setting of attributes on DOM
  1160. // Nodes. If 2 arguments are passed, and a the second argumnt is a
  1161. // string, acts as a getter.
  1162. //
  1163. // If a third argument is passed, or if the second argument is a
  1164. // map of attributes, acts as a setter.
  1165. //
  1166. // When passing functions as values, note that they will not be
  1167. // directly assigned to slots on the node, but rather the default
  1168. // behavior will be removed and the new behavior will be added
  1169. // using `dojo.connect()`, meaning that event handler properties
  1170. // will be normalized and that some caveats with regards to
  1171. // non-standard behaviors for onsubmit apply. Namely that you
  1172. // should cancel form submission using `dojo.stopEvent()` on the
  1173. // passed event object instead of returning a boolean value from
  1174. // the handler itself.
  1175. // node:
  1176. // id or reference to the element to get or set the attribute on
  1177. // name:
  1178. // the name of the attribute to get or set.
  1179. // value:
  1180. // The value to set for the attribute
  1181. // returns:
  1182. // when used as a getter, the value of the requested attribute
  1183. // or null if that attribute does not have a specified or
  1184. // default value;
  1185. //
  1186. // when used as a setter, the DOM node
  1187. //
  1188. // example:
  1189. // | // get the current value of the "foo" attribute on a node
  1190. // | dojo.attr(dojo.byId("nodeId"), "foo");
  1191. // | // or we can just pass the id:
  1192. // | dojo.attr("nodeId", "foo");
  1193. //
  1194. // example:
  1195. // | // use attr() to set the tab index
  1196. // | dojo.attr("nodeId", "tabIndex", 3);
  1197. // |
  1198. //
  1199. // example:
  1200. // Set multiple values at once, including event handlers:
  1201. // | dojo.attr("formId", {
  1202. // | "foo": "bar",
  1203. // | "tabIndex": -1,
  1204. // | "method": "POST",
  1205. // | "onsubmit": function(e){
  1206. // | // stop submitting the form. Note that the IE behavior
  1207. // | // of returning true or false will have no effect here
  1208. // | // since our handler is connect()ed to the built-in
  1209. // | // onsubmit behavior and so we need to use
  1210. // | // dojo.stopEvent() to ensure that the submission
  1211. // | // doesn't proceed.
  1212. // | dojo.stopEvent(e);
  1213. // |
  1214. // | // submit the form with Ajax
  1215. // | dojo.xhrPost({ form: "formId" });
  1216. // | }
  1217. // | });
  1218. //
  1219. // example:
  1220. // Style is s special case: Only set with an object hash of styles
  1221. // | dojo.attr("someNode",{
  1222. // | id:"bar",
  1223. // | style:{
  1224. // | width:"200px", height:"100px", color:"#000"
  1225. // | }
  1226. // | });
  1227. //
  1228. // example:
  1229. // Again, only set style as an object hash of styles:
  1230. // | var obj = { color:"#fff", backgroundColor:"#000" };
  1231. // | dojo.attr("someNode", "style", obj);
  1232. // |
  1233. // | // though shorter to use `dojo.style()` in this case:
  1234. // | dojo.style("someNode", obj);
  1235. node = byId(node);
  1236. var args = arguments.length, prop;
  1237. if(args == 2 && typeof name != "string"){ // inline'd type check
  1238. // the object form of setter: the 2nd argument is a dictionary
  1239. for(var x in name){
  1240. d.attr(node, x, name[x]);
  1241. }
  1242. return node; // DomNode
  1243. }
  1244. var lc = name.toLowerCase(),
  1245. propName = _propNames[lc] || name,
  1246. forceProp = _forcePropNames[propName],
  1247. attrName = _attrNames[lc] || name;
  1248. if(args == 3){
  1249. // setter
  1250. do{
  1251. if(propName == "style" && typeof value != "string"){ // inline'd type check
  1252. // special case: setting a style
  1253. d.style(node, value);
  1254. break;
  1255. }
  1256. if(propName == "innerHTML"){
  1257. // special case: assigning HTML
  1258. if(d.isIE && node.tagName.toLowerCase() in _roInnerHtml){
  1259. d.empty(node);
  1260. node.appendChild(d._toDom(value, node.ownerDocument));
  1261. }else{
  1262. node[propName] = value;
  1263. }
  1264. break;
  1265. }
  1266. if(d.isFunction(value)){
  1267. // special case: assigning an event handler
  1268. // clobber if we can
  1269. var attrId = d.attr(node, _attrId);
  1270. if(!attrId){
  1271. attrId = _ctr++;
  1272. d.attr(node, _attrId, attrId);
  1273. }
  1274. if(!_evtHdlrMap[attrId]){
  1275. _evtHdlrMap[attrId] = {};
  1276. }
  1277. var h = _evtHdlrMap[attrId][propName];
  1278. if(h){
  1279. d.disconnect(h);
  1280. }else{
  1281. try{
  1282. delete node[propName];
  1283. }catch(e){}
  1284. }
  1285. // ensure that event objects are normalized, etc.
  1286. _evtHdlrMap[attrId][propName] = d.connect(node, propName, value);
  1287. break;
  1288. }
  1289. if(forceProp || typeof value == "boolean"){
  1290. // special case: forcing assignment to the property
  1291. // special case: setting boolean to a property instead of attribute
  1292. node[propName] = value;
  1293. break;
  1294. }
  1295. // node's attribute
  1296. node.setAttribute(attrName, value);
  1297. }while(false);
  1298. return node; // DomNode
  1299. }
  1300. // getter
  1301. // should we access this attribute via a property or
  1302. // via getAttribute()?
  1303. value = node[propName];
  1304. if(forceProp && typeof value != "undefined"){
  1305. // node's property
  1306. return value; // Anything
  1307. }
  1308. if(propName != "href" && (typeof value == "boolean" || d.isFunction(value))){
  1309. // node's property
  1310. return value; // Anything
  1311. }
  1312. // node's attribute
  1313. // we need _hasAttr() here to guard against IE returning a default value
  1314. return _hasAttr(node, attrName) ? node.getAttribute(attrName) : null; // Anything
  1315. };
  1316. dojo.removeAttr = function(/*DomNode|String*/ node, /*String*/ name){
  1317. // summary:
  1318. // Removes an attribute from an HTML element.
  1319. // node:
  1320. // id or reference to the element to remove the attribute from
  1321. // name:
  1322. // the name of the attribute to remove
  1323. byId(node).removeAttribute(_fixAttrName(name));
  1324. };
  1325. dojo.getNodeProp = function(/*DomNode|String*/ node, /*String*/ name){
  1326. // summary:
  1327. // Returns an effective value of a property or an attribute.
  1328. // node:
  1329. // id or reference to the element to remove the attribute from
  1330. // name:
  1331. // the name of the attribute
  1332. node = byId(node);
  1333. var lc = name.toLowerCase(),
  1334. propName = _propNames[lc] || name;
  1335. if((propName in node) && propName != "href"){
  1336. // node's property
  1337. return node[propName]; // Anything
  1338. }
  1339. // node's attribute
  1340. var attrName = _attrNames[lc] || name;
  1341. return _hasAttr(node, attrName) ? node.getAttribute(attrName) : null; // Anything
  1342. };
  1343. dojo.create = function(tag, attrs, refNode, pos){
  1344. // summary:
  1345. // Create an element, allowing for optional attribute decoration
  1346. // and placement.
  1347. //
  1348. // description:
  1349. // A DOM Element creation function. A shorthand method for creating a node or
  1350. // a fragment, and allowing for a convenient optional attribute setting step,
  1351. // as well as an optional DOM placement reference.
  1352. //|
  1353. // Attributes are set by passing the optional object through `dojo.attr`.
  1354. // See `dojo.attr` for noted caveats and nuances, and API if applicable.
  1355. //|
  1356. // Placement is done via `dojo.place`, assuming the new node to be the action
  1357. // node, passing along the optional reference node and position.
  1358. //
  1359. // tag: String|DomNode
  1360. // A string of the element to create (eg: "div", "a", "p", "li", "script", "br"),
  1361. // or an existing DOM node to process.
  1362. //
  1363. // attrs: Object
  1364. // An object-hash of attributes to set on the newly created node.
  1365. // Can be null, if you don't want to set any attributes/styles.
  1366. // See: `dojo.attr` for a description of available attributes.
  1367. //
  1368. // refNode: String?|DomNode?
  1369. // Optional reference node. Used by `dojo.place` to place the newly created
  1370. // node somewhere in the dom relative to refNode. Can be a DomNode reference
  1371. // or String ID of a node.
  1372. //
  1373. // pos: String?
  1374. // Optional positional reference. Defaults to "last" by way of `dojo.place`,
  1375. // though can be set to "first","after","before","last", "replace" or "only"
  1376. // to further control the placement of the new node relative to the refNode.
  1377. // 'refNode' is required if a 'pos' is specified.
  1378. //
  1379. // returns: DomNode
  1380. //
  1381. // example:
  1382. // Create a DIV:
  1383. // | var n = dojo.create("div");
  1384. //
  1385. // example:
  1386. // Create a DIV with content:
  1387. // | var n = dojo.create("div", { innerHTML:"<p>hi</p>" });
  1388. //
  1389. // example:
  1390. // Place a new DIV in the BODY, with no attributes set
  1391. // | var n = dojo.create("div", null, dojo.body());
  1392. //
  1393. // example:
  1394. // Create an UL, and populate it with LI's. Place the list as the first-child of a
  1395. // node with id="someId":
  1396. // | var ul = dojo.create("ul", null, "someId", "first");
  1397. // | var items = ["one", "two", "three", "four"];
  1398. // | dojo.forEach(items, function(data){
  1399. // | dojo.create("li", { innerHTML: data }, ul);
  1400. // | });
  1401. //
  1402. // example:
  1403. // Create an anchor, with an href. Place in BODY:
  1404. // | dojo.create("a", { href:"foo.html", title:"Goto FOO!" }, dojo.body());
  1405. //
  1406. // example:
  1407. // Create a `dojo.NodeList()` from a new element (for syntatic sugar):
  1408. // | dojo.query(dojo.create('div'))
  1409. // | .addClass("newDiv")
  1410. // | .onclick(function(e){ console.log('clicked', e.target) })
  1411. // | .place("#someNode"); // redundant, but cleaner.
  1412. var doc = d.doc;
  1413. if(refNode){
  1414. refNode = byId(refNode);
  1415. doc = refNode.ownerDocument;
  1416. }
  1417. if(typeof tag == "string"){ // inline'd type check
  1418. tag = doc.createElement(tag);
  1419. }
  1420. if(attrs){ d.attr(tag, attrs); }
  1421. if(refNode){ d.place(tag, refNode, pos); }
  1422. return tag; // DomNode
  1423. };
  1424. /*=====
  1425. dojo.empty = function(node){
  1426. // summary:
  1427. // safely removes all children of the node.
  1428. // node: DOMNode|String
  1429. // a reference to a DOM node or an id.
  1430. // example:
  1431. // Destroy node's children byId:
  1432. // | dojo.empty("someId");
  1433. //
  1434. // example:
  1435. // Destroy all nodes' children in a list by reference:
  1436. // | dojo.query(".someNode").forEach(dojo.empty);
  1437. }
  1438. =====*/
  1439. function _empty(/*DomNode*/ node){
  1440. if(node.canHaveChildren){
  1441. try{
  1442. // fast path
  1443. node.innerHTML = "";
  1444. return;
  1445. }catch(e){
  1446. // innerHTML is readOnly (e.g. TABLE (sub)elements in quirks mode)
  1447. // Fall through (saves bytes)
  1448. }
  1449. }
  1450. // SVG/strict elements don't support innerHTML/canHaveChildren, and OBJECT/APPLET elements in quirks node have canHaveChildren=false
  1451. for(var c; c = node.lastChild;){ // intentional assignment
  1452. _destroy(c, node); // destroy is better than removeChild so TABLE subelements are removed in proper order
  1453. }
  1454. }
  1455. d.empty = function(node){
  1456. _empty(byId(node));
  1457. };
  1458. /*=====
  1459. dojo._toDom = function(frag, doc){
  1460. // summary:
  1461. // instantiates an HTML fragment returning the corresponding DOM.
  1462. // frag: String
  1463. // the HTML fragment
  1464. // doc: DocumentNode?
  1465. // optional document to use when creating DOM nodes, defaults to
  1466. // dojo.doc if not specified.
  1467. // returns: DocumentFragment
  1468. //
  1469. // example:
  1470. // Create a table row:
  1471. // | var tr = dojo._toDom("<tr><td>First!</td></tr>");
  1472. }
  1473. =====*/
  1474. // support stuff for dojo._toDom
  1475. var tagWrap = {
  1476. option: ["select"],
  1477. tbody: ["table"],
  1478. thead: ["table"],
  1479. tfoot: ["table"],
  1480. tr: ["table", "tbody"],
  1481. td: ["table", "tbody", "tr"],
  1482. th: ["table", "thead", "tr"],
  1483. legend: ["fieldset"],
  1484. caption: ["table"],
  1485. colgroup: ["table"],
  1486. col: ["table", "colgroup"],
  1487. li: ["ul"]
  1488. },
  1489. reTag = /<\s*([\w\:]+)/,
  1490. masterNode = {}, masterNum = 0,
  1491. masterName = "__" + d._scopeName + "ToDomId";
  1492. // generate start/end tag strings to use
  1493. // for the injection for each special tag wrap case.
  1494. for(var param in tagWrap){
  1495. if(tagWrap.hasOwnProperty(param)){
  1496. var tw = tagWrap[param];
  1497. tw.pre = param == "option" ? '<select multiple="multiple">' : "<" + tw.join("><") + ">";
  1498. tw.post = "</" + tw.reverse().join("></") + ">";
  1499. // the last line is destructive: it reverses the array,
  1500. // but we don't care at this point
  1501. }
  1502. }
  1503. d._toDom = function(frag, doc){
  1504. // summary:
  1505. // converts HTML string into DOM nodes.
  1506. doc = doc || d.doc;
  1507. var masterId = doc[masterName];
  1508. if(!masterId){
  1509. doc[masterName] = masterId = ++masterNum + "";
  1510. masterNode[masterId] = doc.createElement("div");
  1511. }
  1512. // make sure the frag is a string.
  1513. frag += "";
  1514. // find the starting tag, and get node wrapper
  1515. var match = frag.match(reTag),
  1516. tag = match ? match[1].toLowerCase() : "",
  1517. master = masterNode[masterId],
  1518. wrap, i, fc, df;
  1519. if(match && tagWrap[tag]){
  1520. wrap = tagWrap[tag];
  1521. master.innerHTML = wrap.pre + frag + wrap.post;
  1522. for(i = wrap.length; i; --i){
  1523. master = master.firstChild;
  1524. }
  1525. }else{
  1526. master.innerHTML = frag;
  1527. }
  1528. // one node shortcut => return the node itself
  1529. if(master.childNodes.length == 1){
  1530. return master.removeChild(master.firstChild); // DOMNode
  1531. }
  1532. // return multiple nodes as a document fragment
  1533. df = doc.createDocumentFragment();
  1534. while((fc = master.firstChild)){ // intentional assignment
  1535. df.appendChild(fc);
  1536. }
  1537. return df; // DOMNode
  1538. };
  1539. // =============================
  1540. // (CSS) Class Functions
  1541. // =============================
  1542. var _className = "className";
  1543. dojo.hasClass = function(/*DomNode|String*/node, /*String*/classStr){
  1544. // summary:
  1545. // Returns whether or not the specified classes are a portion of the
  1546. // class list currently applied to the node.
  1547. //
  1548. // node:
  1549. // String ID or DomNode reference to check the class for.
  1550. //
  1551. // classStr:
  1552. // A string class name to look for.
  1553. //
  1554. // example:
  1555. // Do something if a node with id="someNode" has class="aSillyClassName" present
  1556. // | if(dojo.hasClass("someNode","aSillyClassName")){ ... }
  1557. return ((" "+ byId(node)[_className] +" ").indexOf(" " + classStr + " ") >= 0); // Boolean
  1558. };
  1559. var spaces = /\s+/, a1 = [""],
  1560. fakeNode = {},
  1561. str2array = function(s){
  1562. if(typeof s == "string" || s instanceof String){
  1563. if(s.indexOf(" ") < 0){
  1564. a1[0] = s;
  1565. return a1;
  1566. }else{
  1567. return s.split(spaces);
  1568. }
  1569. }
  1570. // assumed to be an array
  1571. return s || "";
  1572. };
  1573. dojo.addClass = function(/*DomNode|String*/node, /*String|Array*/classStr){
  1574. // summary:
  1575. // Adds the specified classes to the end of the class list on the
  1576. // passed node. Will not re-apply duplicate classes.
  1577. //
  1578. // node:
  1579. // String ID or DomNode reference to add a class string too
  1580. //
  1581. // classStr:
  1582. // A String class name to add, or several space-separated class names,
  1583. // or an array of class names.
  1584. //
  1585. // example:
  1586. // Add a class to some node:
  1587. // | dojo.addClass("someNode", "anewClass");
  1588. //
  1589. // example:
  1590. // Add two classes at once:
  1591. // | dojo.addClass("someNode", "firstClass secondClass");
  1592. //
  1593. // example:
  1594. // Add two classes at once (using array):
  1595. // | dojo.addClass("someNode", ["firstClass", "secondClass"]);
  1596. //
  1597. // example:
  1598. // Available in `dojo.NodeList` for multiple additions
  1599. // | dojo.query("ul > li").addClass("firstLevel");
  1600. node = byId(node);
  1601. classStr = str2array(classStr);
  1602. var cls = node[_className], oldLen;
  1603. cls = cls ? " " + cls + " " : " ";
  1604. oldLen = cls.length;
  1605. for(var i = 0, len = classStr.length, c; i < len; ++i){
  1606. c = classStr[i];
  1607. if(c && cls.indexOf(" " + c + " ") < 0){
  1608. cls += c + " ";
  1609. }
  1610. }
  1611. if(oldLen < cls.length){
  1612. node[_className] = cls.substr(1, cls.length - 2);
  1613. }
  1614. };
  1615. dojo.removeClass = function(/*DomNode|String*/node, /*String|Array?*/classStr){
  1616. // summary:
  1617. // Removes the specified classes from node. No `dojo.hasClass`
  1618. // check is required.
  1619. //
  1620. // node:
  1621. // String ID or DomNode reference to remove the class from.
  1622. //
  1623. // classStr:
  1624. // An optional String class name to remove, or several space-separated
  1625. // class names, or an array of class names. If omitted, all class names
  1626. // will be deleted.
  1627. //
  1628. // example:
  1629. // Remove a class from some node:
  1630. // | dojo.removeClass("someNode", "firstClass");
  1631. //
  1632. // example:
  1633. // Remove two classes from some node:
  1634. // | dojo.removeClass("someNode", "firstClass secondClass");
  1635. //
  1636. // example:
  1637. // Remove two classes from some node (using array):
  1638. // | dojo.removeClass("someNode", ["firstClass", "secondClass"]);
  1639. //
  1640. // example:
  1641. // Remove all classes from some node:
  1642. // | dojo.removeClass("someNode");
  1643. //
  1644. // example:
  1645. // Available in `dojo.NodeList()` for multiple removal
  1646. // | dojo.query(".foo").removeClass("foo");
  1647. node = byId(node);
  1648. var cls;
  1649. if(classStr !== undefined){
  1650. classStr = str2array(classStr);
  1651. cls = " " + node[_className] + " ";
  1652. for(var i = 0, len = classStr.length; i < len; ++i){
  1653. cls = cls.replace(" " + classStr[i] + " ", " ");
  1654. }
  1655. cls = d.trim(cls);
  1656. }else{
  1657. cls = "";
  1658. }
  1659. if(node[_className] != cls){ node[_className] = cls; }
  1660. };
  1661. dojo.replaceClass = function(/*DomNode|String*/node, /*String|Array*/addClassStr, /*String|Array?*/removeClassStr){
  1662. // summary:
  1663. // Replaces one or more classes on a node if not present.
  1664. // Operates more quickly than calling dojo.removeClass and dojo.addClass
  1665. // node:
  1666. // String ID or DomNode reference to remove the class from.
  1667. // addClassStr:
  1668. // A String class name to add, or several space-separated class names,
  1669. // or an array of class names.
  1670. // removeClassStr:
  1671. // A String class name to remove, or several space-separated class names,
  1672. // or an array of class names.
  1673. //
  1674. // example:
  1675. // | dojo.replaceClass("someNode", "add1 add2", "remove1 remove2");
  1676. //
  1677. // example:
  1678. // Replace all classes with addMe
  1679. // | dojo.replaceClass("someNode", "addMe");
  1680. //
  1681. // example:
  1682. // Available in `dojo.NodeList()` for multiple toggles
  1683. // | dojo.query(".findMe").replaceClass("addMe", "removeMe");
  1684. node = byId(node);
  1685. fakeNode.className = node.className;
  1686. dojo.removeClass(fakeNode, removeClassStr);
  1687. dojo.addClass(fakeNode, addClassStr);
  1688. if(node.className !== fakeNode.className){
  1689. node.className = fakeNode.className;
  1690. }
  1691. };
  1692. dojo.toggleClass = function(/*DomNode|String*/node, /*String|Array*/classStr, /*Boolean?*/condition){
  1693. // summary:
  1694. // Adds a class to node if not present, or removes if present.
  1695. // Pass a boolean condition if you want to explicitly add or remove.
  1696. // condition:
  1697. // If passed, true means to add the class, false means to remove.
  1698. //
  1699. // example:
  1700. // | dojo.toggleClass("someNode", "hovered");
  1701. //
  1702. // example:
  1703. // Forcefully add a class
  1704. // | dojo.toggleClass("someNode", "hovered", true);
  1705. //
  1706. // example:
  1707. // Available in `dojo.NodeList()` for multiple toggles
  1708. // | dojo.query(".toggleMe").toggleClass("toggleMe");
  1709. if(condition === undefined){
  1710. condition = !d.hasClass(node, classStr);
  1711. }
  1712. d[condition ? "addClass" : "removeClass"](node, classStr);
  1713. };
  1714. })();
  1715. }