////////////////////////////////////////////////////////////////
// Copyright © 2010 ReFreezed.com
////////////////////////////////////////////////////////////////



function RearrangeableList()
{















	/* MEMBERS
	********************************************************************************************************************************/



	var mDraggedListItem = null;
	var mDraggedListItemStartPosition = null;
	var mEvents = {'change': [], 'move': []};
	var mListElement = null;
	var mMouseOffsetY = 0;
	var mMoveEventHasOccured = false;















	/* INIT
	********************************************************************************************************************************/



	__constructor.apply(this, arguments);

	function __constructor(listElement)
	{
		mListElement = $(listElement).get(0);
		$(mListElement).css('overflow', 'visible'); // Handles within the list element might get fully or partly hidden without this line
	}















	/* PRIVATE METHODS
	********************************************************************************************************************************/



	function AddDraggingClass()
	{
		mDraggedListItem.addClass('dragging');
	}



	function HandleDragging(e)
	{
		var mouse_coords = get_mouse_coords(e);
		var new_y = mouse_coords.y-mDraggedListItemStartPosition.top-mMouseOffsetY;
		var next_list_item = mDraggedListItem.next();
		if (next_list_item.length > 0)
		{
			if (new_y > next_list_item.offset().top-next_list_item.height()/2-mDraggedListItemStartPosition.top)
			{
				TriggerEvent('move', e);
				mMoveEventHasOccured = true;
				mDraggedListItem.insertAfter(next_list_item);
				RemoveDraggingClass();
				InitListItemStartPosition();
				AddDraggingClass();
				HandleDragging(e);
				return;
			}
		}
		else
		{
			if (new_y > 0)
				new_y = 0;
		}
		var prev_list_item = mDraggedListItem.prev();
		if (prev_list_item.length > 0)
		{
			if (new_y < prev_list_item.offset().top+prev_list_item.height()/2-mDraggedListItemStartPosition.top)
			{
				TriggerEvent('move', e);
				mMoveEventHasOccured = true;
				mDraggedListItem.insertBefore(prev_list_item);
				RemoveDraggingClass();
				InitListItemStartPosition();
				AddDraggingClass();
				HandleDragging(e);
				return;
			}
		}
		else
		{
			if (new_y < 0)
				new_y = 0;
		}
		mDraggedListItem.css('top', new_y);
	}



	function InitListItemStartPosition()
	{
		mDraggedListItemStartPosition = mDraggedListItem.offset();
	}



	function InitMouseOffset(e)
	{
		var mouse_coords = get_mouse_coords(e);
		mMouseOffsetY = mouse_coords.y-mDraggedListItemStartPosition.top;
	}



	function RemoveDraggingClass()
	{
		mDraggedListItem.removeClass('dragging').css({'top': '', 'left': ''});
	}



	function StartDragging(e)
	{
		InitListItemStartPosition();
		InitMouseOffset(e);
		AddDraggingClass();
		mMoveEventHasOccured = false;
		$('body').bind('mousemove', HandleDragging).bind('mouseup', StopDragging);
	}



	function StopDragging(e)
	{
		$('body').unbind('mousemove', HandleDragging).unbind('mouseup', StopDragging);
		RemoveDraggingClass();
		if (mMoveEventHasOccured)
			TriggerEvent('change', e);
	}



	function TriggerEvent(eventName, e)
	{
		if (mEvents[eventName] === undefined)
			return;
		var len = mEvents[eventName].length;
		for (var i = 0; i < len; i++)
			mEvents[eventName][i].call(mListElement, e);
	}















	/* PUBLIC METHODS
	********************************************************************************************************************************/

	/*
		BindEvent()
		ConfigureDragHandle()
	*/



	this.BindEvent = function(eventName, func)
	{
		if (mEvents[eventName] === undefined)
			return;
		mEvents[eventName].push(func);
	}



	this.SetDragHandles = function(handleExpression)
	{
		$('li', mListElement).find(handleExpression).bind('mousedown', function(e)
		{
			mDraggedListItem = $(this).parents('li:eq(0)');
			StartDragging(e);
			return false; // To prevent the browser's default action (Selecting text)
		});
	};















}
