;(function($){ // secure $ jQuery alias
/*******************************************************************************************/	
// jquery.event.drag.js - rev 10
// Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)
// Liscensed under the MIT License (MIT-LICENSE.txt)
// http://www.opensource.org/licenses/mit-license.php
// Created: 2008-06-04 | Updated: 2008-08-05
/*******************************************************************************************/
// Events: drag, dragstart, dragend
/*******************************************************************************************/

// jquery method
$.fn.drag = function( fn1, fn2, fn3 ){
	if ( fn2 ) this.bind('dragstart', fn1 ); // 2+ args
	if ( fn3 ) this.bind('dragend', fn3 ); // 3 args
	return !fn1 ? this.trigger('mousedown',{ which:1 }) // 0 args
		: this.bind('drag', fn2 ? fn2 : fn1 ); // 1+ args
	};

// special event configuration
var drag = $.event.special.drag = {
	distance: 0, // default distance dragged before dragstart
	setup: function( data ){
		data = $.extend({ distance: drag.distance }, data || {});
		$.event.add( this, "mousedown", drag.handler, data );
		},
	teardown: function(){
		$.event.remove( this, "mousedown", drag.handler );
		if ( this == drag.dragging ) drag.dragging = drag.proxy = null; // deactivate element
		selectable( this, true ); // enable text selection
		},
	handler: function( event ){ 
		var returnValue;
		// mousedown has initialized
		if ( event.data.elem ){ 
			// update event properties...
			event.dragTarget = event.data.elem; // source element
			event.dragProxy = drag.proxy || event.dragTarget; // proxy element or source
			event.cursorOffsetX = event.data.x - event.data.left; // mousedown offset
			event.cursorOffsetY = event.data.y - event.data.top; // mousedown offset
			event.offsetX = event.pageX - event.cursorOffsetX; // element offset
			event.offsetY = event.pageY - event.cursorOffsetY; // element offset
			}
		// handle various events
		switch ( event.type ){
			// mousedown, left click
			case !drag.dragging && event.which==1 && 'mousedown': // initialize drag
				$.extend( event.data, $( this ).offset(), { 
					x: event.pageX, y: event.pageY, elem: this, 
					dist2: Math.pow( event.data.distance, 2 ) //  x² + y² = distance²
					}); // store some initial attributes
				$.event.add( document.body, "mousemove mouseup", drag.handler, event.data );
				selectable( this, false ); // disable text selection
				return false; // prevents text selection in safari 
			// mousemove, check distance, start dragging
			case !drag.dragging && 'mousemove': // DRAGSTART >>	
				if ( Math.pow( event.pageX-event.data.x, 2 ) 
					+ Math.pow( event.pageY-event.data.y, 2 ) //  x² + y² = distance²
					< event.data.dist2 ) break; // distance tolerance not reached
				drag.dragging = event.dragTarget; // activate element
				event.type = "dragstart"; // hijack event
				returnValue = $.event.handle.call( drag.dragging, event ); // trigger "dragstart", return proxy element
				drag.proxy = $( returnValue )[0] || drag.dragging; // set proxy
				if ( returnValue !== false ) break; // "dragstart" accepted, stop
				selectable( drag.dragging, true ); // enable text selection
				drag.dragging = drag.proxy = null; // deactivate element
			// mousemove, dragging
			case 'mousemove': // DRAG >> 
				if ( drag.dragging ){
					event.type = "drag"; // hijack event
					returnValue = $.event.handle.call( drag.dragging, event ); // trigger "drag"
					if ( $.event.special.drop ){ // manage drop events
						$.event.special.drop.allowed = ( returnValue !== false ); // prevent drop
						$.event.special.drop.handler( event ); // "dropstart", "dropend"
						}
					if ( returnValue !== false ) break; // "drag" not rejected, stop		
					event.type = "mouseup"; // hijack event
					}
			// mouseup, stop dragging
			case 'mouseup': // DRAGEND >> 
				$.event.remove( document.body, "mousemove mouseup", drag.handler ); // remove page events
				if ( drag.dragging ){
					if ( $.event.special.drop ) // manage drop events
						$.event.special.drop.handler( event ); // "drop"
					event.type = "dragend"; // hijack event
					$.event.handle.call( drag.dragging, event ); // trigger "dragend"	
					selectable( drag.dragging, true ); // enable text selection
					drag.dragging = drag.proxy = null; // deactivate element
					event.data = {};
					}
				break;
			} 
		} 
	};
	
// toggles text selection attributes	
function selectable( elem, bool ){ 
	if ( !elem ) return; // maybe element was removed ? 
	elem.unselectable = bool ? "off" : "on"; // IE
	elem.onselectstart = function(){ return bool; }; // IE
	if ( elem.style ) elem.style.MozUserSelect = bool ? "" : "none"; // FF
	};	
	
/*******************************************************************************************/
})( jQuery ); // confine scope
