////////////////////////////////////////////////////////////////////////////////////////////
// Drag and drop ability client-side script
////////////////////////////////////////////////////////////////////////////////////////////

addNamespace("Drag");

////////////////////////////////////////////////////////////////////////////////////////////

Drag.Controller = Class.create();
Drag.Controller.prototype =
{
	initialize: function(control, parent)
	{
		if (!control)
			return;
		
		this.control = $(control);
		this.id = this.control.id;
		
		if (parent)
			this.parent = $(parent);
		else
			this.parent = Core.Utility.getRootNode();
		
		this.capturing = false;
		this.ondrag = null;
		this.ondragstart = null;
		this.ondrop = null;
		this.ondragging = null;
		this.moveX = this.moveY = 0;
		this.currentX = this.currentY = 0;
		this.targets = new Core.Collection();
		
		this.control.onmousedown = this.onmousedown.bindAsEventListener(this);
		this.control.onselectstart = function(){return false;};
		
		this._isDragging = false;
	},
	
	onmousedown: function(e)
	{
		Core.Utility.setEvent(e);
		
		if (this.ondrag)
		{
			if (!this.ondrag())
				return;
		}
		
		this.capturing = true;
		this.currentX = Core.Utility.getClientX();
		this.currentY = Core.Utility.getClientY();
		
		this._onmouseup = document.onmouseup;
		this._onmousemove = document.onmousemove;
		document.onmouseup = this.onmouseup.bindAsEventListener(this);
		document.onmousemove = this.onmousemove.bindAsEventListener(this);
	},
	
	onmouseup: function(e)
	{
		Core.Utility.setEvent(e);
		
		if (!this.capturing)
			return;
		
		if (this._isDragging && this.ondrop)
			this.ondrop();
		
		this.reset();
		this.capturing = false;
		this._isDragging = false;
		this.currentX = this.currentY = 0;
		document.onmouseup = this._onmouseup;
		document.onmousemove = this._onmousemove;
		
		for (i = 0; i < this.targets.items.length; i++)
			this.targets.items[i].notify(this);
	},
	
	onmousemove: function(e)
	{
		Core.Utility.setEvent(e);
		
		if (!this.capturing)
			return;
		
		var offsetX = this.currentX - Core.Utility.getClientX();
		var offsetY = this.currentY - Core.Utility.getClientY();
		this.moveX -= offsetX;
		this.moveY -= offsetY;
		this.currentX = Core.Utility.getClientX();
		this.currentY = Core.Utility.getClientY();
		
		if (!this._isDragging && (this.moveX != 0 || this.moveY != 0))
		{
			if (this.ondragstart)
			{
				if (!this.ondragstart())
					return;
			}
			
			this._isDragging = true;
		}
		
		for (i = 0; i < this.targets.items.length; i++)
		{
			if (!this.targets.items[i].notify(this))
				return;
		}
		
		var bounds = Core.Utility.getElementBounds(this.control, this.parent);
		var targetTop = bounds.top + this.moveY;
		var targetLeft = bounds.left + this.moveX;
		
		// constrain to parent
		var parentBounds = Core.Utility.getElementSize(this.parent);
		var parentLeft = 0;//parentBounds.left;
		var parentTop = 0;//parentBounds.top;
		var parentBottom = parentTop + parentBounds.height;
		var parentRight = parentLeft + parentBounds.width;
		
		if (targetTop < parentTop)
			targetTop = parentTop;
		else if (targetTop + bounds.height > parentBottom && parentBottom > 0)
		{
			if (this.parent != Core.Utility.getRootNode())
				targetTop = parentBottom - bounds.height;
		}
		
		if (targetLeft < parentLeft)
			targetLeft = parentLeft;
		else if (targetLeft + bounds.width > parentRight && parentRight > 0)
			targetLeft = parentRight - bounds.width;
		
		if (targetLeft == bounds.left && targetTop == bounds.top)
			return;
		
		if (this.ondragging)
		{
			if (!this.ondragging())
				return;
		}
		
		this.control.style.top = Core.Utility.toSize(targetTop);
		this.control.style.left = Core.Utility.toSize(targetLeft);
		this.reset();
	},
	
	reset: function()
	{
		this.moveX = this.moveY = 0;
	}
};

////////////////////////////////////////////////////////////////////////////////////////////

Drag.StyleClass = Class.inherit(Core.Enum, 
{
	Normal: 0,			// immediate drag source
	Fade: 1,			// immediate drag source with alpha
	Outline: 2,			// drag outline box
	OutlineFade: 3,		// drag outline box with source alpha
	Copy: 4,			// drag copy with alpha
	CopyFade: 5			// drag copy with alpha and source alpha
});
Drag.Style = new Drag.StyleClass("Drag.StyleClass");

