/* COPYRIGHT (C) Paul Davis 2010. All rights reserved. */

function Dragable () {
 
	this.mousex = null;
	this.scrollx = 0;
	this.offsetx = 0;
	this.mousey = null;
	this.scrolly = 0;
	this.offsety = 0;
	this.elm = null;
	this.ghost = null;
	this.container = null;
	this.dropzones = new Array();
	this.dropping = true;
	this.command = null;
	this.constrain = "y";
	this.dragged = false;
	this.target = null;
	this.source = {"parent":null,"before":null};
	this.scroller = null;
	
	//-------------------------------------------------------------------------------------------------------
	
	this.isChild = function (obj) {
		var first = true;
		while (obj) {
			if (obj.id == this.elm.id) {
				if (first) {
					return false;
				} else {
					return true;
				}
			}
			first = false;
			obj = obj.parentNode;
		}
		return false;
	}

	//------------------------------------------------------------------------------------------------------
	
	this.drag = function (event) {
		var founddrop = false;
		if (this.ghost) {
			this.dragged = true;
			this.mouse(event);
			if (this.constrain == "y")
				this.ghost.style.top  = (this.mousey - this.offsety) + "px";
			else if (this.constrain == "x")
				this.ghost.style.left = (this.mousex - this.offsetx) + "px";
			else {
				this.ghost.style.top  = (this.mousey - this.offsety) + "px";
				this.ghost.style.left = (this.mousex - this.offsetx) + "px";
			}
			// hilite position to drop
			for(var i = 0; i < this.dropzones.length; i++) {
				var target  = this.dropzones[i];
				var over = this.over(target);
				if (over && !founddrop) {
					if (this.dropping && !this.isChild(target)) {
						founddrop = true;
						target.className = target.className.replace(/ drop(above|below|left|right|self)/ig, "");
						if (over) {
							target.className += " drop" + over;
						}
					}
				} else {
					target.className = target.className.replace(/ drop(above|below|left|right|self)/g, "");
				}
			}
			// scroll container if necessary
			this.scroll();
		}
		return false; // in IE this prevents cascading of events, thus text selection is disabled
	};
	
	//-----------------------------------------------------------------------------------------------------
	
	this.drop = function (event) {
		var founddrop = false;
		document.onmousemove = null;
		document.onmouseup = null;
		document.onmousedown = null;
		if (this.ghost) {
			document.body.removeChild(this.ghost);
			this.ghost = null;
		}
		if (this.dragged) {
			this.mouse(event);
			for(var i = 0; i < this.dropzones.length; i++) {
				var target = this.dropzones[i];
				var over = this.over(target);
				if (over && !founddrop) {
					this.target = target;
					if (this.dropping && !this.isChild(target)) {
						founddrop = true;
						switch (over) {
							case "above":
							case "left":
								target.parentNode.insertBefore(this.elm, target);
								break;
							case "below": 
								target.parentNode.insertBefore(this.elm, target.nextSibling);
								break;
							case "right":
								if (this.constrain == "tree") {
									var kids = document.getElementById(target.id + "kids");
									if (kids)
										kids.insertBefore(this.elm, kids.firstChild);
								} else
									target.parentNode.insertBefore(this.elm, target.nextSibling);
								break;
						}
					}
				}
				target.className = target.className.replace(/ drop(above|below|left|right|self)/g, "");
			}
		}
		if (this.command) {
			eval(this.command);
		}
		this.dragged = false;
	};
			
	//--------------------------------------------------------------------------------------------------------
	
	this.grab = function (event, elm, containerid, classname, constrain, jsexecute, dropit, ghostclass, scrollerid) {
		if (typeof(elm) == "string") {
			elm = document.getElementById(elm);
		}
		if (!elm) {
			return false;
		}
		this.elm = elm;
		event = event || window.event; // allow for IE
		this.container = document.getElementById(containerid);
		if (!this.container) {
			return false;
		}
		this.source.parent = elm.parentNode;
		this.source.before = elm.previousSibling;
		this.mouse(event);
		this.scroller = (scrollerid) ? document.getElementById(scrollerid) : null;
		this.scrollx = document.body.scrollLeft;
		this.scrolly = 0; // this cancels out so don;t use: document.body.scrollTop;
		var con = this.container;
		while (con != document.body) {
			this.scrollx += con.scrollLeft;
			this.scrolly += con.scrollTop;
			con = con.parentNode;
		}
		this.offsetx = this.mousex - (findPos(this.elm, "x") - this.scrollx);
		this.offsety = this.mousey - (findPos(this.elm, "y") - this.scrolly);
		this.constrain = (constrain) ? constrain : "none";
		this.command = (jsexecute) ? jsexecute : null;
		if (ghostclass) {
			this.makeGhost(elm, ghostclass);
		} else {
			this.makeGhost(elm);
		}
		this.dragged = false;
		document.onmousedown = function () { return false; }; // prevents cascading of events, disabling text selection
		document.onmousemove = dragdragit;
		document.onmouseup   = dragdropit;
		if (dropit != null) {
			this.dropping = dropit;
		}
		// get dropzones to sort etc
		while (this.dropzones.length > 0) {
			this.dropzones.pop();
		}
		var tagtype = this.elm.tagName.toLowerCase();
		var descendants = this.container.getElementsByTagName(tagtype);
		if (!classname) { // use valid children (no whitespace nodes) of container
			var children = this.container.childNodes;
			for(var i = 0; i < children.length; i++) {
				if (children[i].nodeType == 1) {
					this.dropzones.push(children[i]);
				}
			}
		} else { // allows hierarchical
			var re = new RegExp("(^| )" + classname + "($| )");
			for(var i = (descendants.length - 1); i >= 0; i--) {
				if (descendants[i].className.match(re)) {
					this.dropzones.push(descendants[i]);
				}
			}
		}
		this.target = this.elm;
	};
	
	//-----------------------------------------------------------------------------------------------------
	
	this.hasScrollBar = function () {
		if (this.scroller) {
			return (Math.max(this.scroller.clientHeight, this.scroller.offsetHeight) < Math.max(this.container.clientHeight, this.container.offsetHeight))
		}
		return false;
	};
	
	//--------------------------------------------------------------------------------------------------------
	
	this.makeGhost = function (elm, ghostclass) {
		this.ghost = elm.cloneNode(true);
		this.ghost.id = 'ghost' + this.container.id;
		if (ghostclass) {
			this.ghost.className = ghostclass;
		}
		this.ghost.style.position = "absolute";
		this.ghost.style.float = "none";
		this.ghost.onmousedown = null;
		this.ghost.onmouseover = null;
		this.ghost.style.top  = (this.mousey - this.offsety) + 'px';
		this.ghost.style.left = (this.mousex - this.offsetx) + 'px';
		this.ghost.style.opacity = 0.6;
		document.body.appendChild(this.ghost);
	};
			
	//--------------------------------------------------------------------------------------------------------
	
	this.mouse = function (event) {
		event = event || window.event; // IE fix
		if (event) {
			if (event.pageX || event.pageY) { // this doesn't work on IE6!! (works on FF,Moz,Opera7)
				this.mousex = event.pageX;
				this.mousey = event.pageY;
			} else if (event.clientX || event.clientY) { // works on IE6,FF,Moz,Opera7
				this.mousex = event.clientX;
				this.mousey = event.clientY;
			}  
		}
	};
	
	//-----------------------------------------------------------------------------------------------------
	
	this.over = function (target) {
		if (this.constrain == "y") {
			var targety = findPos(target, "y") - this.scrolly;
			var targetheight = Math.max(target.clientHeight, target.offsetHeight);
			if ((this.mousey > targety) && (this.mousey < (targety + (targetheight / 2))))
				return "above";
			else if ((this.mousey >= (targety + (targetheight / 2))) && (this.mousey < (targety + targetheight)))
				return "below";
		} else if (this.constrain == "x") {
			var targetx = findPos(target, "x") - this.scrollx;
			var targetwidth = Math.max(target.clientWidth, target.offsetWidth);
			if ((this.mousex > targetx) && (this.mousex < (targetx + (targetwidth / 2))))
				return "left";
			else if ((this.mousex >= (targetx + (targetwidth / 2))) && (this.mousex < (targetx + targetwidth)))
				return "right";
		} else if (this.constrain == "tree") { // indentable
			var targety = findPos(target, "y") - this.scrolly;
			var targetheight = Math.max(target.clientHeight, target.offsetHeight);
			var targetx = findPos(target, "x") - this.scrollx;
			var targetwidth = Math.max(target.clientWidth, target.offsetWidth);
			ok = ((this.mousey > targety) && (this.mousey < (targety + targetheight)));
			ok = ok && ((this.mousex > targetx) && (this.mousex < (targetx + targetwidth)));
			if (ok) {
				if (target.id == this.elm.id) {
					return "self";
				} else if ((this.mousex >= (targetx + (targetwidth * 2 / 3))) && (this.mousex < (targetx + targetwidth))) {
					return "right";
				} else if ((this.mousey > targety) && (this.mousey < (targety + (targetheight / 2)))) {
					return "above";
				} else if ((this.mousey >= (targety + (targetheight / 2))) && (this.mousey < (targety + targetheight))) {
					return "below";
				}
			}
		} else { // wrapping horizontal list
			var targety = findPos(target, "y") - this.scrolly;
			var targetheight = Math.max(target.clientHeight, target.offsetHeight);
			var targetx = findPos(target, "x") - this.scrollx;
			var targetwidth = Math.max(target.clientWidth, target.offsetWidth);
			ok = ((this.mousey > targety) && (this.mousey < (targety + targetheight)));
			ok = ok && ((this.mousex > targetx) && (this.mousex < (targetx + targetwidth)));
			if (ok) {
				if ((this.mousex > targetx) && (this.mousex < (targetx + (targetwidth / 2))))
					return "left";
				else if ((this.mousex >= (targetx + (targetwidth / 2))) && (this.mousex < (targetx + targetwidth)))
					return "right";
			}
		}
		return "";
	};
	
	//-----------------------------------------------------------------------------------------------------
	
	this.scroll = function () {
		if (this.scroller) {
			var speed = 5;
			var scrolltop = findPos(this.scroller, 'y');
			var scrollbottom = scrolltop + Math.max(this.scroller.clientHeight, this.scroller.offsetHeight);
			var orig = this.scroller.scrollTop;
			if ((this.mousey - scrolltop) < 10) {
				this.scroller.scrollTop -= speed;
			} else if ((scrollbottom - this.mousey) < 10) {
				this.scroller.scrollTop += speed;
			}
			this.scrolly += this.scroller.scrollTop - orig;
		}
	};
	
	//--------------------------------------------------------------------------------------------------------
	
}

//------------------------------------------------------------------------------------------------------------

var drag = new Dragable();

function dragdragit (e) {
	drag.drag(e);
}

function dragdropit (evt) {
	drag.drop(evt);
}

