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