////////////////////////////////////////////////////////////////////////////////////////////

Drag.Window = Class.create();
Drag.Window.prototype =
{
	initialize: function(control, parent)
	{
		this.control = $(control);
		this.controller = new Drag.Controller(this.control, parent);
		this.parent = this.controller.parent;
		this.controller.ondragstart = this.dragstart.bind(this);
		this.controller.ondrop = this.drop.bind(this);
		this.controller.ondragging = this.dragging.bind(this);
		
		this.style = Drag.Style.Normal;
		this.fadeAlpha = 0.7;
		this.outlineAlpha = 0.2;
		this._copy = null;
		
		if (control)
			this._zIndex = this.control.style.zIndex;
		
		this.shapeOutline = new Drawing.Rectangle();
		this.shapeOutline.parent = this.parent;
		this.shapeOutline.backColor = new Drawing.Color("000000");
		this.shapeOutline.lineStyle = Drawing.LineStyle.None();
		
		this.ondrag = null;
		this.ondragstart = null;
		this.ondrop = null;
		this.ondragging = null;
	},
	
	dragstart: function()
	{
		if (this.ondragstart)
		{
			if (!this.ondragstart())
				return false;
		}
		
		if (this.style == Drag.Style.Fade || this.style == Drag.Style.OutlineFade || this.style == Drag.Style.CopyFade)
		{
			this.control.style.opacity = this.fadeAlpha;
			this.control.style.filter = "alpha(opacity=" + (this.fadeAlpha * 100) + ")";
		}
		
		if (this.style == Drag.Style.Outline || this.style == Drag.Style.OutlineFade || 
			this.style == Drag.Style.Copy || this.style == Drag.Style.CopyFade)
		{
			if (this.style == Drag.Style.Outline || this.style == Drag.Style.OutlineFade)
			{
				var bounds = Core.Utility.getElementBounds(this.control, this.parent);
				this.shapeOutline.opacity = this.outlineAlpha;
				this.shapeOutline.move(bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height);
				this.controller.control = this.shapeOutline._control;
			}
			else
			{
				this._copy = this.control.cloneNode(true);
				
				/*this._copy = this.control.cloneNode(true);
				this._copy.style.position = "absolute";*/
				this._copy.style.opacity = this.outlineAlpha;
				this._copy.style.filter = "alpha(opacity=" + (this.outlineAlpha * 100) + ")";
				this._copy.onselectstart = function(){return false;};
				
				this.controller.parent.appendChild(this._copy);
				this.controller.control = this._copy;
				
				Position.clone(this.control, this._copy);
			}
		}
		
		this.controller.control.style.zIndex = 999;
		
		return true;
	},
	
	dragging: function()
	{
		if (this.ondragging)
		{
			if (!this.ondragging())
				return false;
		}
		
		return true;
	},
	
	drop: function()
	{
		if (this.style == Drag.Style.Outline || this.style == Drag.Style.OutlineFade)
		{
			this.shapeOutline.hide();
			
			this.control.style.top = Core.Utility.toSize(this.shapeOutline._control.style.top);
			this.control.style.left = Core.Utility.toSize(this.shapeOutline._control.style.left);
		}
		else if (this.style == Drag.Style.Copy || this.style == Drag.Style.CopyFade)
		{
			this.control.style.top = Core.Utility.toSize(this._copy.style.top);
			this.control.style.left = Core.Utility.toSize(this._copy.style.left);
			
			this.controller.parent.removeChild(this._copy);
		}
		
		this.controller.control = this.control;
		this.control.style.opacity = 1;
		this.control.style.filter = "alpha(opacity=100)";
		this.control.style.zIndex = this._zIndex;
		
		if (this.ondrop)
			this.ondrop();
	}
};
////////////////////////////////////////////////////////////////////////////////////////////

