var Position = (function() {
	// Resolve a string identifier to an object
	// ========================================
	function resolveObject(s) {
		if (document.getElementById && document.getElementById(s)!=null) {
			return document.getElementById(s);
		}
		else if (document.all && document.all[s]!=null) {
			return document.all[s];
		}
		else if (document.anchors && document.anchors.length && document.anchors.length>0 && document.anchors[0].x) {
			for (var i=0; i<document.anchors.length; i++) {
				if (document.anchors[i].name==s) { 
					return document.anchors[i]
				}
			}
		}
	}
	
	var pos = {};
	pos.$VERSION = 1.0;
	
	// Set the position of an object
	// =============================
	pos.set = function(o,left,top) {
		if (typeof(o)=="string") {
			o = resolveObject(o);
		}
		if (o==null || !o.style) {
			return false;
		}
		
		// If the second parameter is an object, it is assumed to be the result of getPosition()
		if (typeof(left)=="object") {
			var pos = left;
			left = pos.left;
			top = pos.top;
		}
		
		o.style.left = left + "px";
		o.style.top = top + "px";
		return true;
	};
	
	// Retrieve the position and size of an object
	// ===========================================
	pos.get = function(o) {
		var fixBrowserQuirks = true;
	
	 	//START_DEBUG
		var debugTrace = "";
		var tracePadding = 0;
	
		function trace(str) {
			for (var i=0; i<tracePadding; i++) {
				debugTrace += " ";
			}
			debugTrace += str + "\n";
		}
	
		//END_DEBUG
		// If a string is passed in instead of an object ref, resolve it
		if (typeof(o)=="string") {
			o = resolveObject(o);
		}
		
		if (o==null) {
			return null;
		}
		
		var left = 0;
		var top = 0;
		var width = 0;
		var height = 0;
		var parentNode = null;
		var offsetParent = null;
	
		trace("Finding position of tag: "+(o.nodeName||o.tagName));//DEBUG
		trace("Walking up the parentNode chain");//DEBUG
		
		offsetParent = o.offsetParent;
		var originalObject = o;
		var el = o; // "el" will be nodes as we walk up, "o" will be saved for offsetParent references
		while (el.parentNode!=null) {
			tracePadding += 2;//DEBUG
			el = el.parentNode;
			trace("Parent node is "+(el.nodeName||el.tagName));//DEBUG
			if (el.offsetParent==null) {
				trace("Root element has been reached, so scrolling on it will not be considered");//DEBUG
			}
			else {
				var considerScroll = true;
				/*
				In Opera, if parentNode of the first object is scrollable, then offsetLeft/offsetTop already 
				take its scroll position into account. If elements further up the chain are scrollable, their 
				scroll offsets still need to be added in. And for some reason, TR nodes have a scrolltop value
				which must be ignored.
				*/
				if (fixBrowserQuirks && window.opera) {
					if (el==originalObject.parentNode || el.nodeName=="TR") {
						considerScroll = false;
					}
				}
				if (considerScroll) {
					if (el.scrollTop && el.scrollTop>0) {
						trace("scrollTop of this element is "+el.scrollTop+", which will be subtracted from total");//DEBUG
						top -= el.scrollTop;
					}
					if (el.scrollLeft && el.scrollLeft>0) {
						trace("scrollLeft of this element is "+el.scrollLeft+", which will be subtracted from total");//DEBUG
						left -= el.scrollLeft;
					}
				}
			}
			// If this node is also the offsetParent, add on the offsets and reset to the new offsetParent
			if (el == offsetParent) {
				trace("This node is the offsetParent for "+o.nodeName);//DEBUG
				trace("offsetLeft = "+o.offsetLeft);//DEBUG
				trace("offsetTop = "+o.offsetTop);//DEBUG
				left += o.offsetLeft;
				if (el.clientLeft && el.nodeName!="TABLE") { 
					trace(el.nodeName + " has clientLeft value of "+el.clientLeft);//DEBUG
					left += el.clientLeft;
				}
				top += o.offsetTop;
				if (el.clientTop && el.nodeName!="TABLE") {
					trace(el.nodeName + " has clientTop value of "+el.clientTop);//DEBUG
					top += el.clientTop;
				}
				o = el;
				if (o.offsetParent==null) {
					if (o.offsetLeft) {
						trace("offsetParent is null, but offsetLeft of this element still exists: "+o.offsetLeft);//DEBUG
						left += o.offsetLeft;
					}
					if (o.offsetTop) {
						trace("offsetParent is null, but offsetTop of this element still exists: "+o.offsetTop);//DEBUG
						top += o.offsetTop;
					}
				}
				offsetParent = o.offsetParent;
			}
			trace("left="+left);//DEBUG
			trace("top="+top);//DEBUG
		}
		
		tracePadding = 0;//DEBUG
		trace("left="+left);//DEBUG
		trace("top="+top);//DEBUG
	
		if (originalObject.offsetWidth) {
			width = originalObject.offsetWidth;
			trace("width="+width);//DEBUG
		}
		if (originalObject.offsetHeight) {
			height = originalObject.offsetHeight;
			trace("height="+height);//DEBUG
		}
		
		return {'left':left, 'top':top, 'width':width, 'height':height
				,'trace':debugTrace//DEBUG
				};
	};
	
	// Retrieve the position of an object's center point
	// =================================================
	pos.getCenter = function(o) {
		var c = this.get(o);
		if (c==null) { return null; }
		c.left = c.left + (c.width/2);
		c.top = c.top + (c.height/2);
		return c;
	};
	
	return pos;
})();
