function DragAndDrog()
{
	drag_helper = null;
	drag_helper = document.createElement('DIV');
	drag_helper.style.cssText = 'position:absolute; display:none;';
	
	document.body.appendChild(drag_helper);
		
	this.mouse_offset = null;
	this.is_mouse_down = false;
	this.l_mouse_state = false;
	this.drag_object = null;

	this.drag_drops = [];
	this.current_target = null;
	this.last_target = null;

	this.root_parent = null;
	this.root_sibling = null;

	this.img_poof = new Image();
	this.img_poof.src = 'images/drag_drop_poof.gif';
	
	document.onmousedown = this.MouseDown;
	document.onmousemove = this.MouseMove;
	document.onmouseup   = this.MouseUp;

	return this;
}


DragAndDrog.prototype.CreateDragContainer = function()
{
	var c_drag = ob_dnd.drag_drops.length;
	ob_dnd.drag_drops[c_drag] = [];

	for(var i=0; i < arguments.length; i++)
	{
		var c_obj = arguments[i];
		ob_dnd.drag_drops[c_drag].push(c_obj);
		c_obj.setAttribute('DropObj', c_drag);

		for(var j=0; j<c_obj.childNodes.length; j++)
		{
			if(c_obj.childNodes[j].nodeName=='#text')
				continue;
				
			c_obj.childNodes[j].setAttribute('drag_obj', c_drag);
		}
	}
}


DragAndDrog.prototype.GetPosition = function(e)
{
	var left_pos = 0;
	var top_pos = 0;
	
	while(e.offsetParent)
	{
		left_pos += e.offsetLeft + (e.currentStyle ? (parseInt(e.currentStyle.borderLeftWidth)).NaN0() : 0);
		top_pos += e.offsetTop + (e.currentStyle ? (parseInt(e.currentStyle.borderTopWidth)).NaN0() : 0);
		e = e.offsetParent;
	}
	
	
	left_pos += e.offsetLeft + (e.currentStyle ? (parseInt(e.currentStyle.borderLeftWidth)).NaN0() : 0);
	top_pos += e.offsetTop + (e.currentStyle ? (parseInt(e.currentStyle.borderTopWidth)).NaN0() : 0);
	
	return { x:left_pos, y:top_pos };
}


DragAndDrog.prototype.GetMouseOffset = function(target, ev)
{
	ev = ev || window.event;

	var doc_pos = ob_dnd.GetPosition(target);
	var mouse_position = ob_dnd.MouseCoords(ev);
	return { x:mouse_position.x - doc_pos.x, y:mouse_position.y - doc_pos.y };
}


DragAndDrog.prototype.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 };
}


DragAndDrog.prototype.MakeDraggable = function(item)
{
	if(!item)
		return;
		
	item.onmousedown = function(ev)
	{
		ob_dnd.drag_object = this;
		ob_dnd.mouse_offset = ob_dnd.GetMouseOffset(this, ev);
		return false;
	}
}


DragAndDrog.prototype.MakeClickable = function(item)
{
	if(!item)
		return;
		
	item.onmousedown = function(ev)
	{
		document.getElementById('ClickImage').value = this.name;
	}
}


DragAndDrog.prototype.AddDropTarget = function(item, target)
{
	item.setAttribute('droptarget', target);
}


DragAndDrog.prototype.MouseDown = function(ev)
{
	ev = ev || window.event;
	var target = ev.target || ev.srcElement;

	ob_dnd.is_mouse_down = true;

	if(target.onmousedown || target.getAttribute('drag_obj'))
		return false;
}


