/*
http://www.xulplanet.com/tutorials/mozsdk/dragdrop.php
#
ondraggesture
Called when the user starts dragging the element, which normally happens when the user holds down the mouse button and moves the mouse. The script in this handler should set up a drag session.
#
ondragover
This event handler is called for an element when something is being dragged over top of it. If the object can be dropped on the element, the drag session should be notified.
#
ondragenter
Called for an element when the mouse pointer first moves over the element while something is being dragged. This might be used to change the appearance of the element to indicate to the user that the object can be dropped on it.
#
ondragexit
Called for an element when the mouse pointer moves out of an element while something is being dragged. The is also called after a drop is complete so that an element has a chance to remove any highlighting or other indication.
#
ondragdrop
This event handler is called for an element when something is dropped on the element. At this point, the user has already released the mouse button. The element can simply ignore the event or can handle it some way, such as pasting the dragged object into itself.
*/

function Drag(el)
{
	
	var caller = 0;
	
	if( typeof(Drag.caller) == 'undefined' )
	{	//opera does not support caller property of function
	
		if( arguments.length && arguments[0] == 'getInstance' )
		{
			caller = 1;
		}
	
	}
	else
	{
		if (Drag.caller == Drag.getInstance) 
		{
			caller = 1;
   		}
	}
	
	if( !caller )
	{
      	 	throw new Error("There is no public constructor for Drag.");
	}
	
	this.last_clicked = null;
	this.drag_className = 'dragable'; // class name for dragable elements
	this.drag_dropbox_className = 'dropbox';
	this.drag_dropped_onto = null;
	this.drop_focus_className = 'dropbox-focused';
	this.drop_unfocus_className = 'dropbox-unfocused';
	this.contiguous_offset = 5;
	this.elementX_on_drag_start = null;
	this.elementY_on_drag_start = null;
	this.start_drag_x = null;
	this.start_drag_y = null;
	this.zIndex = 0;
	this.drag_approved = false;
	this.drag_obj = null;
	this.drag_obj_width = null;
	this.drag_obj_height = null;
	this.drag_obj_parent = null;
	this.drag_obj_parent_borders = null;
	this.drag_el_id = el;
	this.dragables = [];
	this.drag_constrainer = null;
	this.drag_constrainers_class = {};
	this.drag_constrainer_id = '';
	this.drag_constrainers = {};
	this.drag_move_callbacks = {};
	this.drag_start_callbacks = {};
	this.drag_stop_callbacks = {};
	this.constrainer_is_parent = {};
	this.stop_drag_x = null;
	this.stop_drag_y = null;
	this.stop_drag_evt = null;
}	

Drag.prototype.initialise = function()		
{

	var me = this;
	
	//this.dragables[this.dragables.length] = allDivs[idx].id;
	//EventManager.Add(allDivs[idx],'mousedown',function(e){_this.drags(e)},false);
	if(window.event && !browser.opera)
	{
		EventManager.Add(document,'keydown',function(e){  me.listen_for_keycode(e) },false);
	}
	else
	{
		EventManager.Add(document,'keypress',function(e){  me.listen_for_keycode(e) },false);
	}

	EventManager.Add(document,'mouseup',function(e){ me.stop_drag(e)},false);
	EventManager.Add(document,'mousemove',function(e){ me.move(e) },false);

	
	
}

Drag.prototype.set_className = function(class_name)
{
	this.drag_className = class_name;
}

Drag.prototype.stop_drag = function(e)
{

	this.drag_approved = false;
	this.drag_obj = null;
	var evt = (e) ? e : window.event;
	var evt_id = this.last_clicked;
	var el = getEl(evt_id);
	
	if( !el )
	{
		return;
	}
	
	if( el.className.indexOf(this.drag_className) == -1 )
	{
		return null;
	}
	
	var coords;
	
	if( !(coords = get_mouse_coords(e)) )
	{
		return;
	}
	
	this.stop_drag_x = coords.x;
	this.stop_drag_y = coords.y;
	this.stop_drag_evt = evt;

	if( typeof(this.drag_stop_callbacks[evt_id]) != 'undefined' && (func = window[this.drag_stop_callbacks[evt_id]]) )
	{
		func();
	}
}

