// wDRAG. Thin Drag&Drop Framework
// v2.0 Cedric Savarese 
// April. 2006

// Change Log
// v2.0 (04/10/2006) -  rewritten

 if(typeof helpers == "undefined" && wHELPERS) {
	var helpers = new wHELPERS();
 }
 if (!helpers) {
	alert('Sorry, whelpers.js is not correctly loaded. wDRAG is not active.');
  }
  
 function wDrag() { 
	var self     = this; 
	
	this.create = function(id) {
		this.rootId                = id;
		this.root                  = document.getElementById(id);		
		this.options               = arguments[1] || {};
		this.trashId 			   = this.options.trashId      || "dragTrash";	
		this.classNameHandle       = this.options.handleClass  || "dragHandle";	
		this.classNameDragged      = this.options.draggedClass || "dragged";	
		this.classNameTrashWarning = this.options.warningClass || "warning";		
		this.draggableElements     = this.options.draggable    || ["li"];
		this.containerElements     = this.options.containers   || ["ul"];		
		this.onUpdate              = this.options.onUpdate; 
		this.isReady               = false;
		this.isActive              = false;		
		this.element 	    	   = null;
		this.ghostElement 	       = null;
		this.targets			   = [];
		
		if(!this.root) { 
			return false;
		}
	
		var li = this.root.getElementsByTagName('a');
		var lg = li.length;
		for(var i=0;i<lg; i++) {
			if(helpers.hasClass(li[i],this.classNameHandle)) {		
				this.targets.push(li[i]);			
			}
		}	
		var trash = document.getElementById(this.trashId);
		if(trash) {		
			this.targets.push(trash);			
		}

		helpers.addEvent(this.root,'mousedown', this.start);
	}

	this.clear = function() {
		//helpers.removeEvent(self.root,'mousedown', self.start);  //prblm with IE6
	}
	
	this.start = function(e) {		
		if(self.isActive) {
			wFORMS.debug('cleaning drag..');	
			self.stop();
		}		
		if(!e) e = window.event;
		if(self.isLeftClick(e)) {
			var n = helpers.getSourceElement(e);
			self.element = self.getDraggableElement(n);							
			if(self.element) {		
				wFORMS.debug('drag ready');		
				self.isReady = true;
				self.isActiveTimout = setTimeout( function() { 
					wFORMS.debug('drag start');										
					self.ghostElement = self.getGhostElement(self.element); 		
					self.ghostElement.className += "  ghostDragged";			
					self.isActive = true; 
					self.element.className += self.classNameDragged;	
					self.root.style.cursor = 'move';	
				}, 400);
				//self.insertType	  = "before";			
				self.ghostOffsetLeft = helpers.getLeft(self.element) - self.pointerX(e);
				self.ghostOffsetTop  = helpers.getTop(self.element) - self.pointerY(e);
				helpers.addEvent(self.root,'mouseup', self.stop);
				helpers.addEvent(document.body,'selectstart', self.nothing);
				helpers.addEvent(document.body,'drag', self.nothing);				
				helpers.addEvent(self.root,'mousemove', self.move);
				helpers.addEvent(self.root,'keypress', self.cancelDrag);
				helpers.stopPropagation(e);
				return helpers.preventEvent(e);				
			}
		}	
		wFORMS.debug('click');
		return true;		
	}
	
	this.stop = function(e) {
		
		if(self.isReady) {
			clearTimeout(self.isActiveTimout);
			if(!self.isActive) wFORMS.debug('drag canceled');
			self.isReady = false;
			helpers.removeEvent(self.root,'mousemove', self.move);
			helpers.removeEvent(self.root,'keypress', self.cancelDrag);
			helpers.removeEvent(self.root,'mouseup', self.stop);
			helpers.removeEvent(document.body,'selectstart', self.nothing);
			helpers.removeEvent(document.body,'drag', self.nothing);			
		}
		if(self.isActive) {
			wFORMS.debug('drag completed');
			self.isActive = false;
			self.ghostElement.parentNode.removeChild(self.ghostElement);	
			//self.element.style.visibility = 'visible';	
			self.element.className = self.element.className.replace(self.classNameDragged, "");
			self.root.style.cursor = 'auto';
			/*
			if(self.isInTrash(self.element)) {
				if(!window.confirm("Do you want to empty your trash now?")) {
					helpers.stopPropagation(e);
					helpers.preventEvent(e); 
					return;	
				}
			} */
			self.onUpdate(self.root, self.element);			
			helpers.stopPropagation(e);
			return helpers.preventEvent(e); 
		}
		
	}
	
	this.cancelDrag = function(e) { 
		self.stop(e);
	}
	
	this.move = function(e) {
		if(self.isActive) {			
			var mouseX = self.pointerX(e);
			var mouseY = self.pointerY(e);
			if(!self.previousMouseY)
				self.previousMouseY = mouseY;  
			var moveDirectionY = mouseY - self.previousMouseY; // >0 if going down.
			self.previousMouseY = mouseY; 
			self.ghostElement.style.left = (mouseX+ self.ghostOffsetLeft).toString()+"px";
			self.ghostElement.style.top  = (mouseY+ self.ghostOffsetTop).toString()+"px";
			
			var dragO  = self.getDimensions(self.ghostElement);
			var dragX  = dragO.x;
			var dragY  = dragO.y;
			var dragXW = dragO.x + dragO.w;
			var dragYH = dragO.y + dragO.h;
			
			for(var i = self.targets.length-1; i >= 0; i--) {
				var t = self.targets[i];
				
				var p = t.parentNode;
				while(p && p != self.element)
					p = p.parentNode;
				if (p) continue;
				
				var o = self.getDimensions(t);
				var x = o.x;
				var y = o.y;
				var w = o.w;
				var h = o.h;		
				
				// handle trash separatly
				if(self.isTrash(t)) {
					if(mouseY > y) {
						// activate trash only if the mouse pointer is over it.
						self.activateTrashWarning();
						var over = t.getElementsByTagName('ul')[0];						
						over.appendChild(self.element);
						return;
					} 
				} 
				// list item
				else {
					if (  (moveDirectionY < 0  && dragY > y  && dragY < (y+h))  /* when going up */
					   || (moveDirectionY >= 0 && dragYH > y && dragYH < (y+h)) /* when going down */  ) {

						// deactivate trash warning if necessary
						self.deactivateTrashWarning();
						
						// get the hovered-on element
						var over = self.getDraggableElement(t);
						try {
							var container = self.getContainerElement(over);
							if(self.isContainerEmpty(container)) {
								container.appendChild(self.element);								
							} else {	
								
								if (  (moveDirectionY < 0  && dragY > y+h/2)  /* when going up */
				  			 	   || (moveDirectionY >= 0 && dragYH > y+h/2) /* when going down */  ) {
									// insert as first child of container, if not already there							
									if(container && self.element != container.firstChild) {
										wFORMS.debug('insert as first child  ' + over.id);
			  			 				container.insertBefore(self.element, container.firstChild);
			  			 			}
			  			 			// insert after, if not already there	
			  			 			else if(!container && self.element != over.nextSibling) {
										over.parentNode.insertBefore(self.element, over.nextSibling);
			  			 			}									
				  			 	} else {				  			 		
				  			 		// insert before container, if not already there
			  			 			if(self.element.nextSibling != over) {
			  			 				over.parentNode.insertBefore(self.element, over);
			  			 			} 
				  			 	}
							}
						} catch(x) {
							wFORMS.debug(over.id + ' ' + x.message);	
						}
						return;
					}
				}
			}
			//to-do: check if at the end of the list.

		}
	}

	this.getDraggableElement = function(n) {
		while(n && n.tagName) {
			if(n.id == self.rootId) 
				return null;
			if(self.isDraggable(n))
				return n;
			n = n.parentNode;
		}
		return null;
	}

	this.isDraggable = function(n) {
		if(!n) return false;
		if(n.nodeType==3) n = n.parentNode;
		for(var i=0;i<self.draggableElements.length;i++) {
			if(n.tagName.toLowerCase() == self.draggableElements[i] && !self.isTrash(n) )
				return true;
		}
		return false;
	}

	this.getContainerElement  = function(n) {
		if(!n || n.nodeType!=1) return false;
		for(var j=0;j<n.childNodes.length;j++) {
			cn = n.childNodes[j];
			if(self.isContainer(cn)) return cn;
		}
		return null;
	}

	this.isContainer = function(n) {
		if(!n || n.nodeType!=1) return false;
		for(var j=0;j<self.containerElements.length;j++) {
			if(n.tagName.toLowerCase() == self.containerElements[j] && !self.isTrash(n) )
				return true;	
		}
		return false;
	}
	
	this.isContainerEmpty = function(n) {
		if(!n || n.nodeType!=1) return false;
		for(var j=0;j<n.childNodes.length;j++) {
			if(self.isDraggable(n.childNodes[j]) && n.childNodes[j] != self.element )
				return false;
		}
		return true;
	}
	
	this.isTrash = function(n) {
		return (n && n.id && n.id==self.trashId);
	}
	this.isInTrash = function(n) {
		n = n.parentNode;
		while(n) {
			if(self.isTrash(n)) {
				return true;
			}
			n = n.parentNode;
		}
		return false;
	}
	this.activateTrashWarning = function() {
		var trash = document.getElementById(self.trashId);
		if(trash && !helpers.hasClass(trash, self.classNameTrashWarning) ) {		
			trash.className = trash.className + ' ' + self.classNameTrashWarning;			
		}
	}
	this.deactivateTrashWarning = function() {
		var trash = document.getElementById(self.trashId);
		if(trash && helpers.hasClass(trash, self.classNameTrashWarning) ) {		
			trash.className = trash.className.replace(self.classNameTrashWarning,'');			
		}
	}
	this.getDroppedOnElement = function(n) {
		while(n && n.tagName) {
			if(n.id == self.rootId) 
				return null;
			if(self.isDraggable(n))// not implemented, all draggable elements can be dropped on
				return n;
			if(self.isTrash(n)) 
				return n;
			n = n.parentNode;
		}
		return null;
	}
	
	this.getGhostElement = function(n) {
		if(self.ghostElement && self.ghostElement.parentNode) 
			self.ghostElement.parentNode.removeChild(self.ghostElement);
		
		var ghostElement = n.cloneNode(true);
		ghostElement.style.width = n.offsetWidth + 'px'; // forces width in IE
		ghostElement.style.position = "absolute";
		ghostElement.style.left = helpers.getLeft(n)+ "px";
		ghostElement.style.top = helpers.getTop(n) + "px";		
		ghostElement = self.root.insertBefore(ghostElement, self.root.firstChild);
		return ghostElement;
	}
	
	this.nothing = function(e) {
		if(!e) e = window.event;
		helpers.preventEvent(e);
		helpers.stopPropagation(e);		
		return false; 
	}
	
	this.getDimensions = function(n) {		
		var obj = {
		 	x: helpers.getLeft(n),
			w: n.offsetWidth,
			y: helpers.getTop(n),
			h: n.offsetHeight
		}		
		return obj;
	}
	
	this.isLeftClick = function(e) {
	    return ((e.which && e.which == 1) || (e.button && e.button == 1));
	}
	this.pointerX = function(e) {	
		if(!e) e = window.event;
		return e.pageX || (e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft));
	}
	this.pointerY = function(e) {
		if(!e) e = window.event;
		return e.pageY || (e.clientY + (document.documentElement.scrollTop || document.body.scrollTop));
	}
 }