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