
function DragDrop(){
	
	//PUBLIC fields. (meant to be used as public fields)
	this.mousePosition = new MousePosition();
	this.state = "inactive"; //can be : active, inactive, cancelled			
	this.sourceElement; //Element that was clicked
	this.targetElement; //Target of the operation	
	this.pageTargetElement; //Element in the page that the mouse is over
	this.behavior;
	this.direction;
	
	this.anchorX;
	this.anchorY;		
		
	//PRIVATE fields. (meant to be used as private fields)
	this.checkBounds;
	this.containerTop;
	this.containerBottom;
	this.containerLeft;
	this.containerRight;
			
	this.minClientWidth = 0;
	this.minClientHeight = 0;
		
	//----------------------------------------------------------------------
	//Tries to find a parent element that has the behavior attribute set. 
	//If it can't find any, returns the element passed
	this.findElement = 
		function(p_element){
			var tmp = p_element;		
			while(p_element){		
				if(! p_element.behavior ){
					p_element = p_element.parentNode;			
				}
				else{			
					return p_element;
				}		
			}	
			return tmp;			
	}

	//---------------------------------------------------------------
	//Gets the direction parameter specified in the HTML. 
	//Returns BOTH if value is invalid or if direction is not specified
	this.getDirection = 
		function(p_element){			
			
			var strDirection = p_element.getAttribute("direction");					
			if (!strDirection || (strDirection.toUpperCase() != "VERTICAL" && strDirection.toUpperCase() != "HORIZONTAL") )
				return "BOTH";
			else
				return strDirection.toUpperCase();
		}


	//----------------------------------------------------------------
	//Gets the anchor paramenter specified in the HTML
	this.getAnchor = 
		function(p_element, p_anchor){

			var strAnchorX = "";
			var strAnchorY = "";

			if(p_element.anchorX){
				strAnchorX = p_element.anchorX;
				switch(strAnchorX.toUpperCase()){
					case "LEFT":
					case "RIGHT":
					
						strAnchorX = strAnchorX;
					break;
					
					default:
					
						strAnchorX = "left";
				}
			}else{
				strAnchorX = "left";
			}

			if(p_element.anchorY){
				strAnchorY = p_element.anchorY;
				switch(strAnchorY.toUpperCase()){
					case "TOP":
					case "BOTTOM":
						strAnchorY = strAnchorY;
					break;
					
					default:
						strAnchorY = "top";
				}
			}else{
				strAnchorY = "top";
			}
					switch(p_anchor){
					case 'x':
					return strAnchorX.toUpperCase();
					
					case 'y':
					return strAnchorY.toUpperCase();
					}
	}

	//-------------------------------------------------------------------------
	//private function. will perform the horizontal resize
	this.resizeVertical = 
		function(newTop, newHeight, prevClientHeight, prevStyleHeight){
			if (newHeight >= DragDrop.minClientHeight){				
				
				prevStyleHeight = parseInt(DragDrop.targetElement.style.height);
				prevClientHeight = parseInt(DragDrop.targetElement.clientHeight);
				
				if(!prevStyleHeight)
					prevStyleHeight = 0;
			
				//adjusting height
				DragDrop.targetElement.style.height = newHeight;
				
				//adjusting top. Needed depending on the anchor position
				if (newTop >= 0){
					prevStyleTop = DragDrop.targetElement.style.top;
					DragDrop.targetElement.style.top = newTop;
				}
				
				//if the actual height (clientHeight) of the target element did not change,
				//move the style.height and the style.top to the previous value							
				if(prevClientHeight == DragDrop.targetElement.clientHeight || DragDrop.isOutOfBoundsVertical(newTop) ){
					DragDrop.targetElement.style.height = prevStyleHeight;
					DragDrop.targetElement.style.top = prevStyleTop;
				}
			}
		}
		
	//-------------------------------------------------------------------------
	this.resizeHorizontal = 
		function(newLeft, newWidth, prevClientWidth, prevStyleWidth){
			if (newWidth >= DragDrop.minClientWidth){			
				
				prevStyleWidth = parseInt(DragDrop.targetElement.style.width);
				prevClientWidth = parseInt(DragDrop.targetElement.clientWidth);
				
				if(!prevStyleWidth)
					prevStyleWidth = 0;
				
				DragDrop.targetElement.style.width = newWidth;
				
				if (newLeft >= 0){
					prevStyleLeft = DragDrop.targetElement.style.left;
					DragDrop.targetElement.style.left = newLeft;
				}
			
				if(prevClientWidth == DragDrop.targetElement.clientWidth || DragDrop.isOutOfBoundsHorizontal(newLeft)){
					DragDrop.targetElement.style.width = prevStyleWidth;
					DragDrop.targetElement.style.left = prevStyleLeft;
				}																								
				
			}
									
		}
	
	//-----------------------------------------------------------------------------------------	
	this.isOutOfBoundsVertical = 
		function (childTop){
			
			//if not supposed to ckeck bounds, return false;
			if(!DragDrop.checkBounds)
				return false;						
			
			var childBottom = DragDrop.targetElement.clientHeight + childTop;

			//window.status = "child bottom: " + childBottom + " - con bot: " + DragDrop.containerBottom + " - chiTop:" + childTop +" - ctop:" + DragDrop.containerTop;
			if(childBottom < DragDrop.containerBottom && childTop > DragDrop.containerTop){
				return false;
			}
			
			return true;
		}	
		
	//-----------------------------------------------------------------------------------------	
	this.isOutOfBoundsHorizontal = 
		function (childLeft){							
			//if not supposed to ckeck bounds, return false;
			if(!DragDrop.checkBounds)
				return false;		
				
			var childRight = DragDrop.targetElement.clientWidth + childLeft;
			
			//window.status = "child right: " + childRight + " - cont right: " + DragDrop.containerRight + " - child Left:" + childLeft +" - ctop:" + DragDrop.containerLeft;
			if(childRight < DragDrop.containerRight && childLeft > DragDrop.containerLeft){
				return false;
			}
			return true;
		}	

	//--------------------------------------------------------------
	//Called onmousedown. Itinializes the operation
	this.startOperation = 
		function(){								
			DragDrop.sourceElement = DragDrop.findElement(event.srcElement);	
			if (!DragDrop.sourceElement.getAttribute)
			{
				DragDrop.state = "inactive";
				return true;
			}
				
			DragDrop.behavior = DragDrop.sourceElement.getAttribute("behavior");
						
			//if element or any of its parents has not specified a behavior, exit
			if(!DragDrop.behavior){
				DragDrop.state = "inactive";
				return true;
			}
			else{
				DragDrop.behavior = DragDrop.behavior.toUpperCase();
			}
		
			//if container element was specified, get its coordinates
			var strcontainerElement = DragDrop.sourceElement.getAttribute("containerElement");
			if(strcontainerElement){
				DragDrop.checkBounds = true;
				
				//checking for page bounds				
				if(strcontainerElement.toUpperCase() == 'DOCUMENT'){
					DragDrop.containerLeft = 0; 
					DragDrop.containerTop = 0;
					DragDrop.containerRight = document.body.clientWidth + document.body.scrollLeft -1;
					DragDrop.containerBottom = document.body.clientHeight + document.body.scrollTop -1;
				}
				//checking for bounds of an element in the page
				else{
					var containerElement = document.all(strcontainerElement);
					DragDrop.containerLeft = 0;
					if(containerElement.style.left)
						DragDrop.containerLeft = parseInt(containerElement.style.left);
					
					DragDrop.containerRight = DragDrop.containerLeft + parseInt(containerElement.style.width);
					
					DragDrop.containerTop = 0;
					if(containerElement.style.top)
						DragDrop.containerTop = parseInt(containerElement.style.top);
									
					DragDrop.containerBottom = DragDrop.containerTop + parseInt(containerElement.style.height);
				}
			}
			//not checking for bounds
			else{
				DragDrop.checkBounds = false;
			}
				
			
			//updating state
			DragDrop.state = "active";
				
			DragDrop.direction = DragDrop.getDirection(DragDrop.sourceElement);
			
			//initializing start mousePosition position
			DragDrop.mousePosition.start.rawX = event.x;
			DragDrop.mousePosition.start.x = event.x + document.body.scrollLeft;
			
			DragDrop.mousePosition.start.rawY = event.y;
			DragDrop.mousePosition.start.y = event.y + document.body.scrollTop;
														
			//Reading target element. If one is not specified, source element will be assumed to be the target
			var strTargetElement = DragDrop.sourceElement.getAttribute("targetElement");			
			if(! strTargetElement){
				DragDrop.targetElement = DragDrop.sourceElement;			
			}
			else{
				DragDrop.targetElement = document.all(strTargetElement);
			}
			
			if(DragDrop.behavior != "DRAG"){
				//make sure target element has position set to absolute and has style.top and style.left set
				//if top and left are not set, will set them to the current mouse position
				DragDrop.targetElement.style.setAttribute("position", "absolute", 0);		
							
				if(!DragDrop.targetElement.style.getAttribute("left")){
				DragDrop.targetElement.style.setAttribute("left", DragDrop.mousePosition.start.x);
				}		
				if (!DragDrop.targetElement.style.getAttribute("top")){
					DragDrop.targetElement.style.setAttribute("top", DragDrop.mousePosition.start.y, 0);
				}	
			}														
							
			//initializing mousePosition offset to the target element
			DragDrop.mousePosition.offset.x = DragDrop.mousePosition.start.x - parseInt(DragDrop.targetElement.style.getAttribute("left"));
			DragDrop.mousePosition.offset.y = DragDrop.mousePosition.start.y - parseInt(DragDrop.targetElement.style.getAttribute("top"));
			DragDrop.mousePosition.offset.rawX = DragDrop.mousePosition.offset.x;
			DragDrop.mousePosition.offset.rawY = DragDrop.mousePosition.offset.y;
			
			//-----------------------------------------------------
			//Performing actions specific to the behavior specified						
			switch (DragDrop.behavior){
				
				case "DRAG":
					var strDragImagePath = DragDrop.sourceElement.getAttribute("dragImage");
					if (strDragImagePath && strDragImagePath.length > 0){
						//Setting up drag image
						document.all('baseDrag').style.display='block';	
						document.all('imgDrag').style.display = 'none';
						document.all('imgDrag').src = strDragImagePath;
					}
					else{
						document.all('baseDrag').style.display='none';
					}		
				
				case "RESIZE":
					
					//Initializing anchor positions				
					DragDrop.anchorX = DragDrop.getAnchor(DragDrop.sourceElement, "x");
					DragDrop.anchorY = DragDrop.getAnchor(DragDrop.sourceElement, "y");
					
					//Making sure a height and a width are initially specified
					if (!DragDrop.targetElement.style.getAttribute("height")){
						DragDrop.targetElement.style.setAttribute("height", DragDrop.targetElement.clientHeight)
					}					
					if (!DragDrop.targetElement.style.getAttribute("width")){
						DragDrop.targetElement.style.setAttribute("width", DragDrop.targetElement.clientWidth)
					}
					
					//Initializing min client width and min client height
					if(DragDrop.targetElement.getAttribute("minClientWidth")){			
						DragDrop.minClientWidth = DragDrop.targetElement.getAttribute("minClientWidth");
					}						
					
					if(DragDrop.targetElement.getAttribute("minClientHeight")){			
						DragDrop.minClientHeight = DragDrop.targetElement.getAttribute("minClientHeight");
					}						
			
							
				//add other behaviors here if needed
				//case "XXXX": 
			}
			
			//Calling handler if one has been specified
			var strStartHandler = DragDrop.sourceElement.getAttribute("onStartHandler");
			var funcStartHandler;
			if(strStartHandler){
				funcStartHandler = eval(strStartHandler);
			}
						
			return false;
				
		}//end startOperation()	
	
	//-----------------------------------------------------------------------------------------------------
	//Called on onmousemove. 
	//Will perform the specified behavior taking into account all specified parameters
	this.doOperation = 
		function(){
			var aryAll = document.all;
			
			DragDrop.pageTargetElement = event.srcElement;
			
			//If the state is not active (startOperation() was not executed), exit 
			if(DragDrop.state != 'active')
				return true;
							
			//Reading current mousePosition position
			DragDrop.mousePosition.current.rawX = event.x;
			DragDrop.mousePosition.current.x = event.x + document.body.scrollLeft;
			
			DragDrop.mousePosition.current.rawY = event.y;
			DragDrop.mousePosition.current.y = event.y + document.body.scrollTop;															
																						
			//calling move handler		
			var strMoveHandler = DragDrop.sourceElement.getAttribute("onMoveHandler");
			if(strMoveHandler){
				var funcMoveHandler = eval(strMoveHandler);
				//If move handler returns cancel or state is changed inside handler, exit
				if (funcMoveHandler == "cancel" || DragDrop.state != "active")		
					return false;	
			}		
				
			//Will perform actions specific to the behavior specified
			switch(DragDrop.behavior){
				case "DRAG":
					var baseDrag = aryAll('baseDrag');
					aryAll('imgDrag').style.display = 'block';					
					switch(DragDrop.direction){ // check direction
						case "VERTICAL":							
							if(!DragDrop.isOutOfBoundsVertical(DragDrop.mousePosition.current.y))
							{
								baseDrag.style.top = DragDrop.mousePosition.current.y - 7;
								baseDrag.style.left = DragDrop.mousePosition.start.x + 1;
							}
							break;
							
						case "HORIZONTAL":
							if(!DragDrop.isOutOfBoundsHorizontal(DragDrop.mousePosition.current.x))
								baseDrag.style.left = DragDrop.mousePosition.current.x + 10;
							break;
							
						default:
							if(!DragDrop.isOutOfBoundsVertical(DragDrop.mousePosition.current.y))
								baseDrag.style.top = DragDrop.mousePosition.current.y + 10;
								
							if(!DragDrop.isOutOfBoundsHorizontal(DragDrop.mousePosition.current.x))
								baseDrag.style.left = DragDrop.mousePosition.current.x + 10;																						
					}				
				break;
				
				//-------------------------------------------------------------------				
				case "MOVE":
					var newLeft = DragDrop.mousePosition.current.x - DragDrop.mousePosition.offset.x;
					var newTop = DragDrop.mousePosition.current.y - DragDrop.mousePosition.offset.y;
					switch(DragDrop.direction){ // check direction
						case "VERTICAL":							
							if(!DragDrop.isOutOfBoundsVertical(newTop))
								DragDrop.targetElement.style.top = newTop - 1;
							break;
								
						case "HORIZONTAL":
							if(!DragDrop.isOutOfBoundsHorizontal(newLeft))
								DragDrop.targetElement.style.left = newLeft - 1;
							break;
								
						default:						
							if(!DragDrop.isOutOfBoundsVertical(newTop))
								DragDrop.targetElement.style.top = newTop - 1;
								
							if(!DragDrop.isOutOfBoundsHorizontal(newLeft))
								DragDrop.targetElement.style.left = newLeft - 1;
					}
						
				break;
					
				//-------------------------------------------------------------------
				case "RESIZE": 						
					var newTop;
					var newLeft;
					var newHeight;
					var newWidth;
					var prevClientWidth
					var prevStyleWidth
					var prevStyleLeft;				
												
					var prevClientHeight;
					var prevStyleHeight;
					var prevStyleTop;								
										
					//resizing from right														
					if(DragDrop.anchorX == 'LEFT'){
						newLeft = parseInt(DragDrop.targetElement.style.left);
						newWidth = DragDrop.mousePosition.current.x - parseInt(DragDrop.targetElement.style.left);
					}
					//resizing from left
					else{				
						newLeft = DragDrop.mousePosition.current.x - DragDrop.mousePosition.offset.x;
						var dif = parseInt(DragDrop.targetElement.style.left) - newLeft;
						newWidth = parseInt(DragDrop.targetElement.style.width) + dif;													
						if(!newWidth)
							newWidth = 0;
					}
					
					//resizing from bottom	
					if(DragDrop.anchorY == 'TOP'){
						newTop = parseInt(DragDrop.targetElement.style.top);
						newHeight = DragDrop.mousePosition.current.y - parseInt(DragDrop.targetElement.style.top);
					}					
					//resizing from top
					else{
						newTop = DragDrop.mousePosition.current.y - DragDrop.mousePosition.offset.y;
						var dif = parseInt(DragDrop.targetElement.style.top) -  newTop;
						newHeight = parseInt(DragDrop.targetElement.style.height) + dif;
						if(!newHeight)
							newHeight = 0;
					}					
					
					//performing the resize based on the values calculated below.					
					switch(DragDrop.direction){						
						case "VERTICAL":																				
							DragDrop.resizeVertical(newTop, newHeight);
							break;
							
						case "HORIZONTAL":														
							DragDrop.resizeHorizontal(newLeft, newWidth);
							break;
								
						default:							
							DragDrop.resizeVertical(newTop, newHeight);														
							DragDrop.resizeHorizontal(newLeft, newWidth);
						break;					
					}
				break;
			
			}//end of switch
													
			return false;				
	}//end doOperation()
		
	//-----------------------------------------------------------------------------------------------------
	//Called on onmouseup. 
	//Will finilize the operation and call the specified handler
	this.endOperation = 
		function(){			
			DragDrop.state = "inactive";				
			
			DragDrop.pageTargetElement = event.srcElement;
			
			DragDrop.mousePosition.end.rawX = event.x;
			DragDrop.mousePosition.end.x = event.x + document.body.scrollLeft;
			
			DragDrop.mousePosition.end.rawY = event.y;
			DragDrop.mousePosition.end.y = event.y + document.body.scrollTop;
						
			document.all('baseDrag').style.display='none';					
			if (DragDrop.mousePosition.start.x == DragDrop.mousePosition.end.x && DragDrop.mousePosition.start.y == DragDrop.mousePosition.end.y){
				return true;
			}		
			else if((DragDrop.sourceElement == DragDrop.pageTargetElement) && DragDrop.behavior=="DRAG"){
				return true;
			}			
			
			var strDropHandler;
			var funcDropHandler;
		
			if(DragDrop.sourceElement && DragDrop.sourceElement.onEndHandler){		
				strDropHandler = DragDrop.sourceElement.onEndHandler;
				funcDropHandler = eval(strDropHandler);
			}
		
		}//end of endOperation
				
}//end DragDrop()

//------------------------------------------------
//supporting structures
function MousePosition(){
	this.start = new DHTMLCoordinate(); //start coordinate
	this.end = new DHTMLCoordinate(); //end coordinate (only available after endOperation() is executed)
	this.current = new DHTMLCoordinate(); //current coordinate
	this.offset = new DHTMLCoordinate(); //offset from mouse position and left coordinate of source element	
}

function DHTMLCoordinate(){
	this.x;
	this.y;
	this.rawX; //does not take scrolling into account
	this.rawY; //does not take scrolling into account	
}
//--------------------------------------------------------

var DragDrop = new DragDrop();

document.attachEvent("onmousedown", DragDrop.startOperation);
document.attachEvent("onmousemove", DragDrop.doOperation);
document.attachEvent("onmouseup", DragDrop.endOperation);

//Writing div that will produce the dragging image
document.write("<div id='baseDrag' style='position:absolute; z-index:20000; display:none'><img id='imgDrag'></div>");
