www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

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