Drag.prototype.move = function(e)
{
	
	if( !this.drag_approved )
	{
		return;
	}
		
	var evt = (e) ? e : window.event;
	
	if( !evt )
	{
		return;
	}
	
	var evt_id = this.get_dragged_id();
	
	if( !this.dragables.in_array(evt_id) )
	{
		return;
		
	}
	
	var coords;
	
	if( !(coords = get_mouse_coords(e)) )
	{
		return;
	}

	var leftPos = this.elementX_on_drag_start + coords.x-this.start_drag_x;
	var topPos =  this.elementY_on_drag_start + coords.y-this.start_drag_y;
	
	if( this.drag_constrainer_id )
	{
		var constraints;
		
		if( (constraints = this.constrain_drag_within_parent(leftPos,topPos)) )
		{
			leftPos = constraints.x;
			topPos = constraints.y;
		}
		
	}
	
	this.drag_obj.setY(topPos);
	this.drag_obj.setX(leftPos);
	
	if( window['extra_dragging_actions'] )
	{
		// extra_dragging_actions(e,leftPos,topPos);
	}
	
	if( typeof(this.drag_move_callbacks[evt_id]) != 'undefined' && (func = window[this.drag_move_callbacks[evt_id]]) )
	{
		
		func (leftPos,topPos,evt_id);
	}

	
	stop_bubble(evt);
	disable_text_selection(document.body);
	return false;

}

Drag.prototype.constrain_drag_within_parent = function(leftPos,topPos)
{
	var borders, x=leftPos, y=topPos;
	
	//output(get_mouse_coords(e).y);
	
	if( this.drag_constrainer_id )
	{
	//	getEl('drag-limits2').innerHTML = this.drag_constrainer_id;
		if( (this.drag_constrainer = theObjs[this.drag_constrainer_id]) )
		{
			borders = this.drag_constrainer.get_coordinates();
		}
	}
	
	if( !borders )
	{
		return null;
	}

	//output(parseInt(getStyleById(this.drag_constrainer_id,'borderLeftWidth','border-left-width')));
	if( typeof(this.constrainer_is_parent[this.last_clicked]) != 'undefined' )
	{		
		x -= this.drag_constrainer.getX();
		y -= this.drag_constrainer.getY();
		
		var bordersX = parseInt(getStyleById(this.drag_constrainer_id,'borderLeftWidth','border-left-width'))
		+ parseInt(getStyleById(this.drag_constrainer_id,'borderRightWidth','border-right-width'));
	
		var bordersY = parseInt(getStyleById(this.drag_constrainer_id,'borderTopWidth','border-top-width')) 
		+ parseInt(getStyleById(this.drag_constrainer_id,'borderBottomWidth','border-bottom-width'));
		//getEl('drag-limits2').innerHTML = ('(is parent) topPos = ' + topPos + ' borders top = ' + borders.top );

		var limitY = borders.bottom-this.drag_obj_height-bordersY;
		
		if( topPos > limitY )
		{	// if dragged obj is below bottom of parent reset it to bottom
			y = limitY - borders.top
		}
		else if( topPos < borders.top )
		{	// if dragged el is above top of parent reset it to top
			y = 0;
		}
		
		var limitX = borders.right - this.drag_obj_width - bordersX;

		if( leftPos > limitX )
		{	// if dragged el is passed right side of parent reset it to right
			x =  limitX - borders.left

		} 
		else if( leftPos < borders.left )
		{	// if dragged el is to the left  of parent reset it to left
			x = 0;
		}
	}
	else
	{
	//	getEl('drag-limits2').innerHTML = ('topPos = ' + topPos + ' borders top = ' + borders.top );
		if( topPos > borders.bottom-this.drag_obj_height )
		{	// if dragged obj is below bottom of parent reset it to bottom
			y = borders.bottom - this.drag_obj_height;
		}
		else if( topPos < borders.top )
		{	// if dragged el is above top of parent reset it to top
			y = borders.top;
		}

		if( leftPos > borders.right - this.drag_obj_width )
		{	// if dragged el is passed right side of parent reset it to right
			x = borders.right  - this.drag_obj_width;

		} 
		else if( leftPos < borders.left )
		{	// if dragged el is to the left  of parent reset it to left
			x = borders.left;
		}
	
	}

	return { x : x, y : y};
}

