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