2008-06-04 02:34:33 +00:00
/ *
* jQuery UI Droppable
*
* Copyright ( c ) 2008 Paul Bakaus
* Dual licensed under the MIT ( MIT - LICENSE . txt )
* and GPL ( GPL - LICENSE . txt ) licenses .
*
* http : //docs.jquery.com/UI/Droppables
*
* Depends :
* ui . core . js
* ui . draggable . js
* /
2008-06-05 12:14:12 +00:00
( function ( $ ) {
2008-06-04 02:34:33 +00:00
$ . widget ( "ui.droppable" , {
init : function ( ) {
2008-07-14 15:55:33 +00:00
var o = this . options , accept = o . accept ;
2008-06-04 02:34:33 +00:00
this . isover = 0 ; this . isout = 1 ;
2008-07-14 14:21:07 +00:00
this . options . accept = this . options . accept && this . options . accept . constructor == Function ? this . options . accept : function ( d ) {
return d . is ( accept ) ;
} ;
2008-06-04 02:34:33 +00:00
//Store the droppable's proportions
2008-07-02 17:38:34 +00:00
this . proportions = { width : this . element [ 0 ] . offsetWidth , height : this . element [ 0 ] . offsetHeight } ;
2008-06-04 02:34:33 +00:00
// Add the reference and positions to the manager
2008-07-28 18:34:01 +00:00
$ . ui . ddmanager . droppables [ this . options . scope ] = $ . ui . ddmanager . droppables [ this . options . scope ] || [ ] ;
$ . ui . ddmanager . droppables [ this . options . scope ] . push ( this ) ;
2008-06-04 02:34:33 +00:00
2008-07-28 21:06:04 +00:00
( this . options . cssNamespace && this . element . addClass ( this . options . cssNamespace + "-droppable" ) ) ;
2008-06-04 02:34:33 +00:00
} ,
plugins : { } ,
ui : function ( c ) {
return {
draggable : ( c . currentItem || c . element ) ,
helper : c . helper ,
position : c . position ,
absolutePosition : c . positionAbs ,
options : this . options ,
element : this . element
} ;
} ,
destroy : function ( ) {
2008-07-28 18:34:01 +00:00
var drop = $ . ui . ddmanager . droppables [ this . options . scope ] ;
2008-06-04 02:34:33 +00:00
for ( var i = 0 ; i < drop . length ; i ++ )
if ( drop [ i ] == this )
drop . splice ( i , 1 ) ;
this . element
2008-07-14 14:21:07 +00:00
. removeClass ( "ui-droppable-disabled" )
2008-06-04 02:34:33 +00:00
. removeData ( "droppable" )
. unbind ( ".droppable" ) ;
} ,
over : function ( e ) {
var draggable = $ . ui . ddmanager . current ;
if ( ! draggable || ( draggable . currentItem || draggable . element ) [ 0 ] == this . element [ 0 ] ) return ; // Bail if draggable and droppable are same element
if ( this . options . accept . call ( this . element , ( draggable . currentItem || draggable . element ) ) ) {
$ . ui . plugin . call ( this , 'over' , [ e , this . ui ( draggable ) ] ) ;
this . element . triggerHandler ( "dropover" , [ e , this . ui ( draggable ) ] , this . options . over ) ;
}
} ,
out : function ( e ) {
var draggable = $ . ui . ddmanager . current ;
if ( ! draggable || ( draggable . currentItem || draggable . element ) [ 0 ] == this . element [ 0 ] ) return ; // Bail if draggable and droppable are same element
if ( this . options . accept . call ( this . element , ( draggable . currentItem || draggable . element ) ) ) {
$ . ui . plugin . call ( this , 'out' , [ e , this . ui ( draggable ) ] ) ;
this . element . triggerHandler ( "dropout" , [ e , this . ui ( draggable ) ] , this . options . out ) ;
}
} ,
drop : function ( e , custom ) {
var draggable = custom || $ . ui . ddmanager . current ;
if ( ! draggable || ( draggable . currentItem || draggable . element ) [ 0 ] == this . element [ 0 ] ) return false ; // Bail if draggable and droppable are same element
var childrenIntersection = false ;
2008-07-14 14:21:07 +00:00
this . element . find ( ":data(droppable)" ) . not ( ".ui-draggable-dragging" ) . each ( function ( ) {
2008-06-04 02:34:33 +00:00
var inst = $ . data ( this , 'droppable' ) ;
if ( inst . options . greedy && $ . ui . intersect ( draggable , $ . extend ( inst , { offset : inst . element . offset ( ) } ) , inst . options . tolerance ) ) {
childrenIntersection = true ; return false ;
}
} ) ;
if ( childrenIntersection ) return false ;
if ( this . options . accept . call ( this . element , ( draggable . currentItem || draggable . element ) ) ) {
$ . ui . plugin . call ( this , 'drop' , [ e , this . ui ( draggable ) ] ) ;
this . element . triggerHandler ( "drop" , [ e , this . ui ( draggable ) ] , this . options . drop ) ;
return true ;
}
return false ;
} ,
activate : function ( e ) {
var draggable = $ . ui . ddmanager . current ;
$ . ui . plugin . call ( this , 'activate' , [ e , this . ui ( draggable ) ] ) ;
if ( draggable ) this . element . triggerHandler ( "dropactivate" , [ e , this . ui ( draggable ) ] , this . options . activate ) ;
} ,
deactivate : function ( e ) {
var draggable = $ . ui . ddmanager . current ;
$ . ui . plugin . call ( this , 'deactivate' , [ e , this . ui ( draggable ) ] ) ;
if ( draggable ) this . element . triggerHandler ( "dropdeactivate" , [ e , this . ui ( draggable ) ] , this . options . deactivate ) ;
}
} ) ;
$ . extend ( $ . ui . droppable , {
defaults : {
disabled : false ,
2008-07-28 18:34:01 +00:00
tolerance : 'intersect' ,
2008-07-28 21:06:04 +00:00
scope : 'default' ,
cssNamespace : 'ui'
2008-06-04 02:34:33 +00:00
}
} ) ;
$ . ui . intersect = function ( draggable , droppable , toleranceMode ) {
if ( ! droppable . offset ) return false ;
var x1 = ( draggable . positionAbs || draggable . position . absolute ) . left , x2 = x1 + draggable . helperProportions . width ,
y1 = ( draggable . positionAbs || draggable . position . absolute ) . top , y2 = y1 + draggable . helperProportions . height ;
var l = droppable . offset . left , r = l + droppable . proportions . width ,
t = droppable . offset . top , b = t + droppable . proportions . height ;
switch ( toleranceMode ) {
case 'fit' :
return ( l < x1 && x2 < r
&& t < y1 && y2 < b ) ;
break ;
case 'intersect' :
return ( l < x1 + ( draggable . helperProportions . width / 2 ) // Right Half
&& x2 - ( draggable . helperProportions . width / 2 ) < r // Left Half
&& t < y1 + ( draggable . helperProportions . height / 2 ) // Bottom Half
&& y2 - ( draggable . helperProportions . height / 2 ) < b ) ; // Top Half
break ;
case 'pointer' :
return ( l < ( ( draggable . positionAbs || draggable . position . absolute ) . left + ( draggable . clickOffset || draggable . offset . click ) . left ) && ( ( draggable . positionAbs || draggable . position . absolute ) . left + ( draggable . clickOffset || draggable . offset . click ) . left ) < r
&& t < ( ( draggable . positionAbs || draggable . position . absolute ) . top + ( draggable . clickOffset || draggable . offset . click ) . top ) && ( ( draggable . positionAbs || draggable . position . absolute ) . top + ( draggable . clickOffset || draggable . offset . click ) . top ) < b ) ;
break ;
case 'touch' :
return (
( y1 >= t && y1 <= b ) || // Top edge touching
( y2 >= t && y2 <= b ) || // Bottom edge touching
( y1 < t && y2 > b ) // Surrounded vertically
) && (
( x1 >= l && x1 <= r ) || // Left edge touching
( x2 >= l && x2 <= r ) || // Right edge touching
( x1 < l && x2 > r ) // Surrounded horizontally
) ;
break ;
default :
return false ;
break ;
}
} ;
/ *
This manager tracks offsets of draggables and droppables
* /
$ . ui . ddmanager = {
current : null ,
2008-07-28 21:06:04 +00:00
droppables : { 'default' : [ ] } ,
2008-06-04 02:34:33 +00:00
prepareOffsets : function ( t , e ) {
2008-07-28 18:34:01 +00:00
var m = $ . ui . ddmanager . droppables [ t . options . scope ] ;
2008-06-04 02:34:33 +00:00
var type = e ? e . type : null ; // workaround for #2317
2008-07-28 18:34:01 +00:00
var list = ( t . currentItem || t . element ) . find ( ":data(droppable)" ) . andSelf ( ) ;
2008-07-03 13:05:30 +00:00
2008-07-28 18:34:01 +00:00
droppablesLoop : for ( var i = 0 ; i < m . length ; i ++ ) {
if ( m [ i ] . options . disabled || ( t && ! m [ i ] . options . accept . call ( m [ i ] . element , ( t . currentItem || t . element ) ) ) ) continue ; //No disabled and non-accepted
for ( var j = 0 ; j < list . length ; j ++ ) { if ( list [ j ] == m [ i ] . element [ 0 ] ) { m [ i ] . proportions . height = 0 ; continue droppablesLoop ; } } ; //Filter out elements in the current dragged item
m [ i ] . visible = m [ i ] . element . css ( "display" ) != "none" ; if ( ! m [ i ] . visible ) continue ; //If the element is not visible, continue
2008-06-04 02:34:33 +00:00
m [ i ] . offset = m [ i ] . element . offset ( ) ;
2008-07-02 17:38:34 +00:00
m [ i ] . proportions = { width : m [ i ] . element [ 0 ] . offsetWidth , height : m [ i ] . element [ 0 ] . offsetHeight } ;
2008-06-04 02:34:33 +00:00
2008-07-28 18:34:01 +00:00
if ( type == "dragstart" || type == "sortactivate" ) m [ i ] . activate . call ( m [ i ] , e ) ; //Activate the droppable if used directly from draggables
2008-06-04 02:34:33 +00:00
}
} ,
drop : function ( draggable , e ) {
var dropped = false ;
2008-07-28 18:34:01 +00:00
$ . each ( $ . ui . ddmanager . droppables [ draggable . options . scope ] , function ( ) {
2008-06-04 02:34:33 +00:00
if ( ! this . options ) return ;
if ( ! this . options . disabled && this . visible && $ . ui . intersect ( draggable , this , this . options . tolerance ) )
dropped = this . drop . call ( this , e ) ;
if ( ! this . options . disabled && this . visible && this . options . accept . call ( this . element , ( draggable . currentItem || draggable . element ) ) ) {
this . isout = 1 ; this . isover = 0 ;
this . deactivate . call ( this , e ) ;
}
} ) ;
return dropped ;
} ,
drag : function ( draggable , e ) {
//If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
if ( draggable . options . refreshPositions ) $ . ui . ddmanager . prepareOffsets ( draggable , e ) ;
//Run through all droppables and check their positions based on specific tolerance options
2008-07-03 13:05:30 +00:00
2008-07-28 18:34:01 +00:00
$ . each ( $ . ui . ddmanager . droppables [ draggable . options . scope ] , function ( ) {
2008-06-04 02:34:33 +00:00
2008-06-26 10:49:47 +00:00
if ( this . options . disabled || this . greedyChild || ! this . visible ) return ;
2008-06-04 02:34:33 +00:00
var intersects = $ . ui . intersect ( draggable , this , this . options . tolerance ) ;
var c = ! intersects && this . isover == 1 ? 'isout' : ( intersects && this . isover == 0 ? 'isover' : null ) ;
if ( ! c ) return ;
var parentInstance ;
if ( this . options . greedy ) {
2008-07-14 14:21:07 +00:00
var parent = this . element . parents ( ':data(droppable):eq(0)' ) ;
2008-06-04 02:34:33 +00:00
if ( parent . length ) {
parentInstance = $ . data ( parent [ 0 ] , 'droppable' ) ;
parentInstance . greedyChild = ( c == 'isover' ? 1 : 0 ) ;
}
}
// we just moved into a greedy child
if ( parentInstance && c == 'isover' ) {
parentInstance [ 'isover' ] = 0 ;
parentInstance [ 'isout' ] = 1 ;
parentInstance . out . call ( parentInstance , e ) ;
}
this [ c ] = 1 ; this [ c == 'isout' ? 'isover' : 'isout' ] = 0 ;
this [ c == "isover" ? "over" : "out" ] . call ( this , e ) ;
// we just moved out of a greedy child
if ( parentInstance && c == 'isout' ) {
parentInstance [ 'isout' ] = 0 ;
parentInstance [ 'isover' ] = 1 ;
parentInstance . over . call ( parentInstance , e ) ;
}
} ) ;
}
} ;
/ *
* Droppable Extensions
* /
$ . ui . plugin . add ( "droppable" , "activeClass" , {
activate : function ( e , ui ) {
$ ( this ) . addClass ( ui . options . activeClass ) ;
} ,
deactivate : function ( e , ui ) {
$ ( this ) . removeClass ( ui . options . activeClass ) ;
} ,
drop : function ( e , ui ) {
$ ( this ) . removeClass ( ui . options . activeClass ) ;
}
} ) ;
$ . ui . plugin . add ( "droppable" , "hoverClass" , {
over : function ( e , ui ) {
$ ( this ) . addClass ( ui . options . hoverClass ) ;
} ,
out : function ( e , ui ) {
$ ( this ) . removeClass ( ui . options . hoverClass ) ;
} ,
drop : function ( e , ui ) {
$ ( this ) . removeClass ( ui . options . hoverClass ) ;
}
} ) ;
} ) ( jQuery ) ;