7e5517b6ba5cfdb386cb2fdeddff632a9b00b060.svn-base 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090
  1. if (!dojo._hasResource["dojo._base.html"]) { // _hasResource checks added by
  2. // build. Do not use
  3. // _hasResource directly in your
  4. // code.
  5. dojo._hasResource["dojo._base.html"] = true;
  6. dojo.require("dojo._base.lang");
  7. dojo.provide("dojo._base.html");
  8. // FIXME: need to add unit tests for all the semi-public methods
  9. try {
  10. document.execCommand("BackgroundImageCache", false, true);
  11. } catch (e) {
  12. // sane browsers don't have cache "issues"
  13. }
  14. // =============================
  15. // DOM Functions
  16. // =============================
  17. /*
  18. * ===== dojo.byId = function(id, doc){ // summary: // similar to other
  19. * library's "$" function, takes a // string representing a DOM id or a
  20. * DomNode // and returns the corresponding DomNode. If a Node is // passed,
  21. * this function is a no-op. Returns a // single DOM node or null, working
  22. * around several // browser-specific bugs to do so. // id: String|DOMNode //
  23. * DOM id or DOM Node // doc: DocumentElement // Document to work in.
  24. * Defaults to the current value of // dojo.doc. Can be used to retreive //
  25. * node references from other documents. =====
  26. */
  27. if (dojo.isIE || dojo.isOpera) {
  28. dojo.byId = function(id, doc) {
  29. if (dojo.isString(id)) {
  30. var _d = doc || dojo.doc;
  31. var te = _d.getElementById(id);
  32. // attributes.id.value is better than just id in case the
  33. // user has a name=id inside a form
  34. if (te && te.attributes.id.value == id) {
  35. return te;
  36. } else {
  37. var eles = _d.all[id];
  38. if (!eles) {
  39. return;
  40. }
  41. if (!eles.length) {
  42. return eles;
  43. }
  44. // if more than 1, choose first with the correct id
  45. var i = 0;
  46. while ((te = eles[i++])) {
  47. if (te.attributes.id.value == id) {
  48. return te;
  49. }
  50. }
  51. }
  52. } else {
  53. return id; // DomNode
  54. }
  55. }
  56. } else {
  57. dojo.byId = function(id, doc) {
  58. if (dojo.isString(id)) {
  59. return (doc || dojo.doc).getElementById(id);
  60. } else {
  61. return id; // DomNode
  62. }
  63. }
  64. }
  65. /*
  66. * ===== } =====
  67. */
  68. (function() {
  69. /*
  70. * dojo.createElement = function(obj, parent, position){ // TODO: need
  71. * to finish this! }
  72. */
  73. var _destroyContainer = null;
  74. dojo._destroyElement = function(/* String||DomNode */node) {
  75. // summary:
  76. // removes node from its parent, clobbers it and all of its
  77. // children.
  78. // node:
  79. // the element to be destroyed, either as an ID or a reference
  80. node = dojo.byId(node);
  81. try {
  82. if (!_destroyContainer) {
  83. _destroyContainer = document.createElement("div");
  84. }
  85. _destroyContainer.appendChild(node.parentNode ? node.parentNode
  86. .removeChild(node) : node);
  87. // NOTE: see http://trac.dojotoolkit.org/ticket/2931. This may
  88. // be a bug and not a feature
  89. _destroyContainer.innerHTML = "";
  90. } catch (e) {
  91. /* squelch */
  92. }
  93. };
  94. dojo.isDescendant = function(/* DomNode|String */node, /* DomNode|String */
  95. ancestor) {
  96. // summary:
  97. // Returns true if node is a descendant of ancestor
  98. // node: id or node reference to test
  99. // ancestor: id or node reference of potential parent to test
  100. // against
  101. try {
  102. node = dojo.byId(node);
  103. ancestor = dojo.byId(ancestor);
  104. while (node) {
  105. if (node === ancestor) {
  106. return true; // Boolean
  107. }
  108. node = node.parentNode;
  109. }
  110. } catch (e) {
  111. return -1; /* squelch */
  112. }
  113. return false; // Boolean
  114. };
  115. dojo.setSelectable = function(/* DomNode|String */node, /* Boolean */
  116. selectable) {
  117. // summary: enable or disable selection on a node
  118. // node:
  119. // id or reference to node
  120. // selectable:
  121. node = dojo.byId(node);
  122. if (dojo.isMozilla) {
  123. node.style.MozUserSelect = selectable ? "" : "none";
  124. } else if (dojo.isKhtml) {
  125. node.style.KhtmlUserSelect = selectable ? "auto" : "none";
  126. } else if (dojo.isIE) {
  127. node.unselectable = selectable ? "" : "on";
  128. dojo.query("*", node).forEach(function(descendant) {
  129. descendant.unselectable = selectable ? "" : "on";
  130. });
  131. }
  132. // FIXME: else? Opera?
  133. };
  134. var _insertBefore = function(/* Node */node, /* Node */ref) {
  135. ref.parentNode.insertBefore(node, ref);
  136. return true; // boolean
  137. }
  138. var _insertAfter = function(/* Node */node, /* Node */ref) {
  139. // summary:
  140. // Try to insert node after ref
  141. var pn = ref.parentNode;
  142. if (ref == pn.lastChild) {
  143. pn.appendChild(node);
  144. } else {
  145. return _insertBefore(node, ref.nextSibling); // boolean
  146. }
  147. return true; // boolean
  148. }
  149. dojo.place = function(/* String|DomNode */node, /* String|DomNode */
  150. refNode, /* String|Number */position) {
  151. // summary:
  152. // attempt to insert node in relation to ref based on position
  153. // node:
  154. // id or reference to node to place relative to refNode
  155. // refNode:
  156. // id or reference of node to use as basis for placement
  157. // position:
  158. // string noting the position of node relative to refNode or a
  159. // number indicating the location in the childNodes collection of
  160. // refNode. Accepted string values are:
  161. // * before
  162. // * after
  163. // * first
  164. // * last
  165. // "first" and "last" indicate positions as children of refNode.
  166. // FIXME: need to write tests for this!!!!
  167. if (!node || !refNode || position === undefined) {
  168. return false; // boolean
  169. }
  170. node = dojo.byId(node);
  171. refNode = dojo.byId(refNode);
  172. if (typeof position == "number") {
  173. var cn = refNode.childNodes;
  174. if ((position == 0 && cn.length == 0) || cn.length == position) {
  175. refNode.appendChild(node);
  176. return true;
  177. }
  178. if (position == 0) {
  179. return _insertBefore(node, refNode.firstChild);
  180. }
  181. return _insertAfter(node, cn[position - 1]);
  182. }
  183. switch (position.toLowerCase()) {
  184. case "before" :
  185. return _insertBefore(node, refNode); // boolean
  186. case "after" :
  187. return _insertAfter(node, refNode); // boolean
  188. case "first" :
  189. if (refNode.firstChild) {
  190. return _insertBefore(node, refNode.firstChild); // boolean
  191. } else {
  192. refNode.appendChild(node);
  193. return true; // boolean
  194. }
  195. break;
  196. default : // aka: last
  197. refNode.appendChild(node);
  198. return true; // boolean
  199. }
  200. }
  201. // Box functions will assume this model.
  202. // On IE/Opera, BORDER_BOX will be set if the primary document is in
  203. // quirks mode.
  204. // Can be set to change behavior of box setters.
  205. // can be either:
  206. // "border-box"
  207. // "content-box" (default)
  208. dojo.boxModel = "content-box";
  209. // We punt per-node box mode testing completely.
  210. // If anybody cares, we can provide an additional (optional) unit
  211. // that overrides existing code to include per-node box sensitivity.
  212. // Opera documentation claims that Opera 9 uses border-box in BackCompat
  213. // mode.
  214. // but experiments (Opera 9.10.8679 on Windows Vista) indicate that it
  215. // actually continues to use content-box.
  216. // IIRC, earlier versions of Opera did in fact use border-box.
  217. // Opera guys, this is really confusing. Opera being broken in quirks
  218. // mode is not our fault.
  219. if (dojo.isIE /* || dojo.isOpera */) {
  220. var _dcm = document.compatMode;
  221. // client code may have to adjust if compatMode varies across
  222. // iframes
  223. dojo.boxModel = (_dcm == "BackCompat") || (_dcm == "QuirksMode")
  224. || (dojo.isIE < 6) ? "border-box" : "content-box";
  225. }
  226. // =============================
  227. // Style Functions
  228. // =============================
  229. // getComputedStyle drives most of the style code.
  230. // Wherever possible, reuse the returned object.
  231. //
  232. // API functions below that need to access computed styles accept an
  233. // optional computedStyle parameter.
  234. //
  235. // If this parameter is omitted, the functions will call
  236. // getComputedStyle themselves.
  237. //
  238. // This way, calling code can access computedStyle once, and then pass
  239. // the reference to
  240. // multiple API functions.
  241. //
  242. // This is a faux declaration to take pity on the doc tool
  243. /*
  244. * ===== dojo.getComputedStyle = function(node){ // summary: // Returns
  245. * a "computed style" object. // description: // get "computed style"
  246. * object which can be used to gather // information about the current
  247. * state of the rendered node. // // Note that this may behave
  248. * differently on different browsers. // Values may have different
  249. * formats and value encodings across // browsers. // // Use the
  250. * dojo.style() method for more consistent (pixelized) // return values. //
  251. * node: DOMNode // a reference to a DOM node. Does NOT support taking
  252. * an // ID string for speed reasons. // example: // |
  253. * dojo.getComputedStyle(dojo.byId('foo')).borderWidth; return; //
  254. * CSS2Properties } =====
  255. */
  256. var gcs, dv = document.defaultView;
  257. if (dojo.isSafari) {
  258. gcs = function(/* DomNode */node) {
  259. var s = dv.getComputedStyle(node, null);
  260. if (!s && node.style) {
  261. node.style.display = "";
  262. s = dv.getComputedStyle(node, null);
  263. }
  264. return s || {};
  265. };
  266. } else if (dojo.isIE) {
  267. gcs = function(node) {
  268. return node.currentStyle;
  269. };
  270. } else {
  271. gcs = function(node) {
  272. return dv.getComputedStyle(node, null);
  273. };
  274. }
  275. dojo.getComputedStyle = gcs;
  276. if (!dojo.isIE) {
  277. dojo._toPixelValue = function(element, value) {
  278. // style values can be floats, client code may want
  279. // to round for integer pixels.
  280. return parseFloat(value) || 0;
  281. }
  282. } else {
  283. dojo._toPixelValue = function(element, avalue) {
  284. if (!avalue) {
  285. return 0;
  286. }
  287. // on IE7, medium is usually 4 pixels
  288. if (avalue == "medium") {
  289. return 4;
  290. }
  291. // style values can be floats, client code may
  292. // want to round this value for integer pixels.
  293. if (avalue.slice && (avalue.slice(-2) == 'px')) {
  294. return parseFloat(avalue);
  295. }
  296. with (element) {
  297. var sLeft = style.left;
  298. var rsLeft = runtimeStyle.left;
  299. runtimeStyle.left = currentStyle.left;
  300. try {
  301. // 'avalue' may be incompatible with style.left, which
  302. // can cause IE to throw
  303. // this has been observed for border widths using
  304. // "thin", "medium", "thick" constants
  305. // those particular constants could be trapped by a
  306. // lookup
  307. // but perhaps there are more
  308. style.left = avalue;
  309. avalue = style.pixelLeft;
  310. } catch (e) {
  311. avalue = 0;
  312. }
  313. style.left = sLeft;
  314. runtimeStyle.left = rsLeft;
  315. }
  316. return avalue;
  317. }
  318. }
  319. // FIXME: there opacity quirks on FF that we haven't ported over. Hrm.
  320. /*
  321. * ===== dojo._getOpacity = function(node){ // summary: // Returns the
  322. * current opacity of the passed node as a // floating-point value
  323. * between 0 and 1. // node: DomNode // a reference to a DOM node. Does
  324. * NOT support taking an // ID string for speed reasons. // return:
  325. * Number between 0 and 1 } =====
  326. */
  327. dojo._getOpacity = (dojo.isIE ? function(node) {
  328. try {
  329. return (node.filters.alpha.opacity / 100); // Number
  330. } catch (e) {
  331. return 1; // Number
  332. }
  333. } : function(node) {
  334. return dojo.getComputedStyle(node).opacity;
  335. });
  336. /*
  337. * ===== dojo._setOpacity = function(node, opacity){ // summary: // set
  338. * the opacity of the passed node portably. Returns the // new opacity
  339. * of the node. // node: DOMNode // a reference to a DOM node. Does NOT
  340. * support taking an // ID string for performance reasons. // opacity:
  341. * Number // A Number between 0 and 1. 0 specifies transparent. //
  342. * return: Number between 0 and 1 } =====
  343. */
  344. dojo._setOpacity = (dojo.isIE ? function(/* DomNode */node, /* Number */
  345. opacity) {
  346. if (opacity == 1) {
  347. // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so
  348. // remove it altogether (bug #2661)
  349. node.style.cssText = node.style.cssText.replace(
  350. /FILTER:[^;]*;/i, "");
  351. if (node.nodeName.toLowerCase() == "tr") {
  352. dojo.query("> td", node).forEach(function(i) {
  353. i.style.cssText = i.style.cssText.replace(
  354. /FILTER:[^;]*;/i, "");
  355. });
  356. }
  357. } else {
  358. var o = "Alpha(Opacity=" + (opacity * 100) + ")";
  359. node.style.filter = o;
  360. }
  361. if (node.nodeName.toLowerCase() == "tr") {
  362. dojo.query("> td", node).forEach(function(i) {
  363. i.style.filter = o;
  364. });
  365. }
  366. return opacity;
  367. } : function(node, opacity) {
  368. return node.style.opacity = opacity;
  369. });
  370. var _pixelNamesCache = {
  371. width : true,
  372. height : true,
  373. left : true,
  374. top : true
  375. };
  376. var _toStyleValue = function(node, type, value) {
  377. type = type.toLowerCase();
  378. if (_pixelNamesCache[type] === true) {
  379. return dojo._toPixelValue(node, value)
  380. } else if (_pixelNamesCache[type] === false) {
  381. return value;
  382. } else {
  383. if (dojo.isOpera && type == "cssText") {
  384. // FIXME: add workaround for #2855 here
  385. }
  386. if ((type.indexOf("margin") >= 0)
  387. ||
  388. // (type.indexOf("border") >= 0) ||
  389. (type.indexOf("padding") >= 0)
  390. || (type.indexOf("width") >= 0)
  391. || (type.indexOf("height") >= 0)
  392. || (type.indexOf("max") >= 0)
  393. || (type.indexOf("min") >= 0)
  394. || (type.indexOf("offset") >= 0)) {
  395. _pixelNamesCache[type] = true;
  396. return dojo._toPixelValue(node, value)
  397. } else {
  398. _pixelNamesCache[type] = false;
  399. return value;
  400. }
  401. }
  402. }
  403. // public API
  404. dojo.style = function(/* DomNode|String */node, /* String */style, /* String? */
  405. value) {
  406. // summary:
  407. // gets or sets a style property on node. If 2 arguments are
  408. // passed, acts as a getter. If value is passed, acts as a setter
  409. // for the property.
  410. // node:
  411. // id or reference to node to get/set style for
  412. // style:
  413. // the style property to set in DOM-accessor format
  414. // ("borderWidth", not "border-width").
  415. // value:
  416. // optional. If passed, sets value on the node for style, handling
  417. // cross-browser concerns.
  418. var n = dojo.byId(node), args = arguments.length, op = (style == "opacity");
  419. if (args == 3) {
  420. return op ? dojo._setOpacity(n, value) : n.style[style] = value; /* Number */
  421. }
  422. if (args == 2 && op) {
  423. return dojo._getOpacity(n);
  424. }
  425. var s = dojo.getComputedStyle(n);
  426. return (args == 1) ? s : _toStyleValue(n, style, s[style]); /* CSS2Properties||String||Number */
  427. }
  428. // =============================
  429. // Box Functions
  430. // =============================
  431. dojo._getPadExtents = function(/* DomNode */n, /* Object */computedStyle) {
  432. // summary:
  433. // Returns object with special values specifically useful for node
  434. // fitting.
  435. // l/t = left/top padding (respectively)
  436. // w = the total of the left and right padding
  437. // h = the total of the top and bottom padding
  438. // If 'node' has position, l/t forms the origin for child nodes.
  439. // The w/h are used for calculating boxes.
  440. // Normally application code will not need to invoke this
  441. // directly, and will use the ...box... functions instead.
  442. var s = computedStyle || gcs(n), px = dojo._toPixelValue, l = px(n,
  443. s.paddingLeft), t = px(n, s.paddingTop);
  444. return {
  445. l : l,
  446. t : t,
  447. w : l + px(n, s.paddingRight),
  448. h : t + px(n, s.paddingBottom)
  449. };
  450. }
  451. dojo._getBorderExtents = function(/* DomNode */n, /* Object */
  452. computedStyle) {
  453. // summary:
  454. // returns an object with properties useful for noting the border
  455. // dimensions.
  456. // l/t = the sum of left/top border (respectively)
  457. // w = the sum of the left and right border
  458. // h = the sum of the top and bottom border
  459. // The w/h are used for calculating boxes.
  460. // Normally application code will not need to invoke this
  461. // directly, and will use the ...box... functions instead.
  462. var ne = 'none', px = dojo._toPixelValue, s = computedStyle
  463. || gcs(n), bl = (s.borderLeftStyle != ne ? px(n,
  464. s.borderLeftWidth) : 0), bt = (s.borderTopStyle != ne ? px(
  465. n, s.borderTopWidth) : 0);
  466. return {
  467. l : bl,
  468. t : bt,
  469. w : bl
  470. + (s.borderRightStyle != ne
  471. ? px(n, s.borderRightWidth)
  472. : 0),
  473. h : bt
  474. + (s.borderBottomStyle != ne ? px(n,
  475. s.borderBottomWidth) : 0)
  476. };
  477. }
  478. dojo._getPadBorderExtents = function(/* DomNode */n, /* Object */
  479. computedStyle) {
  480. // summary:
  481. // returns object with properties useful for box fitting with
  482. // regards to padding.
  483. // l/t = the sum of left/top padding and left/top border
  484. // (respectively)
  485. // w = the sum of the left and right padding and border
  486. // h = the sum of the top and bottom padding and border
  487. // The w/h are used for calculating boxes.
  488. // Normally application code will not need to invoke this
  489. // directly, and will use the ...box... functions instead.
  490. var s = computedStyle || gcs(n), p = dojo._getPadExtents(n, s), b = dojo
  491. ._getBorderExtents(n, s);
  492. return {
  493. l : p.l + b.l,
  494. t : p.t + b.t,
  495. w : p.w + b.w,
  496. h : p.h + b.h
  497. };
  498. }
  499. dojo._getMarginExtents = function(n, computedStyle) {
  500. // summary:
  501. // returns object with properties useful for box fitting with
  502. // regards to box margins (i.e., the outer-box).
  503. // l/t = marginLeft, marginTop, respectively
  504. // w = total width, margin inclusive
  505. // h = total height, margin inclusive
  506. // The w/h are used for calculating boxes.
  507. // Normally application code will not need to invoke this
  508. // directly, and will use the ...box... functions instead.
  509. var s = computedStyle || gcs(n), px = dojo._toPixelValue, l = px(n,
  510. s.marginLeft), t = px(n, s.marginTop), r = px(n,
  511. s.marginRight), b = px(n, s.marginBottom);
  512. if (dojo.isSafari && (s.position != "absolute")) {
  513. // FIXME: Safari's version of the computed right margin
  514. // is the space between our right edge and the right edge
  515. // of our offsetParent.
  516. // What we are looking for is the actual margin value as
  517. // determined by CSS.
  518. // Hack solution is to assume left/right margins are the same.
  519. r = l;
  520. }
  521. return {
  522. l : l,
  523. t : t,
  524. w : l + r,
  525. h : t + b
  526. };
  527. }
  528. // Box getters work in any box context because offsetWidth/clientWidth
  529. // are invariant wrt box context
  530. //
  531. // They do *not* work for display: inline objects that have padding
  532. // styles
  533. // because the user agent ignores padding (it's bogus styling in any
  534. // case)
  535. //
  536. // Be careful with IMGs because they are inline or block depending on
  537. // browser and browser mode.
  538. // Although it would be easier to read, there are not separate versions
  539. // of
  540. // _getMarginBox for each browser because:
  541. // 1. the branching is not expensive
  542. // 2. factoring the shared code wastes cycles (function call overhead)
  543. // 3. duplicating the shared code wastes bytes
  544. dojo._getMarginBox = function(/* DomNode */node, /* Object */
  545. computedStyle) {
  546. // summary:
  547. // returns an object that encodes the width, height, left and top
  548. // positions of the node's margin box.
  549. var s = computedStyle || gcs(node), me = dojo._getMarginExtents(
  550. node, s);
  551. var l = node.offsetLeft - me.l, t = node.offsetTop - me.t;
  552. if (dojo.isMoz) {
  553. // Mozilla:
  554. // If offsetParent has a computed overflow != visible, the
  555. // offsetLeft is decreased
  556. // by the parent's border.
  557. // We don't want to compute the parent's style, so instead we
  558. // examine node's
  559. // computed left/top which is more stable.
  560. var sl = parseFloat(s.left), st = parseFloat(s.top);
  561. if (!isNaN(sl) && !isNaN(st)) {
  562. l = sl, t = st;
  563. } else {
  564. // If child's computed left/top are not parseable as a
  565. // number (e.g. "auto"), we
  566. // have no choice but to examine the parent's computed
  567. // style.
  568. var p = node.parentNode;
  569. if (p && p.style) {
  570. var pcs = gcs(p);
  571. if (pcs.overflow != "visible") {
  572. var be = dojo._getBorderExtents(p, pcs);
  573. l += be.l, t += be.t;
  574. }
  575. }
  576. }
  577. } else if (dojo.isOpera) {
  578. // On Opera, offsetLeft includes the parent's border
  579. var p = node.parentNode;
  580. if (p) {
  581. var be = dojo._getBorderExtents(p);
  582. l -= be.l, t -= be.t;
  583. }
  584. }
  585. return {
  586. l : l,
  587. t : t,
  588. w : node.offsetWidth + me.w,
  589. h : node.offsetHeight + me.h
  590. };
  591. }
  592. dojo._getContentBox = function(node, computedStyle) {
  593. // summary:
  594. // Returns an object that encodes the width, height, left and top
  595. // positions of the node's content box, irrespective of the
  596. // current box model.
  597. // clientWidth/Height are important since the automatically account
  598. // for scrollbars
  599. // fallback to offsetWidth/Height for special cases (see #3378)
  600. var s = computedStyle || gcs(node), pe = dojo._getPadExtents(node,
  601. s), be = dojo._getBorderExtents(node, s), w = node.clientWidth, h;
  602. if (!w) {
  603. w = node.offsetWidth, h = node.offsetHeight;
  604. } else {
  605. h = node.clientHeight, be.w = be.h = 0;
  606. }
  607. // On Opera, offsetLeft includes the parent's border
  608. if (dojo.isOpera) {
  609. pe.l += be.l;
  610. pe.t += be.t;
  611. };
  612. return {
  613. l : pe.l,
  614. t : pe.t,
  615. w : w - pe.w - be.w,
  616. h : h - pe.h - be.h
  617. };
  618. }
  619. dojo._getBorderBox = function(node, computedStyle) {
  620. var s = computedStyle || gcs(node), pe = dojo._getPadExtents(node,
  621. s), cb = dojo._getContentBox(node, s);
  622. return {
  623. l : cb.l - pe.l,
  624. t : cb.t - pe.t,
  625. w : cb.w + pe.w,
  626. h : cb.h + pe.h
  627. };
  628. }
  629. // Box setters depend on box context because interpretation of
  630. // width/height styles
  631. // vary wrt box context.
  632. //
  633. // The value of dojo.boxModel is used to determine box context.
  634. // dojo.boxModel can be set directly to change behavior.
  635. //
  636. // Beware of display: inline objects that have padding styles
  637. // because the user agent ignores padding (it's a bogus setup anyway)
  638. //
  639. // Be careful with IMGs because they are inline or block depending on
  640. // browser and browser mode.
  641. //
  642. // Elements other than DIV may have special quirks, like built-in
  643. // margins or padding, or values not detectable via computedStyle.
  644. // In particular, margins on TABLE do not seems to appear
  645. // at all in computedStyle on Mozilla.
  646. dojo._setBox = function(/* DomNode */node, /* Number? */l, /* Number? */t, /* Number? */
  647. w, /* Number? */h, /* String? */u) {
  648. // summary:
  649. // sets width/height/left/top in the current (native) box-model
  650. // dimentions. Uses the unit passed in u.
  651. // node: DOM Node reference. Id string not supported for performance
  652. // reasons.
  653. // l: optional. left offset from parent.
  654. // t: optional. top offset from parent.
  655. // w: optional. width in current box model.
  656. // h: optional. width in current box model.
  657. // u: optional. unit measure to use for other measures. Defaults to
  658. // "px".
  659. u = u || "px";
  660. with (node.style) {
  661. if (!isNaN(l)) {
  662. left = l + u;
  663. }
  664. if (!isNaN(t)) {
  665. top = t + u;
  666. }
  667. if (w >= 0) {
  668. width = w + u;
  669. }
  670. if (h >= 0) {
  671. height = h + u;
  672. }
  673. }
  674. }
  675. dojo._usesBorderBox = function(/* DomNode */node) {
  676. // summary:
  677. // True if the node uses border-box layout.
  678. // We could test the computed style of node to see if a particular
  679. // box
  680. // has been specified, but there are details and we choose not to
  681. // bother.
  682. var n = node.tagName;
  683. // For whatever reason, TABLE and BUTTON are always border-box by
  684. // default.
  685. // If you have assigned a different box to either one via CSS then
  686. // box functions will break.
  687. return dojo.boxModel == "border-box" || n == "TABLE"
  688. || n == "BUTTON"; // boolean
  689. }
  690. dojo._setContentSize = function(/* DomNode */node, /* Number */widthPx, /* Number */
  691. heightPx, /* Object */computedStyle) {
  692. // summary:
  693. // Sets the size of the node's contents, irrespective of margins,
  694. // padding, or borders.
  695. var bb = dojo._usesBorderBox(node);
  696. if (bb) {
  697. var pb = dojo._getPadBorderExtents(node, computedStyle);
  698. if (widthPx >= 0) {
  699. widthPx += pb.w;
  700. }
  701. if (heightPx >= 0) {
  702. heightPx += pb.h;
  703. }
  704. }
  705. dojo._setBox(node, NaN, NaN, widthPx, heightPx);
  706. }
  707. dojo._setMarginBox = function(/* DomNode */node, /* Number? */leftPx, /* Number? */
  708. topPx,
  709. /* Number? */widthPx, /* Number? */heightPx,
  710. /* Object */computedStyle) {
  711. // summary:
  712. // sets the size of the node's margin box and palcement
  713. // (left/top), irrespective of box model. Think of it as a
  714. // passthrough to dojo._setBox that handles box-model vagaries for
  715. // you.
  716. var s = computedStyle || dojo.getComputedStyle(node);
  717. // Some elements have special padding, margin, and box-model
  718. // settings.
  719. // To use box functions you may need to set padding, margin
  720. // explicitly.
  721. // Controlling box-model is harder, in a pinch you might set
  722. // dojo.boxModel.
  723. var bb = dojo._usesBorderBox(node), pb = bb ? _nilExtents : dojo
  724. ._getPadBorderExtents(node, s), mb = dojo
  725. ._getMarginExtents(node, s);
  726. if (widthPx >= 0) {
  727. widthPx = Math.max(widthPx - pb.w - mb.w, 0);
  728. }
  729. if (heightPx >= 0) {
  730. heightPx = Math.max(heightPx - pb.h - mb.h, 0);
  731. }
  732. dojo._setBox(node, leftPx, topPx, widthPx, heightPx);
  733. }
  734. var _nilExtents = {
  735. l : 0,
  736. t : 0,
  737. w : 0,
  738. h : 0
  739. };
  740. // public API
  741. dojo.marginBox = function(/* DomNode|String */node, /* Object? */box) {
  742. // summary:
  743. // getter/setter for the margin-box of node.
  744. // description:
  745. // Returns an object in the expected format of box (regardless
  746. // if box is passed). The object might look like:
  747. // { l: 50, t: 200, w: 300: h: 150 }
  748. // for a node offset from its parent 50px to the left, 200px from
  749. // the top with a margin width of 300px and a margin-height of
  750. // 150px.
  751. // node:
  752. // id or reference to DOM Node to get/set box for
  753. // box:
  754. // optional. If passed, denotes that dojo.marginBox() should
  755. // update/set the margin box for node. Box is an object in the
  756. // above format. All properties are optional if passed.
  757. var n = dojo.byId(node), s = gcs(n), b = box;
  758. return !b ? dojo._getMarginBox(n, s) : dojo._setMarginBox(n, b.l,
  759. b.t, b.w, b.h, s); // Object
  760. }
  761. dojo.contentBox = function(/* DomNode|String */node, /* Object? */box) {
  762. // summary:
  763. // getter/setter for the content-box of node.
  764. // description:
  765. // Returns an object in the expected format of box (regardless if
  766. // box is passed).
  767. // The object might look like:
  768. // { l: 50, t: 200, w: 300: h: 150 }
  769. // for a node offset from its parent 50px to the left, 200px from
  770. // the top with a content width of 300px and a content-height of
  771. // 150px. Note that the content box may have a much larger border
  772. // or margin box, depending on the box model currently in use and
  773. // CSS values set/inherited for node.
  774. // node:
  775. // id or reference to DOM Node to get/set box for
  776. // box:
  777. // optional. If passed, denotes that dojo.contentBox() should
  778. // update/set the content box for node. Box is an object in the
  779. // above format. All properties are optional if passed.
  780. var n = dojo.byId(node), s = gcs(n), b = box;
  781. return !b ? dojo._getContentBox(n, s) : dojo._setContentSize(n,
  782. b.w, b.h, s); // Object
  783. }
  784. // =============================
  785. // Positioning
  786. // =============================
  787. var _sumAncestorProperties = function(node, prop) {
  788. if (!(node = (node || 0).parentNode)) {
  789. return 0
  790. };
  791. var val, retVal = 0, _b = dojo.body();
  792. while (node && node.style) {
  793. if (gcs(node).position == "fixed") {
  794. return 0;
  795. }
  796. val = node[prop];
  797. if (val) {
  798. retVal += val - 0;
  799. // opera and khtml #body & #html has the same values, we
  800. // only
  801. // need one value
  802. if (node == _b) {
  803. break;
  804. }
  805. }
  806. node = node.parentNode;
  807. }
  808. return retVal; // integer
  809. }
  810. dojo._docScroll = function() {
  811. var _b = dojo.body();
  812. var _w = dojo.global;
  813. var de = dojo.doc.documentElement;
  814. return {
  815. y : (_w.pageYOffset || de.scrollTop || _b.scrollTop || 0),
  816. x : (_w.pageXOffset || dojo._fixIeBiDiScrollLeft(de.scrollLeft)
  817. || _b.scrollLeft || 0)
  818. };
  819. };
  820. dojo._isBodyLtr = function() {
  821. // FIXME: could check html and body tags directly instead of
  822. // computed style? need to ignore case, accept empty values
  823. return !("_bodyLtr" in dojo)
  824. ? dojo._bodyLtr = dojo.getComputedStyle(dojo.body()).direction == "ltr"
  825. : dojo._bodyLtr; // Boolean
  826. }
  827. dojo._getIeDocumentElementOffset = function() {
  828. // summary
  829. // The following values in IE contain an offset:
  830. // event.clientX
  831. // event.clientY
  832. // node.getBoundingClientRect().left
  833. // node.getBoundingClientRect().top
  834. // But other position related values do not contain this offset,
  835. // such as
  836. // node.offsetLeft, node.offsetTop, node.style.left and
  837. // node.style.top.
  838. // The offset is always (2, 2) in LTR direction. When the body is in
  839. // RTL
  840. // direction, the offset counts the width of left scroll bar's
  841. // width.
  842. // This function computes the actual offset.
  843. // NOTE: assumes we're being called in an IE browser
  844. var de = dojo.doc.documentElement;
  845. if (dojo.isIE >= 7) {
  846. return {
  847. x : de.getBoundingClientRect().left,
  848. y : de.getBoundingClientRect().top
  849. }; // Object
  850. } else {
  851. // IE 6.0
  852. return {
  853. x : dojo._isBodyLtr() || window.parent == window
  854. ? de.clientLeft
  855. : de.offsetWidth - de.clientWidth - de.clientLeft,
  856. y : de.clientTop
  857. }; // Object
  858. }
  859. };
  860. dojo._fixIeBiDiScrollLeft = function(/* Integer */scrollLeft) {
  861. // In RTL direction, scrollLeft should be a negative value, but IE
  862. // returns a positive one. All codes using
  863. // documentElement.scrollLeft
  864. // must call this function to fix this error, otherwise the position
  865. // will offset to right when there is a horizonal scrollbar.
  866. if (dojo.isIE && !dojo._isBodyLtr()) {
  867. var de = dojo.doc.documentElement;
  868. return scrollLeft + de.clientWidth - de.scrollWidth; // Integer
  869. }
  870. return scrollLeft; // Integer
  871. }
  872. dojo._abs = function(/* DomNode */node, /* Boolean? */includeScroll) {
  873. // summary:
  874. // Gets the absolute position of the passed element based on the
  875. // document itself. Returns an object of the form:
  876. // { x: 100, y: 300 }
  877. // if includeScroll is passed, the x and y values will include any
  878. // document offsets that may affect the position relative to the
  879. // viewport.
  880. // FIXME: need to decide in the brave-new-world if we're going to be
  881. // margin-box or border-box.
  882. var ownerDocument = node.ownerDocument;
  883. var ret = {
  884. x : 0,
  885. y : 0
  886. };
  887. var hasScroll = false;
  888. // targetBoxType == "border-box"
  889. var db = dojo.body();
  890. if (dojo.isIE) {
  891. var client = node.getBoundingClientRect();
  892. var offset = dojo._getIeDocumentElementOffset();
  893. ret.x = client.left - offset.x;
  894. ret.y = client.top - offset.y;
  895. } else if (ownerDocument["getBoxObjectFor"]) {
  896. // mozilla
  897. var bo = ownerDocument.getBoxObjectFor(node);
  898. ret.x = bo.x - _sumAncestorProperties(node, "scrollLeft");
  899. ret.y = bo.y - _sumAncestorProperties(node, "scrollTop");
  900. } else {
  901. if (node["offsetParent"]) {
  902. hasScroll = true;
  903. var endNode;
  904. // in Safari, if the node is an absolutely positioned child
  905. // of
  906. // the body and the body has a margin the offset of the
  907. // child
  908. // and the body contain the body's margins, so we need to
  909. // end
  910. // at the body
  911. // FIXME: getting contrary results to the above in latest
  912. // WebKit.
  913. if (dojo.isSafari
  914. &&
  915. // (node.style.getPropertyValue("position") ==
  916. // "absolute") &&
  917. (gcs(node).position == "absolute")
  918. && (node.parentNode == db)) {
  919. endNode = db;
  920. } else {
  921. endNode = db.parentNode;
  922. }
  923. if (node.parentNode != db) {
  924. var nd = node;
  925. if (dojo.isOpera || (dojo.isSafari >= 3)) {
  926. nd = db;
  927. }
  928. ret.x -= _sumAncestorProperties(nd, "scrollLeft");
  929. ret.y -= _sumAncestorProperties(nd, "scrollTop");
  930. }
  931. var curnode = node;
  932. do {
  933. var n = curnode["offsetLeft"];
  934. // FIXME: ugly hack to workaround the submenu in
  935. // popupmenu2 does not shown up correctly in opera.
  936. // Someone have a better workaround?
  937. if (!dojo.isOpera || n > 0) {
  938. ret.x += isNaN(n) ? 0 : n;
  939. }
  940. var m = curnode["offsetTop"];
  941. ret.y += isNaN(m) ? 0 : m;
  942. curnode = curnode.offsetParent;
  943. } while ((curnode != endNode) && curnode);
  944. } else if (node["x"] && node["y"]) {
  945. ret.x += isNaN(node.x) ? 0 : node.x;
  946. ret.y += isNaN(node.y) ? 0 : node.y;
  947. }
  948. }
  949. // account for document scrolling
  950. // if offsetParent is used, ret value already includes scroll
  951. // position
  952. // so we may have to actually remove that value if !includeScroll
  953. if (hasScroll || includeScroll) {
  954. var scroll = dojo._docScroll();
  955. var m = hasScroll ? (!includeScroll ? -1 : 0) : 1;
  956. ret.y += m * scroll.y;
  957. ret.x += m * scroll.x;
  958. }
  959. return ret; // object
  960. }
  961. // FIXME: need a setter for coords or a moveTo!!
  962. dojo.coords = function(/* DomNode|String */node, /* Boolean? */
  963. includeScroll) {
  964. // summary:
  965. // Returns an object that measures margin box width/height and
  966. // absolute positioning data from dojo._abs(). Return value will
  967. // be in the form:
  968. // { l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 }
  969. // does not act as a setter. If includeScroll is passed, the x and
  970. // y params are affected as one would expect in dojo._abs().
  971. var n = dojo.byId(node), s = gcs(n), mb = dojo._getMarginBox(n, s);
  972. var abs = dojo._abs(n, includeScroll);
  973. mb.x = abs.x;
  974. mb.y = abs.y;
  975. return mb;
  976. }
  977. })();
  978. // =============================
  979. // (CSS) Class Functions
  980. // =============================
  981. dojo.hasClass = function(/* DomNode|String */node, /* String */classStr) {
  982. // summary:
  983. // Returns whether or not the specified classes are a portion of the
  984. // class list currently applied to the node.
  985. return ((" " + dojo.byId(node).className + " ").indexOf(" " + classStr
  986. + " ") >= 0); // Boolean
  987. };
  988. dojo.addClass = function(/* DomNode|String */node, /* String */classStr) {
  989. // summary:
  990. // Adds the specified classes to the end of the class list on the
  991. // passed node.
  992. node = dojo.byId(node);
  993. var cls = node.className;
  994. if ((" " + cls + " ").indexOf(" " + classStr + " ") < 0) {
  995. node.className = cls + (cls ? ' ' : '') + classStr;
  996. }
  997. };
  998. dojo.removeClass = function(/* DomNode|String */node, /* String */classStr) {
  999. // summary: Removes the specified classes from node.
  1000. node = dojo.byId(node);
  1001. var t = dojo.trim((" " + node.className + " ").replace(" " + classStr
  1002. + " ", " "));
  1003. if (node.className != t) {
  1004. node.className = t;
  1005. }
  1006. };
  1007. dojo.toggleClass = function(/* DomNode|String */node, /* String */classStr, /* Boolean? */
  1008. condition) {
  1009. // summary:
  1010. // Adds a class to node if not present, or removes if present.
  1011. // Pass a boolean condition if you want to explicitly add or remove.
  1012. // condition:
  1013. // If passed, true means to add the class, false means to remove.
  1014. if (condition === undefined) {
  1015. condition = !dojo.hasClass(node, classStr);
  1016. }
  1017. dojo[condition ? "addClass" : "removeClass"](node, classStr);
  1018. };
  1019. }