Drag.prototype.set_constrainer = function(drag_el,constrainer)
{
	// string id of container el
	if( getEl(drag_el) && getEl(constrainer) )
	{
		this.drag_constrainers[drag_el] = constrainer;
	}
}

Drag.prototype.set_constrainer_class = function(drag_el,constrainer_class)
{
	// string id of container el
	var el = getEl(drag_el);
	
	if( el )
	{
		this.drag_constrainers_class[drag_el] = constrainer_class;
		
		while( el != null )
		{	// if mousemove occurs in child element iterate through parents until the dragable element is found

				var el_id = el.id;

				if( el_id && el.className == constrainer_class )
				{
					this.set_constrainer(drag_el,el_id);
					break;
				}

			el = el.parentNode;
		}
	}
}

Drag.prototype.set_move_callback = function(drag_el,callback)
{
	// string id of container el
	if( getEl(drag_el) )
	{
		this.drag_move_callbacks[drag_el] = callback;
	}
}

Drag.prototype.set_start_callback = function(drag_el,callback)
{
	// string id of container el
	if( getEl(drag_el) )
	{
		this.drag_start_callbacks[drag_el] = callback;
	}
}

Drag.prototype.set_stop_callback = function(drag_el,callback)
{
	// string id of container el
	if( getEl(drag_el) )
	{
		this.drag_stop_callbacks[drag_el] = callback;
	}
}

Drag.prototype.set_drop_focus_className = function(class_name)
{
	this.drop_focus_className += class_name;
}

Drag.prototype.get_drop_focus_className = function()
{
	return this.drop_focus_className;
}

Drag.prototype.set_drop_unfocus_className = function(class_name)
{
	this.drop_unfocus_className += class_name;
}

Drag.prototype.get_drop_unfocus_className = function()
{
	return this.drop_unfocus_className;
}

this.drop_unfocus_className

function output(x)
{
	var el;
	
	if( (el = getEl('watch')) )
	{
		el.innerHTML = x
	}
}

Drag.prototype.drags = function(e)
{
	if( typeof(theObjs) == 'undefined' )
	{	// a partially loaded page can fire events before window.onload and complain about theObjs being undefined
		
		return;
	}

	var el = get_evt_targ(e);
	var clicked_el = el.id;
	var evt = (e) ? e : window.event;

	this.drag_obj_constrained = false;
	this.last_clicked = '';

	while( el != null )
	{	// if mousemove occurs in child element iterate through parents until the dragable element is found

			var el_id = el.id;

			if( el_id && this.dragables.in_array(el_id) )
			{
				this.last_clicked = el_id;
			}

			if( el_id && ( el_id == this.drag_constrainers[clicked_el] ) )
			{
				//this.drag_obj_parent = theObjs[el_id];
				//output('while: ' + el_id);
				this.drag_obj_constrained = true;
				this.constrainer_is_parent[this.last_clicked] = el_id
				break;
			}

		el = el.parentNode;
	}
	
	if( !(this.drag_obj = theObjs[this.last_clicked]) ) 
	{
		return;
	}
	
	this.drag_constrainer_id = (this.drag_constrainers[this.last_clicked] ) ?  this.drag_constrainers[this.last_clicked] : '';
	this.drag_approved=true;
	this.drag_obj_width = this.drag_obj.getWidth();
	this.drag_obj_height = this.drag_obj.getHeight();
	this.drag_obj.setPosition('absolute');
	this.drag_obj.setZindex(++this.zIndex);
	this.elementX_on_drag_start=parseInt( this.drag_obj.getX() ); // left coordinate of element
	this.elementY_on_drag_start=parseInt( this.drag_obj.getY() ); // top coordinate of element
	var coords = get_mouse_coords(evt);
	this.start_drag_x = coords.x; // where in window clicked
	this.start_drag_y = coords.y;
	
	if( typeof(this.drag_start_callbacks[this.last_clicked]) != 'undefined' && (func = window[this.drag_start_callbacks[this.last_clicked]]) )
	{
		func (evt,coords.x,coords.y);
	}
	stop_bubble(evt);
	return false;
}

