	// # # # Rect object # # # ----------------------------------------------------------------------

	Rect = function(obj)
	{
		var rect    = $(obj).offset();
		rect.width  = $(obj).width();
		rect.height = $(obj).height();

		this.left   = rect.left;
		this.top    = rect.top;
		this.width  = rect.width;
		this.height = rect.height;

		this.bottom = this.top+this.height;
		this.right  = this.left+this.width;

		this.contains = function(other)
		{
			return (other.top >= this.top && other.bottom <= this.bottom) && 
					(other.left >= this.left  && other.right <= this.right);
		}

		this.extend = function(value)
		{
			this.left-=value;
			this.top-=value;
			this.width+=2*value;
			this.height+=2*value;

			this.bottom+=value;
			this.right+=value;

			return this;
		}
	}






	// # # # jQuery.fn.extend # # # -----------------------------------------------------------------

	jQuery.fn.extend({
		resizeTo: function(sizeTo) {
			return this.each(function() {$(this).height($(sizeTo).height()).width($(sizeTo).width())});
		},
		shadow: function() {
			return this.each(function() {$(this).after("\<div>&nbsp;\<\/div>").next().resizeTo(this).hide('slow',function(){$(this).remove()})});
		},
		detach: function() {
			return this.each(function() {if(document.all){$(this).remove()} else {$(this).shadow().remove()}});
		},
		drag: function() {
			$("div:not(.dropTarget) > div:not(.dropTarget)").each(function(){setSelection(this.parentNode,false);});
			return this.each(function() {lastDropPosition=this.nextSibling ? this.nextSibling : this.parentNode;$('div#dragDiv').resizeTo(this).show();$(this).removeClass('selected').detach().appendTo('div#dragDiv').css('opacity',0.5)});
		},
		dropTo: function(target) {
				if(!target || !$('#dragDiv').children().length)
					return;

				$("div:not(.dropTarget) > div:not(.dropTarget)").each(function(){setSelection(this.parentNode,true);});

				var r = this.each(function() {
				if($(target).hasClass('dropTarget')){$(this).appendTo(target).css('opacity',1).setEvents()}else{$(target).before(this);$(this).css('opacity',1).setEvents()}

				realFirstChild(this).value = $(target).hasClass('dropTarget') ? target.id : target.parentNode.id;
				if(typeof(this.onDropTo)=='function')
					this.onDropTo(target);
				});

				target2call = $(target).hasClass('dropTarget') ? target : target.parentNode;
				if(typeof(target2call.onDropItems)=='function')
					target2call.onDropItems();

				return r;
		},
		setEvents: function() {
			return this.each(function() {$(this).mousedown(mouseDown).mouseup(mouseUp).mouseover(mouseOver).mouseout(mouseOut);});
		},
		check: function(chk) {
			return this.each(function() {this.checked=chk});
		},
		selectIn: function(selection,toggle) {
			return this.each(function() {
				var myRect = new Rect(this);
				var selectionRect = new Rect(selection);

				if(selectionRect.extend(10).contains(myRect))
					toggle ? $(this).toggleClass('selected') : $(this).addClass('selected');
			});
		}
	});






	// # # # setSelection # # # ---------------------------------------------------------------------
	function setSelection(target,value)
	{
		if (typeof(target.onselectstart)!="undefined") //IE route
			target.onselectstart=function(){return value};
		else
			target.style.MozUserSelect=value ? "" : "none";
/*		else if (typeof(target.style.MozUserSelect)!="undefined") //Firefox route
			target.style.MozUserSelect=value ? "" : "none";*/
	}






	// # # # realFirstChild # # # -------------------------------------------------------------------
	function realFirstChild(node)
	{
		var first = node.firstChild;
		while(first && first.nodeName=='#text')
			first = first.nextSibling;
		return first;
	}




	// # # # dragable event handlers # # # ----------------------------------------------------------

	var lastDropPosition = null;
	var lastSelected = null;
	var mouseOver = function(e){if(lastDropPosition){$(this).css({'borderTop':'6px solid white'});}};
	var mouseOut  = function(e){$(this).css('borderTop','');};
	var mouseDown = function(e)
		{
			e.stopPropagation();
			selectionPaint = null;
			if(e.ctrlKey || e.ctrlLeft || e.ctrlRight)
			{
				$(this).toggleClass('selected');
				lastSelected=this;
			}
			else if(e.shiftKey)
			{
				var mySelector = (lastSelected && lastSelected.parentNode==this.parentNode) ? lastSelected : realFirstChild(this.parentNode);
				var direction = $(mySelector).position().top < $(this).position().top ? 1 : 0;
				while(mySelector!=this)
				{
					$(mySelector).addClass('selected');
					mySelector = direction ? mySelector.nextSibling : mySelector.previousSibling;
				}
				$(this).addClass('selected');
			}
			else if($(this).hasClass('selected'))
			{
				$(this.parentNode).children().filter('div.dragable.selected').drag();
			}
			else
			{
				lastSelected=this;
				$(this).toggleClass('selected').data('pressed',window.setTimeout(function(){$(lastSelected.parentNode).children().filter('div.dragable.selected').drag();},200));
			}
	}

	var mouseUp = function(e)
		{
			if($(this).data('pressed'))
			{
				window.clearTimeout($(this).data('pressed'));
				$(this).data('pressed',0);
			}

			if(selectionPaint) return;
			e.stopPropagation();
			if(lastDropPosition)
			{
				$('div#dragDiv').children().dropTo(this);
				mouseUpDefault();
			}
		};


	var mouseUpDefault = function()
	{
		lastDropPosition=null;
		$('div.dragable');
		$('div#dragDiv').hide();
		$("input[type='checkbox']").mousedown(function(e){e.stopPropagation();});
	}






	// # # # document ready # # # -------------------------------------------------------------------

	var selectionPaint = null;
	$(document).ready(
		function()
		{
			$("body > *:first").before("<div id='dragDiv' style='position:absolute;z-index:999;left:0;top:0;display:none'></div>");

			$("div.dropTarget > div").each(function(){setSelection(this.parentNode,false);});


			$(document).mousemove(
								function(e){$('div#dragDiv').css({'left':10+(e.pageX),'top':10+(e.pageY)});})

						.mouseup(
								function(e){
											if(!e.shiftKey && !e.ctrlKey)
												$('div.dragable').removeClass('selected');

											selectionPaint = false;
											$('div#dragDiv').children().dropTo(lastDropPosition);
											mouseUpDefault();
											});



			$('div.dragable').setEvents().css({'cursor':'move'});
			$('div.dropTarget').mouseover(function(){$(this).css('border-color','red').css('paddingBottom',lastDropPosition ? 20 : 2)}).mouseout(function(){$(this).css('border-color','').css('paddingBottom',2)})
								.mouseup(
									function(e)
									{
										if(selectionPaint) return;
										e.stopPropagation();
										$('div#dragDiv').children().dropTo(this);
										mouseUpDefault();
									}
								);
		}
		);
