
function DragAndDrop()
{
	function Space()
	{
		this.name = "unknownName";
		this.onStart = null;
		this.onDrag = null;
		this.onDrop = null;
		this.draggingElementId = "";
		this.targetAttr = "";
		this.top = 0;
		this.left = 0;
	}

	var SPACE_NAME_ATTRIBUTE = "dragCurrentSpaceName",MOVE_COPY_ATTRIBUTE = "dragCopyObject";
	var mouseOffset = null,dragObject = null;
	var isMouseDown = false,mouseState = false;
	var spaces = [];

	this.init = function()
	{
		document.onmousemove = mouseMove;
		document.onmouseup = mouseUp;
		document.onmousedown = mouseDown;
	}
	this.addSpace = function(name,startCallback,dragCallback,dropCallback,targetAttribute)
	{
		var space = new Space();
		space.name = name;
		space.onStart = startCallback;
		space.onDrag = dragCallback;
		space.onDrop = dropCallback;
		if(isDefined(targetAttribute))
			space.targetAttr = targetAttribute;
		spaces.push(space);
	}
	this.addSpaceLimits = function(name,top,left)
	{
		var s = getSpaceByName(name);
		s.top = top;
		s.left = left;
	}
	this.removeSpace = function(name)
	{
		for(var i in spaces)
			if(spaces[i].name == name)
			{
				spaces.splice(i,1);
				return;
			}
	}
	this.addDraggableElement = function(spaceName,triggerElement,draggableElement,moveCopy)
	{
		moveCopy = isDefined(moveCopy) ? moveCopy : true;
		var s = getSpaceByName(spaceName);
		if(!isDefined(s))throw "No space with such name exists.";
		if(triggerElement.id == draggableElement.id)
			draggableElement = triggerElement;
		triggerElement.onmousedown = function(ev)
		{
			mouseOffset = getMouseOffset(this,ev);
			s.draggingElementId = draggableElement.id;
			if(moveCopy)
			{
				dragObject = draggableElement.cloneNode(true);
				dragObject.id = draggableElement.id + "Drag";
				dragObject.style.cursor = "default";
				dragObject.style.display = "none";
				dragObject.style.opacity = 0.6;
				dragObject.style.MozOpacity = 0.6;
				dragObject.style.zIndex = 6001;
				dragObject.style.filter = "alpha(opacity=60)";
				if(s.targetAttr != "")dragObject.removeAttribute(s.targetAttr);
				document.body.appendChild(dragObject);
				dragObject.style.height = Sys.UI.DomElement.getBounds(draggableElement).height + "px";
				dragObject.style.width = Sys.UI.DomElement.getBounds(draggableElement).width + "px";
			}
			else dragObject = draggableElement;
			dragObject.setAttribute(SPACE_NAME_ATTRIBUTE,s.name);
			dragObject.setAttribute(MOVE_COPY_ATTRIBUTE,moveCopy);
		}
	}
	this.removeDraggableElement = function(spaceName,triggerElement,draggableElement)
	{
		var s = getSpaceByName(spaceName);
		if(!isDefined(s))throw "No space with such name exists.";
		if(triggerElement.id == draggableElement.id)
			draggableElement = triggerElement;
		triggerElement.onmousedown = null;
	}
	var mouseMove = function(ev)
	{
		ev = ev || window.event;
		var target = ev.target || ev.srcElement;
		if(isMouseDown && isDefined(dragObject))
		{
			var currentMousePosition = mouseCoords(ev);
			var s = getSpaceByName(dragObject.getAttribute(SPACE_NAME_ATTRIBUTE));
			if(s.top > 0)dragObject.style.top = s.top+"px";// VLAD: limiting here (if set)
			else
			{
				if(dragObject.getAttribute(MOVE_COPY_ATTRIBUTE))
					dragObject.style.top = (currentMousePosition.y)+"px";
				else dragObject.style.top = (currentMousePosition.y - mouseOffset.y)+"px";
			}
			if(s.left > 0)dragObject.style.left = s.left+"px"
			else dragObject.style.left = (currentMousePosition.x - mouseOffset.x)+"px";
			var d = target;
			if(isDefined(s.targetAttr) && s.targetAttr.length > 0)
				d = getParentByAttribute(target,s.targetAttr);
			if(isMouseDown != mouseState)
			{
				dragObject.style.position = "absolute";// VLAD: this has to be set here, not in initialization part
				if(isDefined(s.onStart))s.onStart(document.getElementById(s.draggingElementId),dragObject);
				dragObject.style.display = "block";
			}
			if(((isDefined(d) && d.id != "") || dragObject.getAttribute(MOVE_COPY_ATTRIBUTE)) && isDefined(s.onDrag))
				s.onDrag(d,currentMousePosition);
			mouseState = isMouseDown;
			return false;
		}
		else return true;
	}
	var mouseDown = function(ev)
	{
		// freaking firefox :(
		if(ev && ev.target && ev.target.tagName && ((ev.target.tagName == "INPUT" && ev.target.type == "text") || (ev.target.tagName == "TEXTAREA")))
		{
			mouseState = isMouseDown = false;
			return true;
		}
		mouseState = false;
		isMouseDown = true;
		return false;
	}
	var mouseUp = function(ev)
	{
		mouseState = isMouseDown = false;
		if(isDefined(dragObject))
		{
			var s = getSpaceByName(dragObject.getAttribute(SPACE_NAME_ATTRIBUTE));
			if(dragObject.getAttribute(MOVE_COPY_ATTRIBUTE))
				document.body.removeChild(dragObject);
			dragObject = null;
			if(isDefined(s.onDrop))s.onDrop();
		}
		return false;
	}
	var getSpaceByName = function(name)
	{
		for(var i in spaces)
			if(spaces[i].name == name)
				return spaces[i];
		return null;
	}
	var getMouseOffset = function(target,ev)
	{
		ev = ev || window.event;
		var docPos = Sys.UI.DomElement.getBounds(target);
		var mousePos = mouseCoords(ev);
		return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
	}
	var mouseCoords = function(ev)
	{
		if(ev.pageX || ev.pageY){return {x:ev.pageX, y:ev.pageY};}
		return {
			x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
			y:ev.clientY + document.body.scrollTop  - document.body.clientTop
		};
	}
}