/*! jquery.event.drag.js ~ v1.4 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com) Liscensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt */ ;(function($){ // secure $ jQuery alias /*******************************************************************************************/ // Created: 2008-06-04 | Updated: 2009-01-26 /*******************************************************************************************/ // 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('drag') // 0 args : this.bind('drag', fn2 ? fn2 : fn1 ); // 1+ args }; // local refs var $event = $.event, $special = $event.special, // special event configuration drag = $special.drag = { not: ':input', // don't begin to drag on event.targets that match this selector distance: 0, // distance dragged before dragstart which: 1, // mouse button pressed to start drag sequence setup: function( data ){ data = $.extend({ distance: drag.distance, which: drag.which, not: drag.not }, data || {}); data.distance = squared( data.distance ); // x² + y² = distance² $event.add( this, "mousedown", handler, data ); }, teardown: function(){ $event.remove( this, "mousedown", handler ); if ( this === drag.dragging ) drag.dragging = drag.proxy = null; // deactivate element selectable( this, true ); // enable text selection } }; // handle drag-releatd DOM events function handler ( event ){ var elem = this, returned, data = event.data || {}; // mousemove or mouseup if ( data.elem ){ // update event properties... elem = event.dragTarget = data.elem; // drag source element event.dragProxy = drag.proxy || elem; // proxy element or source event.cursorOffsetX = data.pageX - data.left; // mousedown offset event.cursorOffsetY = data.pageY - data.top; // mousedown offset event.offsetX = event.pageX - event.cursorOffsetX; // element offset event.offsetY = event.pageY - event.cursorOffsetY; // element offset } // mousedown, check some initial props to avoid the switch statement else if ( drag.dragging || ( data.which>0 && event.which!=data.which ) || $( event.target ).is( data.not ) ) return; // handle various events switch ( event.type ){ // mousedown, left click, event.target is not restricted, init dragging case 'mousedown': $.extend( data, $( elem ).offset(), { elem: elem, target: event.target, pageX: event.pageX, pageY: event.pageY }); // store some initial attributes $event.add( document, "mousemove mouseup", handler, data ); selectable( elem, false ); // disable text selection return false; // prevents text selection in safari // mousemove, check distance, start dragging case !drag.dragging && 'mousemove': if ( squared( event.pageX-data.pageX ) + squared( event.pageY-data.pageY ) // x² + y² = distance² < data.distance ) break; // distance tolerance not reached event.target = data.target; // force target from "mousedown" event (fix distance issue) returned = hijack( event, "dragstart", elem ); // trigger "dragstart", return proxy element if ( returned !== false ){ // "dragstart" not rejected drag.dragging = elem; // activate element drag.proxy = event.dragProxy = $( returned || elem )[0]; // set proxy } // mousemove, dragging case 'mousemove': if ( drag.dragging ){ returned = hijack( event, "drag", elem ); // trigger "drag" if ( $special.drop ){ // manage drop events $special.drop.allowed = ( returned !== false ); // prevent drop $special.drop.handler( event ); // "dropstart", "dropend" } if ( returned !== false ) break; // "drag" not rejected, stop event.type = "mouseup"; // helps "drop" handler behave } // mouseup, stop dragging case 'mouseup': $event.remove( document, "mousemove mouseup", handler ); // remove page events if ( drag.dragging ) { if ( $special.drop ) $special.drop.handler( event ); // "drop" hijack( event, "dragend", elem ); // trigger "dragend" } else { hijack( event, "dragclickonly", elem ); } selectable( elem, true ); // enable text selection drag.dragging = drag.proxy = data.elem = null; // deactivate element break; } }; // set event type to custom value, and handle it function hijack ( event, type, elem ){ event.type = type; // force the event type var result = $event.handle.call( elem, event ); return result===false ? false : result || event.result; }; // return the value squared function squared ( value ){ return Math.pow( value, 2 ); }; // 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 ( document.selection && document.selection.empty ) document.selection.empty(); // IE if ( elem.style ) elem.style.MozUserSelect = bool ? "" : "none"; // FF }; /*******************************************************************************************/ })( jQuery ); // confine scope