1 | define(["./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/dom-style.getComputedStyle to get one. It is a better way, calling |
---|
53 | // dojo/dom-style.getComputedStyle 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/dom-style.getComputedStyle to get one. It is a better way, calling |
---|
82 | // dojo/dom-style.getComputedStyle 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/dom-style.getComputedStyle to get one. It is a better way, calling |
---|
112 | // dojo/dom-style.getComputedStyle 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/dom-style.getComputedStyle to get one. It is a better way, calling |
---|
147 | // dojo/dom-style.getComputedStyle 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/dom-style.getComputedStyle to get one. It is a better way, calling |
---|
181 | // dojo/dom-style.getComputedStyle 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/dom-style.getComputedStyle to get one. It is a better way, calling |
---|
230 | // dojo/dom-style.getComputedStyle 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/dom-style.getComputedStyle to get one. It is a better way, calling |
---|
337 | // dojo/dom-style.getComputedStyle 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/dom-style.getComputedStyle to get one. It is a better way, calling |
---|
371 | // dojo/dom-style.getComputedStyle 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/dom-style.getComputedStyle to get one. It is a better way, calling |
---|
564 | // dojo/dom-style.getComputedStyle 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/_base/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/_base/window.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 | }); |
---|