dom-geometry.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. define(["./sniff", "./_base/window","./dom", "./dom-style"],
  2. function(has, win, dom, style){
  3. // module:
  4. // dojo/dom-geometry
  5. // the result object
  6. var geom = {
  7. // summary:
  8. // This module defines the core dojo DOM geometry API.
  9. };
  10. // Box functions will assume this model.
  11. // On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode.
  12. // Can be set to change behavior of box setters.
  13. // can be either:
  14. // "border-box"
  15. // "content-box" (default)
  16. geom.boxModel = "content-box";
  17. // We punt per-node box mode testing completely.
  18. // If anybody cares, we can provide an additional (optional) unit
  19. // that overrides existing code to include per-node box sensitivity.
  20. // Opera documentation claims that Opera 9 uses border-box in BackCompat mode.
  21. // but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box.
  22. // IIRC, earlier versions of Opera did in fact use border-box.
  23. // Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault.
  24. if(has("ie") /*|| has("opera")*/){
  25. // client code may have to adjust if compatMode varies across iframes
  26. geom.boxModel = document.compatMode == "BackCompat" ? "border-box" : "content-box";
  27. }
  28. geom.getPadExtents = function getPadExtents(/*DomNode*/ node, /*Object*/ computedStyle){
  29. // summary:
  30. // Returns object with special values specifically useful for node
  31. // fitting.
  32. // description:
  33. // Returns an object with `w`, `h`, `l`, `t` properties:
  34. // | l/t/r/b = left/top/right/bottom padding (respectively)
  35. // | w = the total of the left and right padding
  36. // | h = the total of the top and bottom padding
  37. // If 'node' has position, l/t forms the origin for child nodes.
  38. // The w/h are used for calculating boxes.
  39. // Normally application code will not need to invoke this
  40. // directly, and will use the ...box... functions instead.
  41. // node: DOMNode
  42. // computedStyle: Object?
  43. // This parameter accepts computed styles object.
  44. // If this parameter is omitted, the functions will call
  45. // dojo/dom-style.getComputedStyle to get one. It is a better way, calling
  46. // dojo/dom-style.getComputedStyle once, and then pass the reference to this
  47. // computedStyle parameter. Wherever possible, reuse the returned
  48. // object of dojo/dom-style.getComputedStyle().
  49. node = dom.byId(node);
  50. var s = computedStyle || style.getComputedStyle(node), px = style.toPixelValue,
  51. l = px(node, s.paddingLeft), t = px(node, s.paddingTop), r = px(node, s.paddingRight), b = px(node, s.paddingBottom);
  52. return {l: l, t: t, r: r, b: b, w: l + r, h: t + b};
  53. };
  54. var none = "none";
  55. geom.getBorderExtents = function getBorderExtents(/*DomNode*/ node, /*Object*/ computedStyle){
  56. // summary:
  57. // returns an object with properties useful for noting the border
  58. // dimensions.
  59. // description:
  60. // - l/t/r/b = the sum of left/top/right/bottom border (respectively)
  61. // - w = the sum of the left and right border
  62. // - h = the sum of the top and bottom border
  63. //
  64. // The w/h are used for calculating boxes.
  65. // Normally application code will not need to invoke this
  66. // directly, and will use the ...box... functions instead.
  67. // node: DOMNode
  68. // computedStyle: Object?
  69. // This parameter accepts computed styles object.
  70. // If this parameter is omitted, the functions will call
  71. // dojo/dom-style.getComputedStyle to get one. It is a better way, calling
  72. // dojo/dom-style.getComputedStyle once, and then pass the reference to this
  73. // computedStyle parameter. Wherever possible, reuse the returned
  74. // object of dojo/dom-style.getComputedStyle().
  75. node = dom.byId(node);
  76. var px = style.toPixelValue, s = computedStyle || style.getComputedStyle(node),
  77. l = s.borderLeftStyle != none ? px(node, s.borderLeftWidth) : 0,
  78. t = s.borderTopStyle != none ? px(node, s.borderTopWidth) : 0,
  79. r = s.borderRightStyle != none ? px(node, s.borderRightWidth) : 0,
  80. b = s.borderBottomStyle != none ? px(node, s.borderBottomWidth) : 0;
  81. return {l: l, t: t, r: r, b: b, w: l + r, h: t + b};
  82. };
  83. geom.getPadBorderExtents = function getPadBorderExtents(/*DomNode*/ node, /*Object*/ computedStyle){
  84. // summary:
  85. // Returns object with properties useful for box fitting with
  86. // regards to padding.
  87. // description:
  88. // - l/t/r/b = the sum of left/top/right/bottom padding and left/top/right/bottom border (respectively)
  89. // - w = the sum of the left and right padding and border
  90. // - h = the sum of the top and bottom padding and border
  91. //
  92. // The w/h are used for calculating boxes.
  93. // Normally application code will not need to invoke this
  94. // directly, and will use the ...box... functions instead.
  95. // node: DOMNode
  96. // computedStyle: Object?
  97. // This parameter accepts computed styles object.
  98. // If this parameter is omitted, the functions will call
  99. // dojo/dom-style.getComputedStyle to get one. It is a better way, calling
  100. // dojo/dom-style.getComputedStyle once, and then pass the reference to this
  101. // computedStyle parameter. Wherever possible, reuse the returned
  102. // object of dojo/dom-style.getComputedStyle().
  103. node = dom.byId(node);
  104. var s = computedStyle || style.getComputedStyle(node),
  105. p = geom.getPadExtents(node, s),
  106. b = geom.getBorderExtents(node, s);
  107. return {
  108. l: p.l + b.l,
  109. t: p.t + b.t,
  110. r: p.r + b.r,
  111. b: p.b + b.b,
  112. w: p.w + b.w,
  113. h: p.h + b.h
  114. };
  115. };
  116. geom.getMarginExtents = function getMarginExtents(node, computedStyle){
  117. // summary:
  118. // returns object with properties useful for box fitting with
  119. // regards to box margins (i.e., the outer-box).
  120. //
  121. // - l/t = marginLeft, marginTop, respectively
  122. // - w = total width, margin inclusive
  123. // - h = total height, margin inclusive
  124. //
  125. // The w/h are used for calculating boxes.
  126. // Normally application code will not need to invoke this
  127. // directly, and will use the ...box... functions instead.
  128. // node: DOMNode
  129. // computedStyle: Object?
  130. // This parameter accepts computed styles object.
  131. // If this parameter is omitted, the functions will call
  132. // dojo/dom-style.getComputedStyle to get one. It is a better way, calling
  133. // dojo/dom-style.getComputedStyle once, and then pass the reference to this
  134. // computedStyle parameter. Wherever possible, reuse the returned
  135. // object of dojo/dom-style.getComputedStyle().
  136. node = dom.byId(node);
  137. var s = computedStyle || style.getComputedStyle(node), px = style.toPixelValue,
  138. l = px(node, s.marginLeft), t = px(node, s.marginTop), r = px(node, s.marginRight), b = px(node, s.marginBottom);
  139. return {l: l, t: t, r: r, b: b, w: l + r, h: t + b};
  140. };
  141. // Box getters work in any box context because offsetWidth/clientWidth
  142. // are invariant wrt box context
  143. //
  144. // They do *not* work for display: inline objects that have padding styles
  145. // because the user agent ignores padding (it's bogus styling in any case)
  146. //
  147. // Be careful with IMGs because they are inline or block depending on
  148. // browser and browser mode.
  149. // Although it would be easier to read, there are not separate versions of
  150. // _getMarginBox for each browser because:
  151. // 1. the branching is not expensive
  152. // 2. factoring the shared code wastes cycles (function call overhead)
  153. // 3. duplicating the shared code wastes bytes
  154. geom.getMarginBox = function getMarginBox(/*DomNode*/ node, /*Object*/ computedStyle){
  155. // summary:
  156. // returns an object that encodes the width, height, left and top
  157. // positions of the node's margin box.
  158. // node: DOMNode
  159. // computedStyle: Object?
  160. // This parameter accepts computed styles object.
  161. // If this parameter is omitted, the functions will call
  162. // dojo/dom-style.getComputedStyle to get one. It is a better way, calling
  163. // dojo/dom-style.getComputedStyle once, and then pass the reference to this
  164. // computedStyle parameter. Wherever possible, reuse the returned
  165. // object of dojo/dom-style.getComputedStyle().
  166. node = dom.byId(node);
  167. var s = computedStyle || style.getComputedStyle(node), me = geom.getMarginExtents(node, s),
  168. l = node.offsetLeft - me.l, t = node.offsetTop - me.t, p = node.parentNode, px = style.toPixelValue, pcs;
  169. if(has("mozilla")){
  170. // Mozilla:
  171. // If offsetParent has a computed overflow != visible, the offsetLeft is decreased
  172. // by the parent's border.
  173. // We don't want to compute the parent's style, so instead we examine node's
  174. // computed left/top which is more stable.
  175. var sl = parseFloat(s.left), st = parseFloat(s.top);
  176. if(!isNaN(sl) && !isNaN(st)){
  177. l = sl;
  178. t = st;
  179. }else{
  180. // If child's computed left/top are not parseable as a number (e.g. "auto"), we
  181. // have no choice but to examine the parent's computed style.
  182. if(p && p.style){
  183. pcs = style.getComputedStyle(p);
  184. if(pcs.overflow != "visible"){
  185. l += pcs.borderLeftStyle != none ? px(node, pcs.borderLeftWidth) : 0;
  186. t += pcs.borderTopStyle != none ? px(node, pcs.borderTopWidth) : 0;
  187. }
  188. }
  189. }
  190. }else if(has("opera") || (has("ie") == 8 && !has("quirks"))){
  191. // On Opera and IE 8, offsetLeft/Top includes the parent's border
  192. if(p){
  193. pcs = style.getComputedStyle(p);
  194. l -= pcs.borderLeftStyle != none ? px(node, pcs.borderLeftWidth) : 0;
  195. t -= pcs.borderTopStyle != none ? px(node, pcs.borderTopWidth) : 0;
  196. }
  197. }
  198. return {l: l, t: t, w: node.offsetWidth + me.w, h: node.offsetHeight + me.h};
  199. };
  200. geom.getContentBox = function getContentBox(node, computedStyle){
  201. // summary:
  202. // Returns an object that encodes the width, height, left and top
  203. // positions of the node's content box, irrespective of the
  204. // current box model.
  205. // node: DOMNode
  206. // computedStyle: Object?
  207. // This parameter accepts computed styles object.
  208. // If this parameter is omitted, the functions will call
  209. // dojo/dom-style.getComputedStyle to get one. It is a better way, calling
  210. // dojo/dom-style.getComputedStyle once, and then pass the reference to this
  211. // computedStyle parameter. Wherever possible, reuse the returned
  212. // object of dojo/dom-style.getComputedStyle().
  213. // clientWidth/Height are important since the automatically account for scrollbars
  214. // fallback to offsetWidth/Height for special cases (see #3378)
  215. node = dom.byId(node);
  216. var s = computedStyle || style.getComputedStyle(node), w = node.clientWidth, h,
  217. pe = geom.getPadExtents(node, s), be = geom.getBorderExtents(node, s);
  218. if(!w){
  219. w = node.offsetWidth;
  220. h = node.offsetHeight;
  221. }else{
  222. h = node.clientHeight;
  223. be.w = be.h = 0;
  224. }
  225. // On Opera, offsetLeft includes the parent's border
  226. if(has("opera")){
  227. pe.l += be.l;
  228. pe.t += be.t;
  229. }
  230. return {l: pe.l, t: pe.t, w: w - pe.w - be.w, h: h - pe.h - be.h};
  231. };
  232. // Box setters depend on box context because interpretation of width/height styles
  233. // vary wrt box context.
  234. //
  235. // The value of boxModel is used to determine box context.
  236. // boxModel can be set directly to change behavior.
  237. //
  238. // Beware of display: inline objects that have padding styles
  239. // because the user agent ignores padding (it's a bogus setup anyway)
  240. //
  241. // Be careful with IMGs because they are inline or block depending on
  242. // browser and browser mode.
  243. //
  244. // Elements other than DIV may have special quirks, like built-in
  245. // margins or padding, or values not detectable via computedStyle.
  246. // In particular, margins on TABLE do not seems to appear
  247. // at all in computedStyle on Mozilla.
  248. function setBox(/*DomNode*/ node, /*Number?*/ l, /*Number?*/ t, /*Number?*/ w, /*Number?*/ h, /*String?*/ u){
  249. // summary:
  250. // sets width/height/left/top in the current (native) box-model
  251. // dimensions. Uses the unit passed in u.
  252. // node:
  253. // DOM Node reference. Id string not supported for performance
  254. // reasons.
  255. // l:
  256. // left offset from parent.
  257. // t:
  258. // top offset from parent.
  259. // w:
  260. // width in current box model.
  261. // h:
  262. // width in current box model.
  263. // u:
  264. // unit measure to use for other measures. Defaults to "px".
  265. u = u || "px";
  266. var s = node.style;
  267. if(!isNaN(l)){
  268. s.left = l + u;
  269. }
  270. if(!isNaN(t)){
  271. s.top = t + u;
  272. }
  273. if(w >= 0){
  274. s.width = w + u;
  275. }
  276. if(h >= 0){
  277. s.height = h + u;
  278. }
  279. }
  280. function isButtonTag(/*DomNode*/ node){
  281. // summary:
  282. // True if the node is BUTTON or INPUT.type="button".
  283. return node.tagName.toLowerCase() == "button" ||
  284. node.tagName.toLowerCase() == "input" && (node.getAttribute("type") || "").toLowerCase() == "button"; // boolean
  285. }
  286. function usesBorderBox(/*DomNode*/ node){
  287. // summary:
  288. // True if the node uses border-box layout.
  289. // We could test the computed style of node to see if a particular box
  290. // has been specified, but there are details and we choose not to bother.
  291. // TABLE and BUTTON (and INPUT type=button) are always border-box by default.
  292. // If you have assigned a different box to either one via CSS then
  293. // box functions will break.
  294. return geom.boxModel == "border-box" || node.tagName.toLowerCase() == "table" || isButtonTag(node); // boolean
  295. }
  296. geom.setContentSize = function setContentSize(/*DomNode*/ node, /*Object*/ box, /*Object*/ computedStyle){
  297. // summary:
  298. // Sets the size of the node's contents, irrespective of margins,
  299. // padding, or borders.
  300. // node: DOMNode
  301. // box: Object
  302. // hash with optional "w", and "h" properties for "width", and "height"
  303. // respectively. All specified properties should have numeric values in whole pixels.
  304. // computedStyle: Object?
  305. // This parameter accepts computed styles object.
  306. // If this parameter is omitted, the functions will call
  307. // dojo/dom-style.getComputedStyle to get one. It is a better way, calling
  308. // dojo/dom-style.getComputedStyle once, and then pass the reference to this
  309. // computedStyle parameter. Wherever possible, reuse the returned
  310. // object of dojo/dom-style.getComputedStyle().
  311. node = dom.byId(node);
  312. var w = box.w, h = box.h;
  313. if(usesBorderBox(node)){
  314. var pb = geom.getPadBorderExtents(node, computedStyle);
  315. if(w >= 0){
  316. w += pb.w;
  317. }
  318. if(h >= 0){
  319. h += pb.h;
  320. }
  321. }
  322. setBox(node, NaN, NaN, w, h);
  323. };
  324. var nilExtents = {l: 0, t: 0, w: 0, h: 0};
  325. geom.setMarginBox = function setMarginBox(/*DomNode*/ node, /*Object*/ box, /*Object*/ computedStyle){
  326. // summary:
  327. // sets the size of the node's margin box and placement
  328. // (left/top), irrespective of box model. Think of it as a
  329. // passthrough to setBox that handles box-model vagaries for
  330. // you.
  331. // node: DOMNode
  332. // box: Object
  333. // hash with optional "l", "t", "w", and "h" properties for "left", "right", "width", and "height"
  334. // respectively. All specified properties should have numeric values in whole pixels.
  335. // computedStyle: Object?
  336. // This parameter accepts computed styles object.
  337. // If this parameter is omitted, the functions will call
  338. // dojo/dom-style.getComputedStyle to get one. It is a better way, calling
  339. // dojo/dom-style.getComputedStyle once, and then pass the reference to this
  340. // computedStyle parameter. Wherever possible, reuse the returned
  341. // object of dojo/dom-style.getComputedStyle().
  342. node = dom.byId(node);
  343. var s = computedStyle || style.getComputedStyle(node), w = box.w, h = box.h,
  344. // Some elements have special padding, margin, and box-model settings.
  345. // To use box functions you may need to set padding, margin explicitly.
  346. // Controlling box-model is harder, in a pinch you might set dojo/dom-geometry.boxModel.
  347. pb = usesBorderBox(node) ? nilExtents : geom.getPadBorderExtents(node, s),
  348. mb = geom.getMarginExtents(node, s);
  349. if(has("webkit")){
  350. // on Safari (3.1.2), button nodes with no explicit size have a default margin
  351. // setting an explicit size eliminates the margin.
  352. // We have to swizzle the width to get correct margin reading.
  353. if(isButtonTag(node)){
  354. var ns = node.style;
  355. if(w >= 0 && !ns.width){
  356. ns.width = "4px";
  357. }
  358. if(h >= 0 && !ns.height){
  359. ns.height = "4px";
  360. }
  361. }
  362. }
  363. if(w >= 0){
  364. w = Math.max(w - pb.w - mb.w, 0);
  365. }
  366. if(h >= 0){
  367. h = Math.max(h - pb.h - mb.h, 0);
  368. }
  369. setBox(node, box.l, box.t, w, h);
  370. };
  371. // =============================
  372. // Positioning
  373. // =============================
  374. geom.isBodyLtr = function isBodyLtr(/*Document?*/ doc){
  375. // summary:
  376. // Returns true if the current language is left-to-right, and false otherwise.
  377. // doc: Document?
  378. // Optional document to query. If unspecified, use win.doc.
  379. // returns: Boolean
  380. doc = doc || win.doc;
  381. return (win.body(doc).dir || doc.documentElement.dir || "ltr").toLowerCase() == "ltr"; // Boolean
  382. };
  383. geom.docScroll = function docScroll(/*Document?*/ doc){
  384. // summary:
  385. // Returns an object with {node, x, y} with corresponding offsets.
  386. // doc: Document?
  387. // Optional document to query. If unspecified, use win.doc.
  388. // returns: Object
  389. doc = doc || win.doc;
  390. var node = win.doc.parentWindow || win.doc.defaultView; // use UI window, not dojo.global window. TODO: use dojo/window::get() except for circular dependency problem
  391. return "pageXOffset" in node ? {x: node.pageXOffset, y: node.pageYOffset } :
  392. (node = has("quirks") ? win.body(doc) : doc.documentElement) &&
  393. {x: geom.fixIeBiDiScrollLeft(node.scrollLeft || 0, doc), y: node.scrollTop || 0 };
  394. };
  395. if(has("ie")){
  396. geom.getIeDocumentElementOffset = function getIeDocumentElementOffset(/*Document?*/ doc){
  397. // summary:
  398. // returns the offset in x and y from the document body to the
  399. // visual edge of the page for IE
  400. // doc: Document?
  401. // Optional document to query. If unspecified, use win.doc.
  402. // description:
  403. // The following values in IE contain an offset:
  404. // | event.clientX
  405. // | event.clientY
  406. // | node.getBoundingClientRect().left
  407. // | node.getBoundingClientRect().top
  408. // But other position related values do not contain this offset,
  409. // such as node.offsetLeft, node.offsetTop, node.style.left and
  410. // node.style.top. The offset is always (2, 2) in LTR direction.
  411. // When the body is in RTL direction, the offset counts the width
  412. // of left scroll bar's width. This function computes the actual
  413. // offset.
  414. //NOTE: assumes we're being called in an IE browser
  415. doc = doc || win.doc;
  416. var de = doc.documentElement; // only deal with HTML element here, position() handles body/quirks
  417. if(has("ie") < 8){
  418. var r = de.getBoundingClientRect(), // works well for IE6+
  419. l = r.left, t = r.top;
  420. if(has("ie") < 7){
  421. l += de.clientLeft; // scrollbar size in strict/RTL, or,
  422. t += de.clientTop; // HTML border size in strict
  423. }
  424. return {
  425. x: l < 0 ? 0 : l, // FRAME element border size can lead to inaccurate negative values
  426. y: t < 0 ? 0 : t
  427. };
  428. }else{
  429. return {
  430. x: 0,
  431. y: 0
  432. };
  433. }
  434. };
  435. }
  436. geom.fixIeBiDiScrollLeft = function fixIeBiDiScrollLeft(/*Integer*/ scrollLeft, /*Document?*/ doc){
  437. // summary:
  438. // In RTL direction, scrollLeft should be a negative value, but IE
  439. // returns a positive one. All codes using documentElement.scrollLeft
  440. // must call this function to fix this error, otherwise the position
  441. // will offset to right when there is a horizontal scrollbar.
  442. // scrollLeft: Number
  443. // doc: Document?
  444. // Optional document to query. If unspecified, use win.doc.
  445. // returns: Number
  446. // In RTL direction, scrollLeft should be a negative value, but IE
  447. // returns a positive one. All codes using documentElement.scrollLeft
  448. // must call this function to fix this error, otherwise the position
  449. // will offset to right when there is a horizontal scrollbar.
  450. doc = doc || win.doc;
  451. var ie = has("ie");
  452. if(ie && !geom.isBodyLtr(doc)){
  453. var qk = has("quirks"),
  454. de = qk ? win.body(doc) : doc.documentElement,
  455. pwin = win.global; // TODO: use winUtils.get(doc) after resolving circular dependency b/w dom-geometry.js and dojo/window.js
  456. if(ie == 6 && !qk && pwin.frameElement && de.scrollHeight > de.clientHeight){
  457. scrollLeft += de.clientLeft; // workaround ie6+strict+rtl+iframe+vertical-scrollbar bug where clientWidth is too small by clientLeft pixels
  458. }
  459. return (ie < 8 || qk) ? (scrollLeft + de.clientWidth - de.scrollWidth) : -scrollLeft; // Integer
  460. }
  461. return scrollLeft; // Integer
  462. };
  463. geom.position = function(/*DomNode*/ node, /*Boolean?*/ includeScroll){
  464. // summary:
  465. // Gets the position and size of the passed element relative to
  466. // the viewport (if includeScroll==false), or relative to the
  467. // document root (if includeScroll==true).
  468. //
  469. // description:
  470. // Returns an object of the form:
  471. // `{ x: 100, y: 300, w: 20, h: 15 }`.
  472. // If includeScroll==true, the x and y values will include any
  473. // document offsets that may affect the position relative to the
  474. // viewport.
  475. // Uses the border-box model (inclusive of border and padding but
  476. // not margin). Does not act as a setter.
  477. // node: DOMNode|String
  478. // includeScroll: Boolean?
  479. // returns: Object
  480. node = dom.byId(node);
  481. var db = win.body(node.ownerDocument),
  482. ret = node.getBoundingClientRect();
  483. ret = {x: ret.left, y: ret.top, w: ret.right - ret.left, h: ret.bottom - ret.top};
  484. if(has("ie") < 9){
  485. // On IE<9 there's a 2px offset that we need to adjust for, see dojo.getIeDocumentElementOffset()
  486. var offset = geom.getIeDocumentElementOffset(node.ownerDocument);
  487. // fixes the position in IE, quirks mode
  488. ret.x -= offset.x + (has("quirks") ? db.clientLeft + db.offsetLeft : 0);
  489. ret.y -= offset.y + (has("quirks") ? db.clientTop + db.offsetTop : 0);
  490. }
  491. // account for document scrolling
  492. // if offsetParent is used, ret value already includes scroll position
  493. // so we may have to actually remove that value if !includeScroll
  494. if(includeScroll){
  495. var scroll = geom.docScroll(node.ownerDocument);
  496. ret.x += scroll.x;
  497. ret.y += scroll.y;
  498. }
  499. return ret; // Object
  500. };
  501. // random "private" functions wildly used throughout the toolkit
  502. geom.getMarginSize = function getMarginSize(/*DomNode*/ node, /*Object*/ computedStyle){
  503. // summary:
  504. // returns an object that encodes the width and height of
  505. // the node's margin box
  506. // node: DOMNode|String
  507. // computedStyle: Object?
  508. // This parameter accepts computed styles object.
  509. // If this parameter is omitted, the functions will call
  510. // dojo/dom-style.getComputedStyle to get one. It is a better way, calling
  511. // dojo/dom-style.getComputedStyle once, and then pass the reference to this
  512. // computedStyle parameter. Wherever possible, reuse the returned
  513. // object of dojo/dom-style.getComputedStyle().
  514. node = dom.byId(node);
  515. var me = geom.getMarginExtents(node, computedStyle || style.getComputedStyle(node));
  516. var size = node.getBoundingClientRect();
  517. return {
  518. w: (size.right - size.left) + me.w,
  519. h: (size.bottom - size.top) + me.h
  520. };
  521. };
  522. geom.normalizeEvent = function(event){
  523. // summary:
  524. // Normalizes the geometry of a DOM event, normalizing the pageX, pageY,
  525. // offsetX, offsetY, layerX, and layerX properties
  526. // event: Object
  527. if(!("layerX" in event)){
  528. event.layerX = event.offsetX;
  529. event.layerY = event.offsetY;
  530. }
  531. if(!has("dom-addeventlistener")){
  532. // old IE version
  533. // FIXME: scroll position query is duped from dojo/_base/html to
  534. // avoid dependency on that entire module. Now that HTML is in
  535. // Base, we should convert back to something similar there.
  536. var se = event.target;
  537. var doc = (se && se.ownerDocument) || document;
  538. // DO NOT replace the following to use dojo/_base/window.body(), in IE, document.documentElement should be used
  539. // here rather than document.body
  540. var docBody = has("quirks") ? doc.body : doc.documentElement;
  541. var offset = geom.getIeDocumentElementOffset(doc);
  542. event.pageX = event.clientX + geom.fixIeBiDiScrollLeft(docBody.scrollLeft || 0, doc) - offset.x;
  543. event.pageY = event.clientY + (docBody.scrollTop || 0) - offset.y;
  544. }
  545. };
  546. // TODO: evaluate separate getters/setters for position and sizes?
  547. return geom;
  548. });