DragAndDrog.prototype.MouseMove = function(ev)
{
	ev = ev || window.event;

	var target = ev.target || ev.srcElement;
	var mouse_position = ob_dnd.MouseCoords(ev);

	if(ob_dnd.last_target && (target !== ob_dnd.last_target))
	{
		var orig_class = ob_dnd.last_target.getAttribute('orig_class');
		
		if(orig_class)
			ob_dnd.last_target.className = orig_class;
	}

	var drag_obj = target.getAttribute('drag_obj');

	if(drag_obj != null)
	{
		if(target != ob_dnd.last_target)
		{
			var original_class = target.getAttribute('orig_class');
			
			if(original_class)
			{
				target.setAttribute('orig_class', target.className);
				target.className = original_class;
			}
		}

		// if the user is just starting to drag the element
		if(ob_dnd.is_mouse_down && !ob_dnd.l_mouse_state)
		{
			// mouseDown target
			ob_dnd.current_target = target;

			// Record the mouse x and y offset for the element
			ob_dnd.root_parent = ob_dnd.current_target.parentNode;
			ob_dnd.root_sibling = ob_dnd.current_target.nextSibling;

			ob_dnd.mouse_offset = ob_dnd.GetMouseOffset(target, ev);

			// We remove anything that is in our drag_helper container so we can put a new item in it.
			for(var i=0; i < drag_helper.childNodes.length; i++)
				drag_helper.removeChild(drag_helper.childNodes[i]);

			// Make a copy of the current item and put it in our drag helper.
			drag_helper.appendChild(ob_dnd.current_target.cloneNode(true));
			drag_helper.style.display = 'block';

			// set the class on our helper container if necessary
			var drag_class = ob_dnd.current_target.getAttribute('drag_class');

			if(drag_class)
				drag_helper.firstChild.className = drag_class;

			// disable dragging from our helper container (it's already being dragged)
			drag_helper.firstChild.removeAttribute('drag_obj');

			var drag_cont = ob_dnd.drag_drops[drag_obj];

			ob_dnd.current_target.setAttribute('start_width', parseInt(ob_dnd.current_target.offsetWidth));
			ob_dnd.current_target.setAttribute('start_height', parseInt(ob_dnd.current_target.offsetHeight));
			ob_dnd.current_target.style.display = 'none';

			// loop through each possible drop container
			for(var i=0; i < drag_cont.length; i++)
			{
				with(drag_cont[i])
				{
					var pos = ob_dnd.GetPosition(drag_cont[i]);

					setAttribute('start_width', parseInt(offsetWidth));
					setAttribute('start_height', parseInt(offsetHeight));
					setAttribute('start_left', pos.x);
					setAttribute('start_top', pos.y);
				}

				// loop through each child element of each container
				for(var j=0; j < drag_cont[i].childNodes.length; j++)
				{
					with(drag_cont[i].childNodes[j])
					{
						if((nodeName == '#text') || (drag_cont[i].childNodes[j] == ob_dnd.current_target))
							continue;

						var pos = ob_dnd.GetPosition(drag_cont[i].childNodes[j]);

						// save the width, height and position of each element
						setAttribute('start_width', parseInt(offsetWidth));
						setAttribute('start_height', parseInt(offsetHeight));
						setAttribute('start_left', pos.x);
						setAttribute('start_top', pos.y);
					}
				}
			}
		}
	}

	// If we get in here we are dragging something
	if(ob_dnd.current_target)
	{
		// move our helper container to wherever the mouse is (adjusted by ob_dnd.mouse_offset)
		drag_helper.style.top  = mouse_position.y - ob_dnd.mouse_offset.y + "px";
		drag_helper.style.left = mouse_position.x - ob_dnd.mouse_offset.x + "px";

		var drag_cont  = ob_dnd.drag_drops[ob_dnd.current_target.getAttribute('drag_obj')];
		var active_cont = null;

		var x_pos = mouse_position.x - ob_dnd.mouse_offset.x + (parseInt(ob_dnd.current_target.getAttribute('start_width')) /2);
		var y_pos = mouse_position.y - ob_dnd.mouse_offset.y + (parseInt(ob_dnd.current_target.getAttribute('start_height')) /2);

		// check each drop container to see if our target object is "inside" the container
		for(var i=0; i < drag_cont.length; i++)
		{
			with(drag_cont[i])
			{
				if((parseInt(getAttribute('start_left')) < x_pos) && (parseInt(getAttribute('start_top')) < y_pos) && ((parseInt(getAttribute('start_left')) + parseInt(getAttribute('start_width'))) > x_pos) && ((parseInt(getAttribute('start_top')) + parseInt(getAttribute('start_height'))) > y_pos))
				{
					active_cont = drag_cont[i];
					break;
				}
			}
		}

		// Our target object is in one of our containers.  Check to see where the container belongs
		if(active_cont)
		{
			// before_node will hold the first node AFTER where our div belongs
			var before_node = null;

			// loop through each child node (skipping text nodes).
			for(var i = active_cont.childNodes.length-1; i >= 0; i--)
			{
				with(active_cont.childNodes[i])
				{
					if(nodeName == '#text')
						continue;

					// if the current item is "After" the item being dragged
					if(ob_dnd.current_target != active_cont.childNodes[i] && ((parseInt(getAttribute('start_left')) + parseInt(getAttribute('start_width'))) > x_pos) && ((parseInt(getAttribute('start_top')) + parseInt(getAttribute('start_height'))) > y_pos))
						before_node = active_cont.childNodes[i];
				}
			}

			// the item being dragged belongs before another item
			if(before_node)
			{
				if(before_node != ob_dnd.current_target.nextSibling)
					active_cont.insertBefore(ob_dnd.current_target, before_node);
			}
			// the item being dragged belongs at the end of the current container
			else
			{
				if((ob_dnd.current_target.nextSibling) || (ob_dnd.current_target.parentNode != active_cont))
					active_cont.appendChild(ob_dnd.current_target);
			}

			// the timeout is here because the container doesn't "immediately" resize
			setTimeout(function() {
				var cont_pos = ob_dnd.GetPosition(active_cont);
				active_cont.setAttribute('start_width', parseInt(active_cont.offsetWidth));
				active_cont.setAttribute('start_height', parseInt(active_cont.offsetHeight));
				active_cont.setAttribute('start_left', cont_pos.x);
				active_cont.setAttribute('start_top', cont_pos.y);}, 5);

			// make our drag item visible
			if(ob_dnd.current_target.style.display != '')
			{
				ob_dnd.current_target.style.display = '';
				ob_dnd.current_target.style.visibility = 'hidden';
			}
		}
		else
		{
			// our drag item is not in a container, so hide it.
			if(ob_dnd.current_target.style.display != 'none')
				ob_dnd.current_target.style.display = 'none';
		}
	}

	// track the current mouse state so we can compare against it next time
	ob_dnd.l_mouse_state = ob_dnd.is_mouse_down;

	// what's the target
	ob_dnd.last_target = target;

	if(ob_dnd.drag_object)
	{
		ob_dnd.drag_object.style.position = 'absolute';
		ob_dnd.drag_object.style.top = mouse_position.y - ob_dnd.mouse_offset.y;
		ob_dnd.drag_object.style.left = mouse_position.x - ob_dnd.mouse_offset.x;
	}

	// track the current mouse state so we can compare against it next time
	ob_dnd.l_mouse_state = ob_dnd.is_mouse_down;

	// this prevents items on the page from being highlighted while dragging
	if(ob_dnd.current_target || ob_dnd.drag_object)
		return false;
}


