forgotten additions to repo
[myslice.git] / third-party / slickgrid-2.1 / lib / jquery.event.drop-2.2.js
1 /*! 
2  * jquery.event.drop - v 2.2
3  * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com
4  * Open Source MIT License - http://threedubmedia.com/code/license
5  */
6 // Created: 2008-06-04 
7 // Updated: 2012-05-21
8 // REQUIRES: jquery 1.7.x, event.drag 2.2
9
10 ;(function($){ // secure $ jQuery alias
11
12 // Events: drop, dropstart, dropend
13
14 // add the jquery instance method
15 $.fn.drop = function( str, arg, opts ){
16         // figure out the event type
17         var type = typeof str == "string" ? str : "",
18         // figure out the event handler...
19         fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null;
20         // fix the event type
21         if ( type.indexOf("drop") !== 0 ) 
22                 type = "drop"+ type;
23         // were options passed
24         opts = ( str == fn ? arg : opts ) || {};
25         // trigger or bind event handler
26         return fn ? this.bind( type, opts, fn ) : this.trigger( type );
27 };
28
29 // DROP MANAGEMENT UTILITY
30 // returns filtered drop target elements, caches their positions
31 $.drop = function( opts ){ 
32         opts = opts || {};
33         // safely set new options...
34         drop.multi = opts.multi === true ? Infinity : 
35                 opts.multi === false ? 1 : !isNaN( opts.multi ) ? opts.multi : drop.multi;
36         drop.delay = opts.delay || drop.delay;
37         drop.tolerance = $.isFunction( opts.tolerance ) ? opts.tolerance : 
38                 opts.tolerance === null ? null : drop.tolerance;
39         drop.mode = opts.mode || drop.mode || 'intersect';
40 };
41
42 // local refs (increase compression)
43 var $event = $.event, 
44 $special = $event.special,
45 // configure the drop special event
46 drop = $.event.special.drop = {
47
48         // these are the default settings
49         multi: 1, // allow multiple drop winners per dragged element
50         delay: 20, // async timeout delay
51         mode: 'overlap', // drop tolerance mode
52                 
53         // internal cache
54         targets: [], 
55         
56         // the key name for stored drop data
57         datakey: "dropdata",
58                 
59         // prevent bubbling for better performance
60         noBubble: true,
61         
62         // count bound related events
63         add: function( obj ){ 
64                 // read the interaction data
65                 var data = $.data( this, drop.datakey );
66                 // count another realted event
67                 data.related += 1;
68         },
69         
70         // forget unbound related events
71         remove: function(){
72                 $.data( this, drop.datakey ).related -= 1;
73         },
74         
75         // configure the interactions
76         setup: function(){
77                 // check for related events
78                 if ( $.data( this, drop.datakey ) ) 
79                         return;
80                 // initialize the drop element data
81                 var data = { 
82                         related: 0,
83                         active: [],
84                         anyactive: 0,
85                         winner: 0,
86                         location: {}
87                 };
88                 // store the drop data on the element
89                 $.data( this, drop.datakey, data );
90                 // store the drop target in internal cache
91                 drop.targets.push( this );
92         },
93         
94         // destroy the configure interaction    
95         teardown: function(){ 
96                 var data = $.data( this, drop.datakey ) || {};
97                 // check for related events
98                 if ( data.related ) 
99                         return;
100                 // remove the stored data
101                 $.removeData( this, drop.datakey );
102                 // reference the targeted element
103                 var element = this;
104                 // remove from the internal cache
105                 drop.targets = $.grep( drop.targets, function( target ){ 
106                         return ( target !== element ); 
107                 });
108         },
109         
110         // shared event handler
111         handler: function( event, dd ){ 
112                 // local vars
113                 var results, $targets;
114                 // make sure the right data is available
115                 if ( !dd ) 
116                         return;
117                 // handle various events
118                 switch ( event.type ){
119                         // draginit, from $.event.special.drag
120                         case 'mousedown': // DROPINIT >>
121                         case 'touchstart': // DROPINIT >>
122                                 // collect and assign the drop targets
123                                 $targets =  $( drop.targets );
124                                 if ( typeof dd.drop == "string" )
125                                         $targets = $targets.filter( dd.drop );
126                                 // reset drop data winner properties
127                                 $targets.each(function(){
128                                         var data = $.data( this, drop.datakey );
129                                         data.active = [];
130                                         data.anyactive = 0;
131                                         data.winner = 0;
132                                 });
133                                 // set available target elements
134                                 dd.droppable = $targets;
135                                 // activate drop targets for the initial element being dragged
136                                 $special.drag.hijack( event, "dropinit", dd ); 
137                                 break;
138                         // drag, from $.event.special.drag
139                         case 'mousemove': // TOLERATE >>
140                         case 'touchmove': // TOLERATE >>
141                                 drop.event = event; // store the mousemove event
142                                 if ( !drop.timer )
143                                         // monitor drop targets
144                                         drop.tolerate( dd ); 
145                                 break;
146                         // dragend, from $.event.special.drag
147                         case 'mouseup': // DROP >> DROPEND >>
148                         case 'touchend': // DROP >> DROPEND >>
149                                 drop.timer = clearTimeout( drop.timer ); // delete timer        
150                                 if ( dd.propagates ){
151                                         $special.drag.hijack( event, "drop", dd ); 
152                                         $special.drag.hijack( event, "dropend", dd ); 
153                                 }
154                                 break;
155                                 
156                 }
157         },
158                 
159         // returns the location positions of an element
160         locate: function( elem, index ){ 
161                 var data = $.data( elem, drop.datakey ),
162                 $elem = $( elem ), 
163                 posi = $elem.offset() || {}, 
164                 height = $elem.outerHeight(), 
165                 width = $elem.outerWidth(),
166                 location = { 
167                         elem: elem, 
168                         width: width, 
169                         height: height,
170                         top: posi.top, 
171                         left: posi.left, 
172                         right: posi.left + width, 
173                         bottom: posi.top + height
174                 };
175                 // drag elements might not have dropdata
176                 if ( data ){
177                         data.location = location;
178                         data.index = index;
179                         data.elem = elem;
180                 }
181                 return location;
182         },
183         
184         // test the location positions of an element against another OR an X,Y coord
185         contains: function( target, test ){ // target { location } contains test [x,y] or { location }
186                 return ( ( test[0] || test.left ) >= target.left && ( test[0] || test.right ) <= target.right
187                         && ( test[1] || test.top ) >= target.top && ( test[1] || test.bottom ) <= target.bottom ); 
188         },
189         
190         // stored tolerance modes
191         modes: { // fn scope: "$.event.special.drop" object 
192                 // target with mouse wins, else target with most overlap wins
193                 'intersect': function( event, proxy, target ){
194                         return this.contains( target, [ event.pageX, event.pageY ] ) ? // check cursor
195                                 1e9 : this.modes.overlap.apply( this, arguments ); // check overlap
196                 },
197                 // target with most overlap wins        
198                 'overlap': function( event, proxy, target ){
199                         // calculate the area of overlap...
200                         return Math.max( 0, Math.min( target.bottom, proxy.bottom ) - Math.max( target.top, proxy.top ) )
201                                 * Math.max( 0, Math.min( target.right, proxy.right ) - Math.max( target.left, proxy.left ) );
202                 },
203                 // proxy is completely contained within target bounds   
204                 'fit': function( event, proxy, target ){
205                         return this.contains( target, proxy ) ? 1 : 0;
206                 },
207                 // center of the proxy is contained within target bounds        
208                 'middle': function( event, proxy, target ){
209                         return this.contains( target, [ proxy.left + proxy.width * .5, proxy.top + proxy.height * .5 ] ) ? 1 : 0;
210                 }
211         },      
212         
213         // sort drop target cache by by winner (dsc), then index (asc)
214         sort: function( a, b ){
215                 return ( b.winner - a.winner ) || ( a.index - b.index );
216         },
217                 
218         // async, recursive tolerance execution
219         tolerate: function( dd ){               
220                 // declare local refs
221                 var i, drp, drg, data, arr, len, elem,
222                 // interaction iteration variables
223                 x = 0, ia, end = dd.interactions.length,
224                 // determine the mouse coords
225                 xy = [ drop.event.pageX, drop.event.pageY ],
226                 // custom or stored tolerance fn
227                 tolerance = drop.tolerance || drop.modes[ drop.mode ];
228                 // go through each passed interaction...
229                 do if ( ia = dd.interactions[x] ){
230                         // check valid interaction
231                         if ( !ia )
232                                 return; 
233                         // initialize or clear the drop data
234                         ia.drop = [];
235                         // holds the drop elements
236                         arr = []; 
237                         len = ia.droppable.length;
238                         // determine the proxy location, if needed
239                         if ( tolerance )
240                                 drg = drop.locate( ia.proxy ); 
241                         // reset the loop
242                         i = 0;
243                         // loop each stored drop target
244                         do if ( elem = ia.droppable[i] ){ 
245                                 data = $.data( elem, drop.datakey );
246                                 drp = data.location;
247                                 if ( !drp ) continue;
248                                 // find a winner: tolerance function is defined, call it
249                                 data.winner = tolerance ? tolerance.call( drop, drop.event, drg, drp ) 
250                                         // mouse position is always the fallback
251                                         : drop.contains( drp, xy ) ? 1 : 0; 
252                                 arr.push( data );       
253                         } while ( ++i < len ); // loop 
254                         // sort the drop targets
255                         arr.sort( drop.sort );                  
256                         // reset the loop
257                         i = 0;
258                         // loop through all of the targets again
259                         do if ( data = arr[ i ] ){
260                                 // winners...
261                                 if ( data.winner && ia.drop.length < drop.multi ){
262                                         // new winner... dropstart
263                                         if ( !data.active[x] && !data.anyactive ){
264                                                 // check to make sure that this is not prevented
265                                                 if ( $special.drag.hijack( drop.event, "dropstart", dd, x, data.elem )[0] !== false ){  
266                                                         data.active[x] = 1;
267                                                         data.anyactive += 1;
268                                                 }
269                                                 // if false, it is not a winner
270                                                 else
271                                                         data.winner = 0;
272                                         }
273                                         // if it is still a winner
274                                         if ( data.winner )
275                                                 ia.drop.push( data.elem );
276                                 }
277                                 // losers... 
278                                 else if ( data.active[x] && data.anyactive == 1 ){
279                                         // former winner... dropend
280                                         $special.drag.hijack( drop.event, "dropend", dd, x, data.elem ); 
281                                         data.active[x] = 0;
282                                         data.anyactive -= 1;
283                                 }
284                         } while ( ++i < len ); // loop          
285                 } while ( ++x < end ) // loop
286                 // check if the mouse is still moving or is idle
287                 if ( drop.last && xy[0] == drop.last.pageX && xy[1] == drop.last.pageY ) 
288                         delete drop.timer; // idle, don't recurse
289                 else  // recurse
290                         drop.timer = setTimeout(function(){ 
291                                 drop.tolerate( dd ); 
292                         }, drop.delay );
293                 // remember event, to compare idleness
294                 drop.last = drop.event; 
295         }
296         
297 };
298
299 // share the same special event configuration with related events...
300 $special.dropinit = $special.dropstart = $special.dropend = drop;
301
302 })(jQuery); // confine scope