Drag.prototype.listen_for_keycode = function(e)
{
	var el;
	
	if( !(el = theObjs[this.last_clicked]) )
	{
		return;
	}


	
	var inc = 1;
	
	if(!e) 
	{
		if( !(e = window.event) )
		{
			return;
		}
	}
	
	if(e['shiftKey']) 
	{
		inc = 10;
	}

	var currX = parseInt( el.getX() );
	var currY = parseInt( el.getY() );
	
	switch (e.keyCode)
	{
   		case 37 : currX = currX - inc;
      		break;

		case 38 : currY = currY - inc;
      		break;

		case 39 : currX = currX + inc;
      		break;

		case 40 : currY = currY + inc;
      		break;
	}
	
	if( (constraints = this.constrain_drag_within_parent(currX ,currY)) )
	{
		currX  = constraints.x;
		currY = constraints.y;
	}

	el.setX(currX);
        el.setY(currY);
}

Drag.prototype.reset_drag = function()
{
	if(this.drag_obj) 
	{
		this.drag_obj.setPosition('static');
		this.drag_obj.setY(parseInt(this.drag_obj.getY())); // resets the absolute position to the returned position so next move starts from static position
		this.drag_obj.setX(parseInt(this.drag_obj.getX()));
	}
}
	
Drag.prototype.add = function(el_id)
{
	var new_drag_el;
	var _this = this;
		
	if( this.dragables[el_id] )
	{
		return;
	}
	//alert(el_id);
	if( new_drag_el = getEl(el_id) )
	{
		if( ! theObjs[el_id] )
		{
			theObjs[el_id] = new dom_object(new_drag_el);
		}

		var el = theObjs[el_id];
		
		if( el.getClass().indexOf('dragable') == -1 )
		{
			el.setClass(this.drag_className);
		}

		this.dragables[this.dragables.length] = el_id;
		
		EventManager.Add(new_drag_el,'mousedown',function(e){_this.drags(e); return false},false);
	//	EventManager.Add(new_drag_el,'dblclick',function(e){_this.reset_drag(e)},false);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////

Drag.prototype.compute_dropped = function()
{
	alert( 'dropped onto: ' + this.drag_dropped_onto );
}

/////////////////////////////////////////////////////////////////////////////////////////////////////

function get_scroll(axis)
{
	if( axis.toLowerCase() == 'x')
	{

		if( typeof(window.pageXOffset) != 'undefined' )
		{
			return window.pageXOffset;
		}
		else if( typeof(window.scrollX) != 'undefined' )
		{
			return window.scrollX;
			
		}
	}
	if( axis.toLowerCase() == 'y')
	{
		if( typeof(window.pageYOffset) != 'undefined' )
		{
			return window.pageYOffset;
		}
		else if( typeof(window.scrollY) != 'undefined' )
		{
			return window.scrollY;
		}
	}
	
	return 0;
}

Drag.prototype.get_drop_target = function()
{
	return this.drag_dropped_onto;
}

Drag.prototype.get_stop_drag_x = function()
{
	return this.stop_drag_x;
}

Drag.prototype.get_stop_drag_y = function()
{
	return this.stop_drag_y;
}

Drag.prototype.get_start_drag_x = function()
{
	return this.start_drag_x; // mouse coordinate on start drag
}

Drag.prototype.get_start_drag_y = function()
{
	return this.start_drag_y; // mouse coordinate on start drag
}

Drag.prototype.get_dragged_id = function()
{
	return this.last_clicked;
}

Drag.prototype.set_dropbox_className = function(class_name)
{
	this.drag_dropbox_className = class_name;
}

Drag.prototype.get_dropbox_className = function()
{
	return this.drag_dropbox_className;
}

Drag.prototype.get_contiguous = function(dragged_class,target_class)
{
	// returns id of element with target_class as className
	var dragged_el_id;
	var drop_boxes;
	var targets = [];
	target_class = target_class || this.drag_dropbox_className;
	dragged_class = dragged_class || this.drag_className;
	
	if( !target_class )
	{
		return null;
	}
		 
	if( !(drop_boxes = getElementsByClass(target_class,'','div')) )
	{
		return null;
 	}
	
	if( this.get_dragged_id() )
	{
		dragged_el = theObjs[this.get_dragged_id()];
	}
	else
	{
		return null;
	}

	if( dragged_class != '*' )
	{
		var regexp = new RegExp("\\b" + dragged_class + "\\b");
		var dragged_class = dragged_el.getClass();
		
		if( !regexp.test(dragged_class))
		{
			return null;
		}
	}
	
//	var coords = find_mouseclick_coords(this.evt); // coordinates of mouse when drag is stopped on mouseup event
	var drop_box_left = null;
	
	drag_x = dragged_el.getX(); // left coordinate of element when drag is stopped on mouseup
	drag_y = dragged_el.getY(); // right coordinate of element when drag is stopped on mouseup
	drag_w = dragged_el.getWidth();
	drag_h = dragged_el.getHeight();

	drag_right_side = drag_x + drag_w;
	drag_bottom_side = drag_y + drag_h;
	var drag_half_width = parseInt(drag_w/2);
	var drag_half_height = parseInt(drag_h/2);
	
	if( /\bdef\b/.test(dragged_el.actual.className) )
	{	// send a definition element (class 'def') back to position preceeding drag
		reset_drag();
	}
	
	for(var i=0;i<drop_boxes.length;i++)
	{
		//alert( drop_boxes[i].id );
	
		var dropbox_id = drop_boxes[i].id
		if( this.get_dragged_id() == dropbox_id )
		{
			continue;
		}
		var dropbox = theObjs[dropbox_id];
		var drop_x_margin = drag_half_width;
		var drop_y_margin = drag_half_height;
		var drop_left = dropbox.getX();
		var drop_top = dropbox.getY();
		var drop_bottom = drop_top + dropbox.getHeight();
		var drop_right = drop_left + dropbox.getWidth();
		var xtest = false;
		//  - parseInt(drag_w/2)  - parseInt(drag_w/2
	//	alert( 'drag x : ' + drag_x + ' drop x: ' + drop_x + ' drag y: ' + drag_y + ' drop y: ' + drop_y);
	
		if( (drag_x > (drop_left - this.contiguous_offset) ) && (drag_right_side < (drop_right+	this.contiguous_offset)) )
		{
			if( ((drag_y > (drop_top-this.contiguous_offset))) && (drag_bottom_side < (drop_bottom + this.contiguous_offset)))
			{
				targets[targets.length] = dropbox_id;
			}	
		}
	}
	
	if(targets.length)
	{
		return targets;
	}
	
	return null;
}

Drag.__instance__ = null;  //define the static property

Drag.getInstance = function () 
{
    if (this.__instance__ == null) 
    {
        this.__instance__ = new Drag('getInstance');
	this.__instance__.initialise();
    }

    return this.__instance__;
}	

function init_Drag_proto_enclosed()
{
	drag = Drag.getInstance();
}

			
			
	
				