DragAndDrog.prototype.MouseUp = function(ev)
{
	if(ob_dnd.current_target)
	{
		drag_helper.style.display = 'none';
		
		if(ob_dnd.current_target.style.display == 'none')
		{
			if(ob_dnd.root_sibling)
				ob_dnd.root_parent.insertBefore(ob_dnd.current_target, ob_dnd.root_sibling);
			else
				ob_dnd.root_parent.appendChild(ob_dnd.current_target);
		}
		
		ob_dnd.current_target.style.display = '';
		ob_dnd.current_target.style.visibility = 'visible';
	}
	
	
	ob_dnd.current_target  = null;
	
	if(ob_dnd.drag_object)
	{
		ev = ev || window.event;
		var mouse_position = ob_dnd.MouseCoords(ev);
		var drop_tar = ob_dnd.drag_object.getAttribute('droptarget');
		
		if(drop_tar)
		{
			var targ_obj = document.getElementById(drop_tar);
			var obj_pos  = ob_dnd.GetPosition(targ_obj);
			
			if((mouse_position.x > obj_pos.x) && (mouse_position.y > obj_pos.y) && (mouse_position.x < (obj_pos.x + parseInt(targ_obj.offsetWidth))) && (mouse_position.y < (obj_pos.y + parseInt(targ_obj.offsetHeight))))
			{
				var n_src = targ_obj.getAttribute('new_src');
				
				if(n_src)
				{
					ob_dnd.drag_object.src = n_src;
					
					setTimeout(function() {
						if(!ob_dnd.drag_object || !ob_dnd.drag_object.parentNode) return;
						ob_dnd.drag_object.parentNode.removeChild(ob_dnd.drag_object);
						ob_dnd.drag_object = null;
					}, parseInt(targ_obj.getAttribute('timeout')));
				}
				else
				{
					ob_dnd.drag_object.parentNode.removeChild(ob_dnd.drag_object);
				}
			}
		}
	}

	ob_dnd.drag_object = null;
	ob_dnd.is_mouse_down = false;
}


DragAndDrog.prototype.UpdateHiddenField = function(in_field_name)
{
	var sort_value = "";
	var kids = document.getElementById(in_field_name).getElementsByTagName("div");
		
	for(var i=0; i<kids.length; i++)
		sort_value += kids[i].title.replace(new RegExp('Move Item ', 'gi'), '') + "|";
		
	document.getElementById(in_field_name + "_display").innerHTML = " &nbsp; &nbsp; " + sort_value.slice(0, sort_value.length - 1);
	
	return false;
}



Number.prototype.NaN0 = function()
{
	return isNaN(this) ? 0 : this;
}


