jquery.event.drop-1.2.js (7309B)
1 /*! jquery.event.drop.js * v1.2 2 Copyright (c) 2008-2009, Three Dub Media (http://threedubmedia.com) 3 Liscensed under the MIT License (http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt) 4 */;(function($){ // secure $ jQuery alias 5 // Created: 2008-06-04 | Updated: 2009-03-16 6 /*******************************************************************************************/ 7 // Events: drop, dropstart, dropend 8 /*******************************************************************************************/ 9 10 // JQUERY METHOD 11 $.fn.drop = function( fn1, fn2, fn3 ){ 12 if ( fn2 ) this.bind('dropstart', fn1 ); // 2+ args 13 if ( fn3 ) this.bind('dropend', fn3 ); // 3 args 14 return !fn1 ? this.trigger('drop') // 0 args 15 : this.bind('drop', fn2 ? fn2 : fn1 ); // 1+ args 16 }; 17 18 // DROP MANAGEMENT UTILITY 19 $.dropManage = function( opts ){ // return filtered drop target elements, cache their positions 20 opts = opts || {}; 21 // safely set new options... 22 drop.data = []; 23 drop.filter = opts.filter || '*'; 24 drop.delay = opts.delay || drop.delay; 25 drop.tolerance = opts.tolerance || null; 26 drop.mode = opts.mode || drop.mode || 'intersect'; 27 // return the filtered set of drop targets 28 return drop.$targets.filter( drop.filter ).each(function(){ 29 // locate and store the filtered drop targets 30 drop.data[ drop.data.length ] = drop.locate( this ); 31 }); 32 }; 33 34 // local refs 35 var $event = $.event, $special = $event.special, 36 37 // SPECIAL EVENT CONFIGURATION 38 drop = $special.drop = { 39 delay: 100, // default frequency to track drop targets 40 mode: 'intersect', // default mode to determine valid drop targets 41 $targets: $([]), data: [], // storage of drop targets and locations 42 setup: function(){ 43 drop.$targets = drop.$targets.add( this ); 44 drop.data[ drop.data.length ] = drop.locate( this ); 45 }, 46 teardown: function(){ var elem = this; 47 drop.$targets = drop.$targets.not( this ); 48 drop.data = $.grep( drop.data, function( obj ){ 49 return ( obj.elem !== elem ); 50 }); 51 }, 52 // shared handler 53 handler: function( event ){ 54 var dropstart = null, dropped; 55 event.dropTarget = drop.dropping || undefined; // dropped element 56 if ( drop.data.length && event.dragTarget ){ 57 // handle various events 58 switch ( event.type ){ 59 // drag/mousemove, from $.event.special.drag 60 case 'drag': // TOLERATE >> 61 drop.event = event; // store the mousemove event 62 if ( !drop.timer ) // monitor drop targets 63 drop.timer = setTimeout( tolerate, 20 ); 64 break; 65 // dragstop/mouseup, from $.event.special.drag 66 case 'mouseup': // DROP >> DROPEND >> 67 drop.timer = clearTimeout( drop.timer ); // delete timer 68 if ( !drop.dropping ) break; // stop, no drop 69 if ( drop.allowed ) 70 dropped = hijack( event, "drop", drop.dropping ); // trigger "drop" 71 dropstart = false; 72 // activate new target, from tolerate (async) 73 case drop.dropping && 'dropstart': // DROPSTART >> ( new target ) 74 dropstart = dropstart===null && drop.allowed ? true : false; 75 // deactivate active target, from tolerate (async) 76 case drop.dropping && 'dropend': // DROPEND >> 77 hijack( event, "dropend", drop.dropping ); // trigger "dropend" 78 drop.dropping = null; // empty dropper 79 if ( dropped === false ) event.dropTarget = undefined; 80 if ( !dropstart ) break; // stop 81 // activate target, from tolerate (async) 82 case drop.allowed && 'dropstart': // DROPSTART >> 83 event.dropTarget = this; 84 drop.dropping = hijack( event, "dropstart", this )!==false ? this : null; // trigger "dropstart" 85 break; 86 } 87 } 88 }, 89 // returns the location positions of an element 90 locate: function( elem ){ // return { L:left, R:right, T:top, B:bottom, H:height, W:width } 91 var $el = $(elem), pos = $el.offset(), h = $el.outerHeight(), w = $el.outerWidth(); 92 return { elem: elem, L: pos.left, R: pos.left+w, T: pos.top, B: pos.top+h, W: w, H: h }; 93 }, 94 // test the location positions of an element against another OR an X,Y coord 95 contains: function( target, test ){ // target { L,R,T,B,H,W } contains test [x,y] or { L,R,T,B,H,W } 96 return ( ( test[0] || test.L ) >= target.L && ( test[0] || test.R ) <= target.R 97 && ( test[1] || test.T ) >= target.T && ( test[1] || test.B ) <= target.B ); 98 }, 99 // stored tolerance modes 100 modes: { // fn scope: "$.event.special.drop" object 101 // target with mouse wins, else target with most overlap wins 102 'intersect': function( event, proxy, target ){ 103 return this.contains( target, [ event.pageX, event.pageY ] ) ? // check cursor 104 target : this.modes['overlap'].apply( this, arguments ); // check overlap 105 }, 106 // target with most overlap wins 107 'overlap': function( event, proxy, target ){ 108 // calculate the area of overlap... 109 target.overlap = Math.max( 0, Math.min( target.B, proxy.B ) - Math.max( target.T, proxy.T ) ) 110 * Math.max( 0, Math.min( target.R, proxy.R ) - Math.max( target.L, proxy.L ) ); 111 if ( target.overlap > ( ( this.best || {} ).overlap || 0 ) ) // compare overlap 112 this.best = target; // set as the best match so far 113 return null; // no winner 114 }, 115 // proxy is completely contained within target bounds 116 'fit': function( event, proxy, target ){ 117 return this.contains( target, proxy ) ? target : null; 118 }, 119 // center of the proxy is contained within target bounds 120 'middle': function( event, proxy, target ){ 121 return this.contains( target, [ proxy.L+proxy.W/2, proxy.T+proxy.H/2 ] ) ? target : null; 122 } 123 } 124 }; 125 126 // set event type to custom value, and handle it 127 function hijack ( event, type, elem ){ 128 event.type = type; // force the event type 129 //try { 130 var result = $event.handle.call( elem, event ); 131 // } catch ( ex ){ /* catch IE error with async event handling */ } 132 return result===false ? false : result || event.result; 133 }; 134 135 // async, recursive tolerance execution 136 function tolerate (){ 137 var i = 0, drp, winner, // local variables 138 xy = [ drop.event.pageX, drop.event.pageY ], // mouse location 139 drg = drop.locate( drop.event.dragProxy ); // drag proxy location 140 drop.tolerance = drop.tolerance || drop.modes[ drop.mode ]; // custom or stored tolerance fn 141 do if ( drp = drop.data[i] ){ // each drop target location 142 // tolerance function is defined, or mouse contained 143 winner = drop.tolerance ? drop.tolerance.call( drop, drop.event, drg, drp ) 144 : drop.contains( drp, xy ) ? drp : null; // mouse is always fallback 145 } 146 while ( ++i<drop.data.length && !winner ); // loop 147 drop.event.type = ( winner = winner || drop.best ) ? 'dropstart' : 'dropend'; // start ? stop 148 if ( drop.event.type=='dropend' || winner.elem!=drop.dropping ) // don't dropstart on active drop target 149 drop.handler.call( winner ? winner.elem : drop.dropping, drop.event ); // handle events 150 if ( drop.last && xy[0] == drop.last.pageX && xy[1] == drop.last.pageY ) // no movement 151 delete drop.timer; // idle, don't recurse 152 else drop.timer = setTimeout( tolerate, drop.delay ); // recurse 153 drop.last = drop.event; // to compare idleness 154 drop.best = null; // reset comparitors 155 }; 156 157 /*******************************************************************************************/ 158 })(jQuery); // confine scope