Drag.Target = Class.create();
Drag.Target.prototype =
{
	initialize: function(controlOrLeft, top, right, bottom)
	{
		if (top)
		{
			this.top = top;
			this.left = controlOrLeft;
			this.bottom = bottom;
			this.right = right;
			
			this.control = document.createElement("div");
			this.control.style.zIndex = "-100";
			this.control.style.position = "absolute";
			this.control.style.left = Core.Utility.toSize(this.left);
			this.control.style.top = Core.Utility.toSize(this.top);
			this.control.style.width = Core.Utility.toSize(this.right - this.left);
			this.control.style.height = Core.Utility.toSize(this.bottom - this.top);
			
			//this.control = document.createElement("<div style=\"z-index:-100;position:absolute;left:" + this.left + ";top:" + this.top + ";width:" + (this.right - this.left) + ";height:" + (this.bottom - this.top) + "\">");
			document.body.appendChild(this.control);
		}
		else
		{
			this.control = controlOrLeft;
			
			var bounds = Core.Utility.getElementBounds(this.control);
			this.top = bounds.top;
			this.left = bounds.left;
			this.bottom = this.top + bounds.height;
			this.right = this.left + bounds.width;
		}
		
		this.id = String("Target:" + Math.random());
		this.snapTolerance = 20;
		this.sensitivity = 2;
		this.highlight = true;
		
		this._overlay = document.createElement("div");
		this._overlay.style.display = "none";
		this._overlay.style.position = "absolute";
		this._overlay.style.left = Core.Utility.toSize(this.left);
		this._overlay.style.top = Core.Utility.toSize(this.top);
		this._overlay.style.width = Core.Utility.toSize(this.right - this.left);
		this._overlay.style.height = Core.Utility.toSize(this.bottom - this.top);
		this._overlay.style.backgroundColor = "black";
		//this._overlay.style.cssText = "display:none;position:absolute;left:" + this.left + ";top:" + this.top + ";width:" + (this.right - this.left) + ";height:" + (this.bottom - this.top) + ";background-color:black";
		
		this._overlay.onselectstart = function(){return false;};
		document.body.appendChild(this._overlay);
	},
	
	notify: function(controller)
	{
		if (controller.capturing)
		{
			if (this.highlight)
				this.draw();
			
			var snapped = false;
			var mouseX = Core.Utility.getClientX();
			var mouseY = Core.Utility.getClientY();
			
			var bounds = Core.Utility.getElementBounds(this.control);
			var top = bounds.top;
			var left = bounds.left;
			var bottom = top + bounds.height;
			var right = left + bounds.width;
			
			var dragBounds = Core.Utility.getElementBounds(controller.control);
			var dragTop = dragBounds.top;
			var dragLeft = dragBounds.left;
			var dragBottom = dragTop + dragBounds.height;
			var dragRight = dragLeft + dragBounds.width;
			
			var within = Core.Utility.numberWithin;
			var internal = (mouseX >= left && mouseX <= right && mouseY >= top && mouseY <= bottom);
			
			var snapLeft = within(left - this.snapTolerance, left + this.snapTolerance, dragLeft);
			var snapRight = within(right - this.snapTolerance, right + this.snapTolerance, dragRight);
			var snapTop = within(top - this.snapTolerance, top + this.snapTolerance, dragTop);
			var snapBottom = within(bottom - this.snapTolerance, bottom + this.snapTolerance, dragBottom);
			
			if (controller.moveX)
			{
				if (snapLeft && (internal || snapTop || snapBottom) && within(-this.sensitivity, this.sensitivity, controller.moveX))
				{
					snapped = true;
					controller.moveX = 0;
					controller.control.style.left = Core.Utility.toSize(left);
				}
				else if (snapRight && (internal || snapTop || snapBottom) && within(-this.sensitivity, this.sensitivity, controller.moveX))
				{
					snapped = true;
					controller.moveX = 0;
					controller.control.style.left = Core.Utility.toSize(right - (dragRight - dragLeft));
				}
			}
			
			if (controller.moveY)
			{
				if (snapTop && (internal || snapLeft || snapRight) && within(-this.sensitivity, this.sensitivity, controller.moveY))
				{
					snapped = true;
					controller.moveY = 0;
					controller.control.style.top = Core.Utility.toSize(top);
				}
				else if (snapBottom && (internal || snapLeft || snapRight) && within(-this.sensitivity, this.sensitivity, controller.moveY))
				{
					snapped = true;
					controller.moveY = 0;
					controller.control.style.top = Core.Utility.toSize(bottom - (dragBottom - dragTop));
				}
			}
			
			if (this.highlight && (snapped || internal))
			{
				this._overlay.style.opacity = 0.2;
				this._overlay.style.filter = "alpha(opacity=20)";
			}
			else
			{
				this._overlay.style.opacity = 0.1;
				this._overlay.style.filter = "alpha(opacity=10)";
			}
			
			return !snapped;
		}
		else if (this.highlight)
			this._overlay.style.display = "none";
		
		return true;
	},
	
	draw: function()
	{
		this._overlay.style.display = "";
	}
};




