753 lines
26 KiB
JavaScript
753 lines
26 KiB
JavaScript
|
//>>built
|
||
|
define("dojo/dom-geometry", ["./_base/sniff", "./_base/window","./dom", "./dom-style"],
|
||
|
function(has, win, dom, style){
|
||
|
// module:
|
||
|
// dojo/dom-geometry
|
||
|
// summary:
|
||
|
// This module defines the core dojo DOM geometry API.
|
||
|
|
||
|
var geom = {}; // the result object
|
||
|
|
||
|
// Box functions will assume this model.
|
||
|
// On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode.
|
||
|
// Can be set to change behavior of box setters.
|
||
|
|
||
|
// can be either:
|
||
|
// "border-box"
|
||
|
// "content-box" (default)
|
||
|
geom.boxModel = "content-box";
|
||
|
|
||
|
// We punt per-node box mode testing completely.
|
||
|
// If anybody cares, we can provide an additional (optional) unit
|
||
|
// that overrides existing code to include per-node box sensitivity.
|
||
|
|
||
|
// Opera documentation claims that Opera 9 uses border-box in BackCompat mode.
|
||
|
// but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box.
|
||
|
// IIRC, earlier versions of Opera did in fact use border-box.
|
||
|
// Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault.
|
||
|
|
||
|
if(has("ie") /*|| has("opera")*/){
|
||
|
// client code may have to adjust if compatMode varies across iframes
|
||
|
geom.boxModel = document.compatMode == "BackCompat" ? "border-box" : "content-box";
|
||
|
}
|
||
|
|
||
|
// =============================
|
||
|
// Box Functions
|
||
|
// =============================
|
||
|
|
||
|
/*=====
|
||
|
dojo.getPadExtents = function(node, computedStyle){
|
||
|
// summary:
|
||
|
// Returns object with special values specifically useful for node
|
||
|
// fitting.
|
||
|
// description:
|
||
|
// Returns an object with `w`, `h`, `l`, `t` properties:
|
||
|
// | l/t/r/b = left/top/right/bottom padding (respectively)
|
||
|
// | w = the total of the left and right padding
|
||
|
// | h = the total of the top and bottom padding
|
||
|
// If 'node' has position, l/t forms the origin for child nodes.
|
||
|
// The w/h are used for calculating boxes.
|
||
|
// Normally application code will not need to invoke this
|
||
|
// directly, and will use the ...box... functions instead.
|
||
|
// node: DOMNode
|
||
|
// computedStyle: Object?
|
||
|
// This parameter accepts computed styles object.
|
||
|
// If this parameter is omitted, the functions will call
|
||
|
// dojo.getComputedStyle to get one. It is a better way, calling
|
||
|
// dojo.computedStyle once, and then pass the reference to this
|
||
|
// computedStyle parameter. Wherever possible, reuse the returned
|
||
|
// object of dojo.getComputedStyle.
|
||
|
|
||
|
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo._getPadExtents = function(node, computedStyle){
|
||
|
// summary:
|
||
|
// Existing alias for `dojo.getPadExtents`. Deprecated, will be removed in 2.0.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo.getBorderExtents = function(node, computedStyle){
|
||
|
// summary:
|
||
|
// returns an object with properties useful for noting the border
|
||
|
// dimensions.
|
||
|
// description:
|
||
|
// * l/t/r/b = the sum of left/top/right/bottom border (respectively)
|
||
|
// * w = the sum of the left and right border
|
||
|
// * h = the sum of the top and bottom border
|
||
|
//
|
||
|
// The w/h are used for calculating boxes.
|
||
|
// Normally application code will not need to invoke this
|
||
|
// directly, and will use the ...box... functions instead.
|
||
|
// node: DOMNode
|
||
|
// computedStyle: Object?
|
||
|
// This parameter accepts computed styles object.
|
||
|
// If this parameter is omitted, the functions will call
|
||
|
// dojo.getComputedStyle to get one. It is a better way, calling
|
||
|
// dojo.computedStyle once, and then pass the reference to this
|
||
|
// computedStyle parameter. Wherever possible, reuse the returned
|
||
|
// object of dojo.getComputedStyle.
|
||
|
|
||
|
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo._getBorderExtents = function(node, computedStyle){
|
||
|
// summary:
|
||
|
// Existing alias for `dojo.getBorderExtents`. Deprecated, will be removed in 2.0.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo.getPadBorderExtents = function(node, computedStyle){
|
||
|
// summary:
|
||
|
// Returns object with properties useful for box fitting with
|
||
|
// regards to padding.
|
||
|
// description:
|
||
|
// * l/t/r/b = the sum of left/top/right/bottom padding and left/top/right/bottom border (respectively)
|
||
|
// * w = the sum of the left and right padding and border
|
||
|
// * h = the sum of the top and bottom padding and border
|
||
|
//
|
||
|
// The w/h are used for calculating boxes.
|
||
|
// Normally application code will not need to invoke this
|
||
|
// directly, and will use the ...box... functions instead.
|
||
|
// node: DOMNode
|
||
|
// computedStyle: Object?
|
||
|
// This parameter accepts computed styles object.
|
||
|
// If this parameter is omitted, the functions will call
|
||
|
// dojo.getComputedStyle to get one. It is a better way, calling
|
||
|
// dojo.computedStyle once, and then pass the reference to this
|
||
|
// computedStyle parameter. Wherever possible, reuse the returned
|
||
|
// object of dojo.getComputedStyle.
|
||
|
|
||
|
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo._getPadBorderExtents = function(node, computedStyle){
|
||
|
// summary:
|
||
|
// Existing alias for `dojo.getPadBorderExtents`. Deprecated, will be removed in 2.0.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo.getMarginExtents = function(node, computedStyle){
|
||
|
// summary:
|
||
|
// returns object with properties useful for box fitting with
|
||
|
// regards to box margins (i.e., the outer-box).
|
||
|
//
|
||
|
// * l/t = marginLeft, marginTop, respectively
|
||
|
// * w = total width, margin inclusive
|
||
|
// * h = total height, margin inclusive
|
||
|
//
|
||
|
// The w/h are used for calculating boxes.
|
||
|
// Normally application code will not need to invoke this
|
||
|
// directly, and will use the ...box... functions instead.
|
||
|
// node: DOMNode
|
||
|
// computedStyle: Object?
|
||
|
// This parameter accepts computed styles object.
|
||
|
// If this parameter is omitted, the functions will call
|
||
|
// dojo.getComputedStyle to get one. It is a better way, calling
|
||
|
// dojo.computedStyle once, and then pass the reference to this
|
||
|
// computedStyle parameter. Wherever possible, reuse the returned
|
||
|
// object of dojo.getComputedStyle.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo._getMarginExtents = function(node, computedStyle){
|
||
|
// summary:
|
||
|
// Existing alias for `dojo.getMarginExtents`. Deprecated, will be removed in 2.0.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo.getMarginSize = function(node, computedStyle){
|
||
|
// summary:
|
||
|
// returns an object that encodes the width and height of
|
||
|
// the node's margin box
|
||
|
// node: DOMNode|String
|
||
|
// computedStyle: Object?
|
||
|
// This parameter accepts computed styles object.
|
||
|
// If this parameter is omitted, the functions will call
|
||
|
// dojo.getComputedStyle to get one. It is a better way, calling
|
||
|
// dojo.computedStyle once, and then pass the reference to this
|
||
|
// computedStyle parameter. Wherever possible, reuse the returned
|
||
|
// object of dojo.getComputedStyle.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo._getMarginSize = function(node, computedStyle){
|
||
|
// summary:
|
||
|
// Existing alias for `dojo.getMarginSize`. Deprecated, will be removed in 2.0.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo.getMarginBox = function(node, computedStyle){
|
||
|
// summary:
|
||
|
// returns an object that encodes the width, height, left and top
|
||
|
// positions of the node's margin box.
|
||
|
// node: DOMNode
|
||
|
// computedStyle: Object?
|
||
|
// This parameter accepts computed styles object.
|
||
|
// If this parameter is omitted, the functions will call
|
||
|
// dojo.getComputedStyle to get one. It is a better way, calling
|
||
|
// dojo.computedStyle once, and then pass the reference to this
|
||
|
// computedStyle parameter. Wherever possible, reuse the returned
|
||
|
// object of dojo.getComputedStyle.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo._getMarginBox = function(node, computedStyle){
|
||
|
// summary:
|
||
|
// Existing alias for `dojo.getMarginBox`. Deprecated, will be removed in 2.0.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo.setMarginBox = function(node, box, computedStyle){
|
||
|
// summary:
|
||
|
// sets the size of the node's margin box and placement
|
||
|
// (left/top), irrespective of box model. Think of it as a
|
||
|
// passthrough to setBox that handles box-model vagaries for
|
||
|
// you.
|
||
|
// node: DOMNode
|
||
|
// box: Object
|
||
|
// hash with optional "l", "t", "w", and "h" properties for "left", "right", "width", and "height"
|
||
|
// respectively. All specified properties should have numeric values in whole pixels.
|
||
|
// computedStyle: Object?
|
||
|
// This parameter accepts computed styles object.
|
||
|
// If this parameter is omitted, the functions will call
|
||
|
// dojo.getComputedStyle to get one. It is a better way, calling
|
||
|
// dojo.computedStyle once, and then pass the reference to this
|
||
|
// computedStyle parameter. Wherever possible, reuse the returned
|
||
|
// object of dojo.getComputedStyle.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo.getContentBox = function(node, computedStyle){
|
||
|
// summary:
|
||
|
// Returns an object that encodes the width, height, left and top
|
||
|
// positions of the node's content box, irrespective of the
|
||
|
// current box model.
|
||
|
// node: DOMNode
|
||
|
// computedStyle: Object?
|
||
|
// This parameter accepts computed styles object.
|
||
|
// If this parameter is omitted, the functions will call
|
||
|
// dojo.getComputedStyle to get one. It is a better way, calling
|
||
|
// dojo.computedStyle once, and then pass the reference to this
|
||
|
// computedStyle parameter. Wherever possible, reuse the returned
|
||
|
// object of dojo.getComputedStyle.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo._getContentBox = function(node, computedStyle){
|
||
|
// summary:
|
||
|
// Existing alias for `dojo.getContentBox`. Deprecated, will be removed in 2.0.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo.setContentSize = function(node, box, computedStyle){
|
||
|
// summary:
|
||
|
// Sets the size of the node's contents, irrespective of margins,
|
||
|
// padding, or borders.
|
||
|
// node: DOMNode
|
||
|
// box: Object
|
||
|
// hash with optional "w", and "h" properties for "width", and "height"
|
||
|
// respectively. All specified properties should have numeric values in whole pixels.
|
||
|
// computedStyle: Object?
|
||
|
// This parameter accepts computed styles object.
|
||
|
// If this parameter is omitted, the functions will call
|
||
|
// dojo.getComputedStyle to get one. It is a better way, calling
|
||
|
// dojo.computedStyle once, and then pass the reference to this
|
||
|
// computedStyle parameter. Wherever possible, reuse the returned
|
||
|
// object of dojo.getComputedStyle.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo.isBodyLtr = function(){
|
||
|
// summary:
|
||
|
// Returns true if the current language is left-to-right, and false otherwise.
|
||
|
// returns: Boolean
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo._isBodyLtr = function(){
|
||
|
// summary:
|
||
|
// Existing alias for `dojo.isBodyLtr`. Deprecated, will be removed in 2.0.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo.docScroll = function(){
|
||
|
// summary:
|
||
|
// Returns an object with {node, x, y} with corresponding offsets.
|
||
|
// returns: Object
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo._docScroll = function(){
|
||
|
// summary:
|
||
|
// Existing alias for `dojo.docScroll`. Deprecated, will be removed in 2.0.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo.getIeDocumentElementOffset = function(){
|
||
|
// summary:
|
||
|
// returns the offset in x and y from the document body to the
|
||
|
// visual edge of the page for IE
|
||
|
// description:
|
||
|
// The following values in IE contain an offset:
|
||
|
// | event.clientX
|
||
|
// | event.clientY
|
||
|
// | node.getBoundingClientRect().left
|
||
|
// | node.getBoundingClientRect().top
|
||
|
// But other position related values do not contain this offset,
|
||
|
// such as node.offsetLeft, node.offsetTop, node.style.left and
|
||
|
// node.style.top. The offset is always (2, 2) in LTR direction.
|
||
|
// When the body is in RTL direction, the offset counts the width
|
||
|
// of left scroll bar's width. This function computes the actual
|
||
|
// offset.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo._getIeDocumentElementOffset = function(){
|
||
|
// summary:
|
||
|
// Existing alias for `dojo.getIeDocumentElementOffset`. Deprecated, will be removed in 2.0.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo.fixIeBiDiScrollLeft = function(scrollLeft){
|
||
|
// summary:
|
||
|
// In RTL direction, scrollLeft should be a negative value, but IE
|
||
|
// returns a positive one. All codes using documentElement.scrollLeft
|
||
|
// must call this function to fix this error, otherwise the position
|
||
|
// will offset to right when there is a horizontal scrollbar.
|
||
|
// scrollLeft: NUmber
|
||
|
// returns: Number
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo._fixIeBiDiScrollLeft = function(scrollLeft){
|
||
|
// summary:
|
||
|
// Existing alias for `dojo.fixIeBiDiScrollLeft`. Deprecated, will be removed in 2.0.
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
/*=====
|
||
|
dojo.position = function(node, includeScroll){
|
||
|
// summary:
|
||
|
// Gets the position and size of the passed element relative to
|
||
|
// the viewport (if includeScroll==false), or relative to the
|
||
|
// document root (if includeScroll==true).
|
||
|
//
|
||
|
// description:
|
||
|
// Returns an object of the form:
|
||
|
// { x: 100, y: 300, w: 20, h: 15 }
|
||
|
// If includeScroll==true, the x and y values will include any
|
||
|
// document offsets that may affect the position relative to the
|
||
|
// viewport.
|
||
|
// Uses the border-box model (inclusive of border and padding but
|
||
|
// not margin). Does not act as a setter.
|
||
|
// node: DOMNode|String
|
||
|
// includeScroll: Boolean?
|
||
|
// returns: Object
|
||
|
};
|
||
|
=====*/
|
||
|
|
||
|
geom.getPadExtents = function getPadExtents(/*DomNode*/node, /*Object*/computedStyle){
|
||
|
node = dom.byId(node);
|
||
|
var s = computedStyle || style.getComputedStyle(node), px = style.toPixelValue,
|
||
|
l = px(node, s.paddingLeft), t = px(node, s.paddingTop), r = px(node, s.paddingRight), b = px(node, s.paddingBottom);
|
||
|
return {l: l, t: t, r: r, b: b, w: l + r, h: t + b};
|
||
|
};
|
||
|
|
||
|
var none = "none";
|
||
|
|
||
|
geom.getBorderExtents = function getBorderExtents(/*DomNode*/node, /*Object*/computedStyle){
|
||
|
node = dom.byId(node);
|
||
|
var px = style.toPixelValue, s = computedStyle || style.getComputedStyle(node),
|
||
|
l = s.borderLeftStyle != none ? px(node, s.borderLeftWidth) : 0,
|
||
|
t = s.borderTopStyle != none ? px(node, s.borderTopWidth) : 0,
|
||
|
r = s.borderRightStyle != none ? px(node, s.borderRightWidth) : 0,
|
||
|
b = s.borderBottomStyle != none ? px(node, s.borderBottomWidth) : 0;
|
||
|
return {l: l, t: t, r: r, b: b, w: l + r, h: t + b};
|
||
|
};
|
||
|
|
||
|
geom.getPadBorderExtents = function getPadBorderExtents(/*DomNode*/node, /*Object*/computedStyle){
|
||
|
node = dom.byId(node);
|
||
|
var s = computedStyle || style.getComputedStyle(node),
|
||
|
p = geom.getPadExtents(node, s),
|
||
|
b = geom.getBorderExtents(node, s);
|
||
|
return {
|
||
|
l: p.l + b.l,
|
||
|
t: p.t + b.t,
|
||
|
r: p.r + b.r,
|
||
|
b: p.b + b.b,
|
||
|
w: p.w + b.w,
|
||
|
h: p.h + b.h
|
||
|
};
|
||
|
};
|
||
|
|
||
|
geom.getMarginExtents = function getMarginExtents(node, computedStyle){
|
||
|
node = dom.byId(node);
|
||
|
var s = computedStyle || style.getComputedStyle(node), px = style.toPixelValue,
|
||
|
l = px(node, s.marginLeft), t = px(node, s.marginTop), r = px(node, s.marginRight), b = px(node, s.marginBottom);
|
||
|
if(has("webkit") && (s.position != "absolute")){
|
||
|
// FIXME: Safari's version of the computed right margin
|
||
|
// is the space between our right edge and the right edge
|
||
|
// of our offsetParent.
|
||
|
// What we are looking for is the actual margin value as
|
||
|
// determined by CSS.
|
||
|
// Hack solution is to assume left/right margins are the same.
|
||
|
r = l;
|
||
|
}
|
||
|
return {l: l, t: t, r: r, b: b, w: l + r, h: t + b};
|
||
|
};
|
||
|
|
||
|
// Box getters work in any box context because offsetWidth/clientWidth
|
||
|
// are invariant wrt box context
|
||
|
//
|
||
|
// They do *not* work for display: inline objects that have padding styles
|
||
|
// because the user agent ignores padding (it's bogus styling in any case)
|
||
|
//
|
||
|
// Be careful with IMGs because they are inline or block depending on
|
||
|
// browser and browser mode.
|
||
|
|
||
|
// Although it would be easier to read, there are not separate versions of
|
||
|
// _getMarginBox for each browser because:
|
||
|
// 1. the branching is not expensive
|
||
|
// 2. factoring the shared code wastes cycles (function call overhead)
|
||
|
// 3. duplicating the shared code wastes bytes
|
||
|
|
||
|
geom.getMarginBox = function getMarginBox(/*DomNode*/node, /*Object*/computedStyle){
|
||
|
// summary:
|
||
|
// returns an object that encodes the width, height, left and top
|
||
|
// positions of the node's margin box.
|
||
|
node = dom.byId(node);
|
||
|
var s = computedStyle || style.getComputedStyle(node), me = geom.getMarginExtents(node, s),
|
||
|
l = node.offsetLeft - me.l, t = node.offsetTop - me.t, p = node.parentNode, px = style.toPixelValue, pcs;
|
||
|
if(has("mozilla")){
|
||
|
// Mozilla:
|
||
|
// If offsetParent has a computed overflow != visible, the offsetLeft is decreased
|
||
|
// by the parent's border.
|
||
|
// We don't want to compute the parent's style, so instead we examine node's
|
||
|
// computed left/top which is more stable.
|
||
|
var sl = parseFloat(s.left), st = parseFloat(s.top);
|
||
|
if(!isNaN(sl) && !isNaN(st)){
|
||
|
l = sl, t = st;
|
||
|
}else{
|
||
|
// If child's computed left/top are not parseable as a number (e.g. "auto"), we
|
||
|
// have no choice but to examine the parent's computed style.
|
||
|
if(p && p.style){
|
||
|
pcs = style.getComputedStyle(p);
|
||
|
if(pcs.overflow != "visible"){
|
||
|
l += pcs.borderLeftStyle != none ? px(node, pcs.borderLeftWidth) : 0;
|
||
|
t += pcs.borderTopStyle != none ? px(node, pcs.borderTopWidth) : 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}else if(has("opera") || (has("ie") == 8 && !has("quirks"))){
|
||
|
// On Opera and IE 8, offsetLeft/Top includes the parent's border
|
||
|
if(p){
|
||
|
pcs = style.getComputedStyle(p);
|
||
|
l -= pcs.borderLeftStyle != none ? px(node, pcs.borderLeftWidth) : 0;
|
||
|
t -= pcs.borderTopStyle != none ? px(node, pcs.borderTopWidth) : 0;
|
||
|
}
|
||
|
}
|
||
|
return {l: l, t: t, w: node.offsetWidth + me.w, h: node.offsetHeight + me.h};
|
||
|
};
|
||
|
|
||
|
geom.getContentBox = function getContentBox(node, computedStyle){
|
||
|
// clientWidth/Height are important since the automatically account for scrollbars
|
||
|
// fallback to offsetWidth/Height for special cases (see #3378)
|
||
|
node = dom.byId(node);
|
||
|
var s = computedStyle || style.getComputedStyle(node), w = node.clientWidth, h,
|
||
|
pe = geom.getPadExtents(node, s), be = geom.getBorderExtents(node, s);
|
||
|
if(!w){
|
||
|
w = node.offsetWidth;
|
||
|
h = node.offsetHeight;
|
||
|
}else{
|
||
|
h = node.clientHeight;
|
||
|
be.w = be.h = 0;
|
||
|
}
|
||
|
// On Opera, offsetLeft includes the parent's border
|
||
|
if(has("opera")){
|
||
|
pe.l += be.l;
|
||
|
pe.t += be.t;
|
||
|
}
|
||
|
return {l: pe.l, t: pe.t, w: w - pe.w - be.w, h: h - pe.h - be.h};
|
||
|
};
|
||
|
|
||
|
// Box setters depend on box context because interpretation of width/height styles
|
||
|
// vary wrt box context.
|
||
|
//
|
||
|
// The value of dojo.boxModel is used to determine box context.
|
||
|
// dojo.boxModel can be set directly to change behavior.
|
||
|
//
|
||
|
// Beware of display: inline objects that have padding styles
|
||
|
// because the user agent ignores padding (it's a bogus setup anyway)
|
||
|
//
|
||
|
// Be careful with IMGs because they are inline or block depending on
|
||
|
// browser and browser mode.
|
||
|
//
|
||
|
// Elements other than DIV may have special quirks, like built-in
|
||
|
// margins or padding, or values not detectable via computedStyle.
|
||
|
// In particular, margins on TABLE do not seems to appear
|
||
|
// at all in computedStyle on Mozilla.
|
||
|
|
||
|
function setBox(/*DomNode*/node, /*Number?*/l, /*Number?*/t, /*Number?*/w, /*Number?*/h, /*String?*/u){
|
||
|
// summary:
|
||
|
// sets width/height/left/top in the current (native) box-model
|
||
|
// dimensions. Uses the unit passed in u.
|
||
|
// node:
|
||
|
// DOM Node reference. Id string not supported for performance
|
||
|
// reasons.
|
||
|
// l:
|
||
|
// left offset from parent.
|
||
|
// t:
|
||
|
// top offset from parent.
|
||
|
// w:
|
||
|
// width in current box model.
|
||
|
// h:
|
||
|
// width in current box model.
|
||
|
// u:
|
||
|
// unit measure to use for other measures. Defaults to "px".
|
||
|
u = u || "px";
|
||
|
var s = node.style;
|
||
|
if(!isNaN(l)){
|
||
|
s.left = l + u;
|
||
|
}
|
||
|
if(!isNaN(t)){
|
||
|
s.top = t + u;
|
||
|
}
|
||
|
if(w >= 0){
|
||
|
s.width = w + u;
|
||
|
}
|
||
|
if(h >= 0){
|
||
|
s.height = h + u;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function isButtonTag(/*DomNode*/node){
|
||
|
// summary:
|
||
|
// True if the node is BUTTON or INPUT.type="button".
|
||
|
return node.tagName.toLowerCase() == "button" ||
|
||
|
node.tagName.toLowerCase() == "input" && (node.getAttribute("type") || "").toLowerCase() == "button"; // boolean
|
||
|
}
|
||
|
|
||
|
function usesBorderBox(/*DomNode*/node){
|
||
|
// summary:
|
||
|
// True if the node uses border-box layout.
|
||
|
|
||
|
// We could test the computed style of node to see if a particular box
|
||
|
// has been specified, but there are details and we choose not to bother.
|
||
|
|
||
|
// TABLE and BUTTON (and INPUT type=button) are always border-box by default.
|
||
|
// If you have assigned a different box to either one via CSS then
|
||
|
// box functions will break.
|
||
|
|
||
|
return geom.boxModel == "border-box" || node.tagName.toLowerCase() == "table" || isButtonTag(node); // boolean
|
||
|
}
|
||
|
|
||
|
geom.setContentSize = function setContentSize(/*DomNode*/node, /*Object*/box, /*Object*/computedStyle){
|
||
|
// summary:
|
||
|
// Sets the size of the node's contents, irrespective of margins,
|
||
|
// padding, or borders.
|
||
|
|
||
|
node = dom.byId(node);
|
||
|
var w = box.w, h = box.h;
|
||
|
if(usesBorderBox(node)){
|
||
|
var pb = geom.getPadBorderExtents(node, computedStyle);
|
||
|
if(w >= 0){
|
||
|
w += pb.w;
|
||
|
}
|
||
|
if(h >= 0){
|
||
|
h += pb.h;
|
||
|
}
|
||
|
}
|
||
|
setBox(node, NaN, NaN, w, h);
|
||
|
};
|
||
|
|
||
|
var nilExtents = {l: 0, t: 0, w: 0, h: 0};
|
||
|
|
||
|
geom.setMarginBox = function setMarginBox(/*DomNode*/node, /*Object*/box, /*Object*/computedStyle){
|
||
|
node = dom.byId(node);
|
||
|
var s = computedStyle || style.getComputedStyle(node), w = box.w, h = box.h,
|
||
|
// Some elements have special padding, margin, and box-model settings.
|
||
|
// To use box functions you may need to set padding, margin explicitly.
|
||
|
// Controlling box-model is harder, in a pinch you might set dojo.boxModel.
|
||
|
pb = usesBorderBox(node) ? nilExtents : geom.getPadBorderExtents(node, s),
|
||
|
mb = geom.getMarginExtents(node, s);
|
||
|
if(has("webkit")){
|
||
|
// on Safari (3.1.2), button nodes with no explicit size have a default margin
|
||
|
// setting an explicit size eliminates the margin.
|
||
|
// We have to swizzle the width to get correct margin reading.
|
||
|
if(isButtonTag(node)){
|
||
|
var ns = node.style;
|
||
|
if(w >= 0 && !ns.width){
|
||
|
ns.width = "4px";
|
||
|
}
|
||
|
if(h >= 0 && !ns.height){
|
||
|
ns.height = "4px";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if(w >= 0){
|
||
|
w = Math.max(w - pb.w - mb.w, 0);
|
||
|
}
|
||
|
if(h >= 0){
|
||
|
h = Math.max(h - pb.h - mb.h, 0);
|
||
|
}
|
||
|
setBox(node, box.l, box.t, w, h);
|
||
|
};
|
||
|
|
||
|
// =============================
|
||
|
// Positioning
|
||
|
// =============================
|
||
|
|
||
|
geom.isBodyLtr = function isBodyLtr(){
|
||
|
return (win.body().dir || win.doc.documentElement.dir || "ltr").toLowerCase() == "ltr"; // Boolean
|
||
|
};
|
||
|
|
||
|
geom.docScroll = function docScroll(){
|
||
|
var node = win.doc.parentWindow || win.doc.defaultView; // use UI window, not dojo.global window
|
||
|
return "pageXOffset" in node ? {x: node.pageXOffset, y: node.pageYOffset } :
|
||
|
(node = has("quirks") ? win.body() : win.doc.documentElement,
|
||
|
{x: geom.fixIeBiDiScrollLeft(node.scrollLeft || 0), y: node.scrollTop || 0 });
|
||
|
};
|
||
|
|
||
|
geom.getIeDocumentElementOffset = function getIeDocumentElementOffset(){
|
||
|
//NOTE: assumes we're being called in an IE browser
|
||
|
|
||
|
var de = win.doc.documentElement; // only deal with HTML element here, position() handles body/quirks
|
||
|
|
||
|
if(has("ie") < 8){
|
||
|
var r = de.getBoundingClientRect(), // works well for IE6+
|
||
|
l = r.left, t = r.top;
|
||
|
if(has("ie") < 7){
|
||
|
l += de.clientLeft; // scrollbar size in strict/RTL, or,
|
||
|
t += de.clientTop; // HTML border size in strict
|
||
|
}
|
||
|
return {
|
||
|
x: l < 0 ? 0 : l, // FRAME element border size can lead to inaccurate negative values
|
||
|
y: t < 0 ? 0 : t
|
||
|
};
|
||
|
}else{
|
||
|
return {
|
||
|
x: 0,
|
||
|
y: 0
|
||
|
};
|
||
|
}
|
||
|
};
|
||
|
|
||
|
geom.fixIeBiDiScrollLeft = function fixIeBiDiScrollLeft(/*Integer*/ scrollLeft){
|
||
|
// In RTL direction, scrollLeft should be a negative value, but IE
|
||
|
// returns a positive one. All codes using documentElement.scrollLeft
|
||
|
// must call this function to fix this error, otherwise the position
|
||
|
// will offset to right when there is a horizontal scrollbar.
|
||
|
|
||
|
var ie = has("ie");
|
||
|
if(ie && !geom.isBodyLtr()){
|
||
|
var qk = has("quirks"),
|
||
|
de = qk ? win.body() : win.doc.documentElement;
|
||
|
if(ie == 6 && !qk && win.global.frameElement && de.scrollHeight > de.clientHeight){
|
||
|
scrollLeft += de.clientLeft; // workaround ie6+strict+rtl+iframe+vertical-scrollbar bug where clientWidth is too small by clientLeft pixels
|
||
|
}
|
||
|
return (ie < 8 || qk) ? (scrollLeft + de.clientWidth - de.scrollWidth) : -scrollLeft; // Integer
|
||
|
}
|
||
|
return scrollLeft; // Integer
|
||
|
};
|
||
|
|
||
|
geom.position = function(/*DomNode*/node, /*Boolean?*/includeScroll){
|
||
|
node = dom.byId(node);
|
||
|
var db = win.body(),
|
||
|
dh = db.parentNode,
|
||
|
ret = node.getBoundingClientRect();
|
||
|
ret = {x: ret.left, y: ret.top, w: ret.right - ret.left, h: ret.bottom - ret.top};
|
||
|
if(has("ie")){
|
||
|
// On IE there's a 2px offset that we need to adjust for, see dojo.getIeDocumentElementOffset()
|
||
|
var offset = geom.getIeDocumentElementOffset();
|
||
|
|
||
|
// fixes the position in IE, quirks mode
|
||
|
ret.x -= offset.x + (has("quirks") ? db.clientLeft + db.offsetLeft : 0);
|
||
|
ret.y -= offset.y + (has("quirks") ? db.clientTop + db.offsetTop : 0);
|
||
|
}else if(has("ff") == 3){
|
||
|
// In FF3 you have to subtract the document element margins.
|
||
|
// Fixed in FF3.5 though.
|
||
|
var cs = style.getComputedStyle(dh), px = style.toPixelValue;
|
||
|
ret.x -= px(dh, cs.marginLeft) + px(dh, cs.borderLeftWidth);
|
||
|
ret.y -= px(dh, cs.marginTop) + px(dh, cs.borderTopWidth);
|
||
|
}
|
||
|
// account for document scrolling
|
||
|
// if offsetParent is used, ret value already includes scroll position
|
||
|
// so we may have to actually remove that value if !includeScroll
|
||
|
if(includeScroll){
|
||
|
var scroll = geom.docScroll();
|
||
|
ret.x += scroll.x;
|
||
|
ret.y += scroll.y;
|
||
|
}
|
||
|
|
||
|
return ret; // Object
|
||
|
};
|
||
|
|
||
|
// random "private" functions wildly used throughout the toolkit
|
||
|
|
||
|
geom.getMarginSize = function getMarginSize(/*DomNode*/node, /*Object*/computedStyle){
|
||
|
node = dom.byId(node);
|
||
|
var me = geom.getMarginExtents(node, computedStyle || style.getComputedStyle(node));
|
||
|
var size = node.getBoundingClientRect();
|
||
|
return {
|
||
|
w: (size.right - size.left) + me.w,
|
||
|
h: (size.bottom - size.top) + me.h
|
||
|
}
|
||
|
};
|
||
|
|
||
|
geom.normalizeEvent = function(event){
|
||
|
// summary:
|
||
|
// Normalizes the geometry of a DOM event, normalizing the pageX, pageY,
|
||
|
// offsetX, offsetY, layerX, and layerX properties
|
||
|
// event: Object
|
||
|
if(!("layerX" in event)){
|
||
|
event.layerX = event.offsetX;
|
||
|
event.layerY = event.offsetY;
|
||
|
}
|
||
|
if(!has("dom-addeventlistener")){
|
||
|
// old IE version
|
||
|
// FIXME: scroll position query is duped from dojo.html to
|
||
|
// avoid dependency on that entire module. Now that HTML is in
|
||
|
// Base, we should convert back to something similar there.
|
||
|
var se = event.target;
|
||
|
var doc = (se && se.ownerDocument) || document;
|
||
|
// DO NOT replace the following to use dojo.body(), in IE, document.documentElement should be used
|
||
|
// here rather than document.body
|
||
|
var docBody = has("quirks") ? doc.body : doc.documentElement;
|
||
|
var offset = geom.getIeDocumentElementOffset();
|
||
|
event.pageX = event.clientX + geom.fixIeBiDiScrollLeft(docBody.scrollLeft || 0) - offset.x;
|
||
|
event.pageY = event.clientY + (docBody.scrollTop || 0) - offset.y;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// TODO: evaluate separate getters/setters for position and sizes?
|
||
|
|
||
|
return geom;
|
||
|
});
|