// get element by id or element (renamed to $$ as $ is used for thickbox)
function $$(e) {return _$(window,e);}

function _$(wnd,e) {
	_e = (typeof e == "string" ? wnd.document.getElementById(e) : e);
	return (!_e && (wnd.parent != wnd)) ? _$(wnd.parent,e) : _e;
}

// add event handler after original event handler if defined
function doAfter(obj,prop,func) {
	var oldf = obj[prop] ? obj[prop] : null;
	obj[prop] = (oldf) ? function() { oldf.apply(this,arguments); func.apply(this,arguments); } : func;	
}


Canvas = {

	/*
		Computes element position.
		Returns { left : x, top : x}
	*/
	findPosition : function(e) {
		var obj = $$(e);
		var curleft = curtop = 0;
		if (obj.offsetParent) {
			curleft = obj.offsetLeft;
			curtop = obj.offsetTop;
			while (obj = obj.offsetParent) {
				curleft += obj.offsetLeft;
				curtop += obj.offsetTop;
			}
		}
		return { left : curleft, top : curtop };
	},

	/*
		Moves element to specified (in pixels) place on the screen.
		Element style.position must be set to "absolute"
	*/
	moveTo : function(e,left,top) {
		var obj = $$(e);
		obj.style.left=left + "px";
		obj.style.top=top + "px";
	},	
	
	/*
		Moves element to target element location (top-left corner),
		tries to keep element on screen if keeponscreen flag is specified.
	*/
	moveToElement : function(e,target,keeponscreen) {
		var tpos = this.findPosition(target);
		if(keeponscreen) {
			var eSize = this.elementSize(e);	
			var vSize = this.viewportSize();

			if((tpos.top + eSize.height) > vSize.height) {
				tpos.top-=(tpos.top + eSize.height) - vSize.height;
			}
			
			if((tpos.left + eSize.width) > vSize.width) {
				tpos.left-=(tpost.left + eSize.width) - vSize.width;
			}
		}
		this.moveTo(e,tpos.left,tpos.top);
	},

	/*
		Resizes element to target width/height in pixels.
	*/
	resizeTo : function(e,width,height) {
			e.style.width=Math.max(0,width) + "px";
			e.style.height=Math.max(0,height) + "px";
	},

	/*
		Resize element to target element width/height
	*/
	resizeToElement : function(e,target) {
			var wh = this.elementSize(target);
			this.resizeTo(e,wh.width,wh.height);
	},

	/*
		Computes visible viewport (visible screen area) size.
		Returns { width : x, height : x}
	*/
	viewportSize : function(w) {
		var wnd = (typeof w != "undefined") ? w : window;

		var height = wnd.innerHeight 
				? wnd.innerHeight 
				: (wnd.document.compatMode == 'CSS1Compat' ? wnd.document.documentElement.clientHeight
									   : (wnd.document.body ? wnd.document.body.clientHeight : null)); 
			
		var width =  wnd.innerWidth
				? wnd.innerWidth
				: (wnd.document.compatMode == 'CSS1Compat' ? wnd.document.documentElement.clientWidth
									   : (wnd.document.body ? wnd.document.body.clientWidth : null));

		return { height : height, width : width };
	},

	/*
		Computes element size.
		Returns { width : x, height : x}
	*/
	elementSize : function(e) {
			var obj = $$(e);
			return { height: obj.offsetHeight, width: obj.offsetWidth };
	},

	/*
		Centers specified element at visible screen viewport
	*/
	center : function(e) {
		var obj = $$(e);

		var vSize = this.viewportSize();
		var eSize = this.elementSize(obj);

		var left = Math.round(vSize.width/2 - eSize.width/2);
		var top = Math.round(vSize.height/2 - eSize.height/2);

		this.moveTo(obj,left > 10 ? left : 10, top > 10 ? top : 10);
	},

	/*
		Make element centered, and sets window.onresize handler to recenter  
	*/
	keepCentered : function(e) {
		var self = this;

		self.center(e);

		doAfter(window,"onresize",function() {
			self.center(e);
		});
	}

};


/*
	Drag & Drop (DnD) Service
	Use makeMovable() to make DOM-elements movable.
	Use makeResizable() to make DOM-elements resizable.
*/
DragDrop = {

	isDrag : false,
	target : null,
	shield : null,

	attach : function(draggable) {
		var dragger = draggable.dragger != null ? $$(draggable.dragger) : $$(draggable.target);

		if(this.target == null) {
			document.onmousemove = EventUtil.handler(function(e) {
				if(DragDrop.isDrag) {
					DragDrop.target.onDrag(e);
				}
			});

			document.onmouseup = EventUtil.handler(this.stopDrag);
		}

		dragger.onmousedown = EventUtil.handler(function(e) {
				DragDrop.target=draggable;
				DragDrop.isDrag=true;
				if(DragDrop.shield) {
					DragDrop.shield.style.visibility="visible";
				}
				draggable.onStartDrag(e);
		});

	},

	stopDrag : function() {
		if(DragDrop.isDrag) {
			if(DragDrop.target) {
				DragDrop.target.onStopDrag();
			}
			if(DragDrop.shield) {
				DragDrop.shield.style.visibility="hidden";
			}
			DragDrop.isDrag=false;
		}
	},

	setShield : function(shield) {
		this.shield = $$(shield);
		this.shield.onmouseout = EventUtil.handler(this.stopDrag);
	},

	makeMovable : function(proto) {
		var draggable = {
			deltaX : 0,
			deltaY : 0,
			target : $$(proto.target),
			dragger : $$(proto.dragger)
		};		
				
		draggable.onStartDrag = function(e) {
			if(typeof proto.onStartDrag == "function") proto.onStartDrag(e);

			var xy = Canvas.findPosition(this.target);
			this.deltaX = e.clientX - xy.left;
			this.deltaY = e.clientY - xy.top;
		};

		draggable.onDrag = function(e) {
			Canvas.moveTo(this.target,e.clientX - this.deltaX,e.clientY - this.deltaY);
		};

		draggable.onStopDrag = function() {
			if(typeof proto.onStopDrag == "function") proto.onStopDrag();
		}

		this.attach(draggable);
	},

	makeResizable : function(proto) {
		var draggable = {
			x : 0,
			y : 0,
			target : $$(proto.target),
			dragger : $$(proto.dragger)
		};

		draggable.onStartDrag = function(e) {
			if(typeof proto.onStartDrag == "function") proto.onStartDrag(e);

			var xy = Canvas.findPosition(this.target);
			this.x = xy.left - (this.target.offsetWidth - (e.clientX - xy.left));
			this.y = xy.top - (this.target.offsetHeight - (e.clientY - xy.top));
		};
		
		draggable.onDrag = function(e) {
			Canvas.resizeTo(this.target,(e.clientX - this.x),(e.clientY - this.y));
		};

		draggable.onStopDrag = function() {
			if(typeof proto.onStopDrag == "function") proto.onStopDrag();
		};

		this.attach(draggable);
	}
}
