//>>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;
});
