2008-06-04 02:34:33 +00:00
/ *
* jQuery UI Sortable
*
* Copyright ( c ) 2008 Paul Bakaus
* Dual licensed under the MIT ( MIT - LICENSE . txt )
* and GPL ( GPL - LICENSE . txt ) licenses .
*
* http : //docs.jquery.com/UI/Sortables
*
* Depends :
* ui . core . js
* /
2008-06-05 12:14:12 +00:00
( function ( $ ) {
2008-06-04 02:34:33 +00:00
function contains ( a , b ) {
var safari2 = $ . browser . safari && $ . browser . version < 522 ;
if ( a . contains && ! safari2 ) {
return a . contains ( b ) ;
}
if ( a . compareDocumentPosition )
return ! ! ( a . compareDocumentPosition ( b ) & 16 ) ;
while ( b = b . parentNode )
if ( b == a ) return true ;
return false ;
} ;
2008-07-07 07:39:12 +00:00
$ . widget ( "ui.sortable" , $ . extend ( { } , $ . ui . mouse , {
2008-08-17 02:15:55 +00:00
_init : function ( ) {
2008-06-04 02:34:33 +00:00
var o = this . options ;
this . containerCache = { } ;
this . element . addClass ( "ui-sortable" ) ;
//Get the items
this . refresh ( ) ;
//Let's determine if the items are floating
this . floating = this . items . length ? ( /left|right/ ) . test ( this . items [ 0 ] . item . css ( 'float' ) ) : false ;
//Let's determine the parent's offset
this . offset = this . element . offset ( ) ;
//Initialize mouse events for interaction
2008-08-17 08:15:49 +00:00
this . _mouseInit ( ) ;
2008-06-04 02:34:33 +00:00
} ,
plugins : { } ,
ui : function ( inst ) {
return {
helper : ( inst || this ) [ "helper" ] ,
placeholder : ( inst || this ) [ "placeholder" ] || $ ( [ ] ) ,
position : ( inst || this ) [ "position" ] ,
absolutePosition : ( inst || this ) [ "positionAbs" ] ,
options : this . options ,
element : this . element ,
item : ( inst || this ) [ "currentItem" ] ,
sender : inst ? inst . element : null
} ;
} ,
2008-07-19 15:05:37 +00:00
2008-08-17 02:15:55 +00:00
_propagate : function ( n , e , inst , noPropagation ) {
2008-06-04 02:34:33 +00:00
$ . ui . plugin . call ( this , n , [ e , this . ui ( inst ) ] ) ;
if ( ! noPropagation ) this . element . triggerHandler ( n == "sort" ? n : "sort" + n , [ e , this . ui ( inst ) ] , this . options [ n ] ) ;
} ,
2008-07-19 15:05:37 +00:00
2008-06-04 02:34:33 +00:00
serialize : function ( o ) {
2008-06-08 11:00:47 +00:00
2008-08-17 02:19:14 +00:00
var items = this . _getItemsAsjQuery ( o && o . connected ) ;
2008-06-04 02:34:33 +00:00
var str = [ ] ; o = o || { } ;
2008-07-19 15:05:37 +00:00
$ ( items ) . each ( function ( ) {
var res = ( $ ( this . item || this ) . attr ( o . attribute || 'id' ) || '' ) . match ( o . expression || ( /(.+)[-=_](.+)/ ) ) ;
2008-06-26 10:00:32 +00:00
if ( res ) str . push ( ( o . key || res [ 1 ] ) + '[]=' + ( o . key && o . expression ? res [ 1 ] : res [ 2 ] ) ) ;
2008-06-04 02:34:33 +00:00
} ) ;
return str . join ( '&' ) ;
} ,
2008-07-19 15:05:37 +00:00
2008-06-04 02:34:33 +00:00
toArray : function ( attr ) {
2008-06-08 11:00:47 +00:00
2008-08-17 02:19:14 +00:00
var items = this . _getItemsAsjQuery ( o && o . connected ) ;
2008-06-04 02:34:33 +00:00
var ret = [ ] ;
items . each ( function ( ) { ret . push ( $ ( this ) . attr ( attr || 'id' ) ) ; } ) ;
return ret ;
2008-06-08 11:00:47 +00:00
2008-06-04 02:34:33 +00:00
} ,
2008-07-19 15:05:37 +00:00
2008-06-04 02:34:33 +00:00
/* Be careful with the following core functions */
2008-08-17 02:19:14 +00:00
_intersectsWith : function ( item ) {
2008-06-04 02:34:33 +00:00
var x1 = this . positionAbs . left , x2 = x1 + this . helperProportions . width ,
y1 = this . positionAbs . top , y2 = y1 + this . helperProportions . height ;
var l = item . left , r = l + item . width ,
t = item . top , b = t + item . height ;
2008-07-10 00:56:29 +00:00
var dyClick = this . offset . click . top , dxClick = this . offset . click . left ;
var isOverElement = ( y1 + dyClick ) > t && ( y1 + dyClick ) < b && ( x1 + dxClick ) > l && ( x1 + dxClick ) < r ;
if ( this . options . tolerance == "pointer" || this . options . forcePointerForContainers || ( this . options . tolerance == "guess" && this . helperProportions [ this . floating ? 'width' : 'height' ] > item [ this . floating ? 'width' : 'height' ] ) ) {
return isOverElement ;
2008-06-04 02:34:33 +00:00
} else {
2008-07-10 00:56:29 +00:00
2008-06-04 02:34:33 +00:00
return ( l < x1 + ( this . helperProportions . width / 2 ) // Right Half
&& x2 - ( this . helperProportions . width / 2 ) < r // Left Half
&& t < y1 + ( this . helperProportions . height / 2 ) // Bottom Half
&& y2 - ( this . helperProportions . height / 2 ) < b ) ; // Top Half
2008-07-10 00:56:29 +00:00
}
2008-06-04 02:34:33 +00:00
} ,
2008-07-19 15:05:37 +00:00
2008-08-17 02:19:14 +00:00
_intersectsWithEdge : function ( item ) {
2008-06-04 02:34:33 +00:00
var x1 = this . positionAbs . left , x2 = x1 + this . helperProportions . width ,
y1 = this . positionAbs . top , y2 = y1 + this . helperProportions . height ;
2008-07-10 00:56:29 +00:00
2008-06-04 02:34:33 +00:00
var l = item . left , r = l + item . width ,
t = item . top , b = t + item . height ;
2008-07-10 00:56:29 +00:00
var dyClick = this . offset . click . top , dxClick = this . offset . click . left ;
var isOverElement = ( y1 + dyClick ) > t && ( y1 + dyClick ) < b && ( x1 + dxClick ) > l && ( x1 + dxClick ) < r ;
2008-06-05 06:35:16 +00:00
if ( this . options . tolerance == "pointer" || ( this . options . tolerance == "guess" && this . helperProportions [ this . floating ? 'width' : 'height' ] > item [ this . floating ? 'width' : 'height' ] ) ) {
2008-07-10 00:56:29 +00:00
if ( ! isOverElement ) return false ;
2008-06-04 02:34:33 +00:00
if ( this . floating ) {
2008-07-10 00:56:29 +00:00
if ( ( x1 + dxClick ) > l && ( x1 + dxClick ) < l + item . width / 2 ) return 2 ;
if ( ( x1 + dxClick ) > l + item . width / 2 && ( x1 + dxClick ) < r ) return 1 ;
2008-06-04 02:34:33 +00:00
} else {
2008-07-28 18:34:01 +00:00
var height = item . height ;
2008-07-10 17:09:41 +00:00
var direction = y1 - this . updateOriginalPosition . top < 0 ? 2 : 1 ; // 2 = up
2008-07-10 00:56:29 +00:00
if ( direction == 1 && ( y1 + dyClick ) < t + height / 2 ) { return 2 ; } // up
else if ( direction == 2 && ( y1 + dyClick ) > t + height / 2 ) { return 1 ; } // down
2008-06-04 02:34:33 +00:00
}
2008-07-10 00:56:29 +00:00
2008-06-04 02:34:33 +00:00
} else {
if ( ! ( l < x1 + ( this . helperProportions . width / 2 ) // Right Half
&& x2 - ( this . helperProportions . width / 2 ) < r // Left Half
&& t < y1 + ( this . helperProportions . height / 2 ) // Bottom Half
&& y2 - ( this . helperProportions . height / 2 ) < b ) ) return false ; // Top Half
if ( this . floating ) {
if ( x2 > l && x1 < l ) return 2 ; //Crosses left edge
if ( x1 < r && x2 > r ) return 1 ; //Crosses right edge
} else {
if ( y2 > t && y1 < t ) return 1 ; //Crosses top edge
if ( y1 < b && y2 > b ) return 2 ; //Crosses bottom edge
}
}
return false ;
} ,
2008-07-19 15:05:37 +00:00
2008-06-04 02:34:33 +00:00
refresh : function ( ) {
2008-08-17 02:19:14 +00:00
this . _refreshItems ( ) ;
2008-06-04 02:34:33 +00:00
this . refreshPositions ( ) ;
} ,
2008-07-19 15:05:37 +00:00
2008-08-17 02:19:14 +00:00
_getItemsAsjQuery : function ( connected ) {
2008-07-19 15:05:37 +00:00
var self = this ;
var items = [ ] ;
var queries = [ ] ;
if ( this . options . connectWith && connected ) {
for ( var i = this . options . connectWith . length - 1 ; i >= 0 ; i -- ) {
var cur = $ ( this . options . connectWith [ i ] ) ;
for ( var j = cur . length - 1 ; j >= 0 ; j -- ) {
var inst = $ . data ( cur [ j ] , 'sortable' ) ;
if ( inst && inst != this && ! inst . options . disabled ) {
queries . push ( [ $ . isFunction ( inst . options . items ) ? inst . options . items . call ( inst . element ) : $ ( inst . options . items , inst . element ) . not ( ".ui-sortable-helper" ) , inst ] ) ;
}
} ;
} ;
}
queries . push ( [ $ . isFunction ( this . options . items ) ? this . options . items . call ( this . element , null , { options : this . options , item : this . currentItem } ) : $ ( this . options . items , this . element ) . not ( ".ui-sortable-helper" ) , this ] ) ;
for ( var i = queries . length - 1 ; i >= 0 ; i -- ) {
queries [ i ] [ 0 ] . each ( function ( ) {
items . push ( this ) ;
} ) ;
} ;
return $ ( items ) ;
} ,
2008-08-17 02:19:14 +00:00
_removeCurrentsFromItems : function ( ) {
2008-07-28 18:34:01 +00:00
var list = this . currentItem . find ( ":data(sortable-item)" ) ;
for ( var i = 0 ; i < this . items . length ; i ++ ) {
for ( var j = 0 ; j < list . length ; j ++ ) {
if ( list [ j ] == this . items [ i ] . item [ 0 ] )
this . items . splice ( i , 1 ) ;
} ;
} ;
} ,
2008-08-17 02:19:14 +00:00
_refreshItems : function ( ) {
2008-06-04 02:34:33 +00:00
this . items = [ ] ;
this . containers = [ this ] ;
var items = this . items ;
2008-06-24 12:51:15 +00:00
var self = this ;
var queries = [ [ $ . isFunction ( this . options . items ) ? this . options . items . call ( this . element , null , { options : this . options , item : this . currentItem } ) : $ ( this . options . items , this . element ) , this ] ] ;
2008-06-16 12:19:31 +00:00
2008-06-04 02:34:33 +00:00
if ( this . options . connectWith ) {
for ( var i = this . options . connectWith . length - 1 ; i >= 0 ; i -- ) {
var cur = $ ( this . options . connectWith [ i ] ) ;
for ( var j = cur . length - 1 ; j >= 0 ; j -- ) {
var inst = $ . data ( cur [ j ] , 'sortable' ) ;
2008-07-19 15:05:37 +00:00
if ( inst && inst != this && ! inst . options . disabled ) {
2008-06-24 12:51:15 +00:00
queries . push ( [ $ . isFunction ( inst . options . items ) ? inst . options . items . call ( inst . element ) : $ ( inst . options . items , inst . element ) , inst ] ) ;
2008-06-04 02:34:33 +00:00
this . containers . push ( inst ) ;
}
} ;
} ;
}
for ( var i = queries . length - 1 ; i >= 0 ; i -- ) {
2008-06-24 12:51:15 +00:00
queries [ i ] [ 0 ] . each ( function ( ) {
$ . data ( this , 'sortable-item' , queries [ i ] [ 1 ] ) ; // Data for target checking (mouse manager)
2008-06-04 02:34:33 +00:00
items . push ( {
item : $ ( this ) ,
2008-06-24 15:28:12 +00:00
instance : queries [ i ] [ 1 ] ,
2008-06-04 02:34:33 +00:00
width : 0 , height : 0 ,
left : 0 , top : 0
} ) ;
} ) ;
} ;
} ,
2008-07-19 15:05:37 +00:00
2008-06-04 02:34:33 +00:00
refreshPositions : function ( fast ) {
2008-06-24 12:51:15 +00:00
//This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
if ( this . offsetParent ) {
var po = this . offsetParent . offset ( ) ;
2008-06-25 13:30:22 +00:00
this . offset . parent = { top : po . top + this . offsetParentBorders . top , left : po . left + this . offsetParentBorders . left } ;
2008-06-24 12:51:15 +00:00
}
2008-06-24 15:28:12 +00:00
for ( var i = this . items . length - 1 ; i >= 0 ; i -- ) {
//We ignore calculating positions of all connected containers when we're not over them
if ( this . items [ i ] . instance != this . currentContainer && this . currentContainer && this . items [ i ] . item [ 0 ] != this . currentItem [ 0 ] )
continue ;
2008-06-25 13:30:22 +00:00
var t = this . options . toleranceElement ? $ ( this . options . toleranceElement , this . items [ i ] . item ) : this . items [ i ] . item ;
2008-06-24 15:28:12 +00:00
2008-06-25 13:30:22 +00:00
if ( ! fast ) {
2008-06-30 13:43:09 +00:00
this . items [ i ] . width = t [ 0 ] . offsetWidth ;
this . items [ i ] . height = t [ 0 ] . offsetHeight ;
2008-06-25 13:30:22 +00:00
}
2008-06-24 15:28:12 +00:00
2008-06-25 13:30:22 +00:00
var p = t . offset ( ) ;
2008-06-04 02:34:33 +00:00
this . items [ i ] . left = p . left ;
this . items [ i ] . top = p . top ;
2008-06-24 15:28:12 +00:00
2008-06-04 02:34:33 +00:00
} ;
2008-07-09 09:03:24 +00:00
if ( this . options . custom && this . options . custom . refreshContainers ) {
this . options . custom . refreshContainers . call ( this ) ;
} else {
for ( var i = this . containers . length - 1 ; i >= 0 ; i -- ) {
var p = this . containers [ i ] . element . offset ( ) ;
this . containers [ i ] . containerCache . left = p . left ;
this . containers [ i ] . containerCache . top = p . top ;
this . containers [ i ] . containerCache . width = this . containers [ i ] . element . outerWidth ( ) ;
this . containers [ i ] . containerCache . height = this . containers [ i ] . element . outerHeight ( ) ;
} ;
}
2008-06-04 02:34:33 +00:00
} ,
2008-07-19 15:05:37 +00:00
2008-06-04 02:34:33 +00:00
destroy : function ( ) {
this . element
. removeClass ( "ui-sortable ui-sortable-disabled" )
. removeData ( "sortable" )
. unbind ( ".sortable" ) ;
2008-08-17 08:15:49 +00:00
this . _mouseDestroy ( ) ;
2008-06-04 02:34:33 +00:00
for ( var i = this . items . length - 1 ; i >= 0 ; i -- )
this . items [ i ] . item . removeData ( "sortable-item" ) ;
} ,
2008-07-19 15:05:37 +00:00
2008-08-17 02:19:14 +00:00
_createPlaceholder : function ( that ) {
2008-06-04 02:34:33 +00:00
var self = that || this , o = self . options ;
2008-07-09 11:09:40 +00:00
if ( ! o . placeholder || o . placeholder . constructor == String ) {
2008-06-04 02:34:33 +00:00
var className = o . placeholder ;
o . placeholder = {
element : function ( ) {
2008-07-09 11:09:40 +00:00
var el = $ ( document . createElement ( self . currentItem [ 0 ] . nodeName ) ) . addClass ( className || "ui-sortable-placeholder" ) [ 0 ] ;
if ( ! className ) { el . style . visibility = "hidden" ; el . innerHTML = self . currentItem [ 0 ] . innerHTML ; } ;
return el ;
2008-06-04 02:34:33 +00:00
} ,
2008-07-09 11:09:40 +00:00
update : function ( container , p ) {
if ( className ) return ;
if ( ! p . height ( ) ) { p . height ( self . currentItem . innerHeight ( ) ) ; } ;
if ( ! p . width ( ) ) { p . width ( self . currentItem . innerWidth ( ) ) ; } ;
2008-06-04 02:34:33 +00:00
}
} ;
}
2008-07-09 11:09:40 +00:00
self . placeholder = $ ( o . placeholder . element . call ( self . element , self . currentItem ) ) . appendTo ( self . currentItem . parent ( ) ) ;
self . currentItem . before ( self . placeholder ) ;
o . placeholder . update ( self , self . placeholder ) ;
2008-06-04 02:34:33 +00:00
} ,
2008-07-19 15:05:37 +00:00
2008-08-17 02:19:14 +00:00
_contactContainers : function ( e ) {
2008-06-04 02:34:33 +00:00
for ( var i = this . containers . length - 1 ; i >= 0 ; i -- ) {
2008-08-17 02:19:14 +00:00
if ( this . _intersectsWith ( this . containers [ i ] . containerCache ) ) {
2008-06-04 02:34:33 +00:00
if ( ! this . containers [ i ] . containerCache . over ) {
if ( this . currentContainer != this . containers [ i ] ) {
//When entering a new container, we will find the item with the least distance and append our item near it
var dist = 10000 ; var itemWithLeastDistance = null ; var base = this . positionAbs [ this . containers [ i ] . floating ? 'left' : 'top' ] ;
for ( var j = this . items . length - 1 ; j >= 0 ; j -- ) {
if ( ! contains ( this . containers [ i ] . element [ 0 ] , this . items [ j ] . item [ 0 ] ) ) continue ;
var cur = this . items [ j ] [ this . containers [ i ] . floating ? 'left' : 'top' ] ;
if ( Math . abs ( cur - base ) < dist ) {
dist = Math . abs ( cur - base ) ; itemWithLeastDistance = this . items [ j ] ;
}
}
if ( ! itemWithLeastDistance && ! this . options . dropOnEmpty ) //Check if dropOnEmpty is enabled
continue ;
2008-06-24 15:28:12 +00:00
this . currentContainer = this . containers [ i ] ;
2008-07-09 11:09:40 +00:00
itemWithLeastDistance ? this . options . sortIndicator . call ( this , e , itemWithLeastDistance , null , true ) : this . options . sortIndicator . call ( this , e , null , this . containers [ i ] . element , true ) ;
2008-08-17 02:15:55 +00:00
this . _propagate ( "change" , e ) ; //Call plugins and callbacks
this . containers [ i ] . _propagate ( "change" , e , this ) ; //Call plugins and callbacks
2008-07-09 11:09:40 +00:00
//Update the placeholder
this . options . placeholder . update ( this . currentContainer , this . placeholder ) ;
2008-06-04 02:34:33 +00:00
}
2008-08-17 02:15:55 +00:00
this . containers [ i ] . _propagate ( "over" , e , this ) ;
2008-06-04 02:34:33 +00:00
this . containers [ i ] . containerCache . over = 1 ;
}
} else {
if ( this . containers [ i ] . containerCache . over ) {
2008-08-17 02:15:55 +00:00
this . containers [ i ] . _propagate ( "out" , e , this ) ;
2008-06-04 02:34:33 +00:00
this . containers [ i ] . containerCache . over = 0 ;
}
}
} ;
} ,
2008-07-19 15:05:37 +00:00
2008-08-17 08:15:49 +00:00
_mouseCapture : function ( e , overrideHandle ) {
2008-06-24 12:51:15 +00:00
2008-06-04 02:34:33 +00:00
if ( this . options . disabled || this . options . type == 'static' ) return false ;
2008-06-24 12:51:15 +00:00
//We have to refresh the items data once first
2008-08-17 02:19:14 +00:00
this . _refreshItems ( ) ;
2008-06-24 12:51:15 +00:00
2008-06-04 02:34:33 +00:00
//Find out if the clicked node (or one of its parents) is a actual item in this.items
2008-06-24 12:51:15 +00:00
var currentItem = null , self = this , nodes = $ ( e . target ) . parents ( ) . each ( function ( ) {
if ( $ . data ( this , 'sortable-item' ) == self ) {
2008-06-04 02:34:33 +00:00
currentItem = $ ( this ) ;
return false ;
}
} ) ;
2008-06-24 12:51:15 +00:00
if ( $ . data ( e . target , 'sortable-item' ) == self ) currentItem = $ ( e . target ) ;
2008-06-04 02:34:33 +00:00
if ( ! currentItem ) return false ;
if ( this . options . handle && ! overrideHandle ) {
var validHandle = false ;
2008-06-24 12:51:15 +00:00
2008-06-07 23:25:47 +00:00
$ ( this . options . handle , currentItem ) . find ( "*" ) . andSelf ( ) . each ( function ( ) { if ( this == e . target ) validHandle = true ; } ) ;
2008-06-04 02:34:33 +00:00
if ( ! validHandle ) return false ;
}
this . currentItem = currentItem ;
2008-08-17 02:19:14 +00:00
this . _removeCurrentsFromItems ( ) ;
2008-06-09 01:54:28 +00:00
return true ;
} ,
2008-07-19 15:05:37 +00:00
2008-08-17 08:15:49 +00:00
_mouseStart : function ( e , overrideHandle , noActivation ) {
2008-06-09 01:54:28 +00:00
var o = this . options ;
this . currentContainer = this ;
2008-06-04 02:34:33 +00:00
2008-06-24 12:51:15 +00:00
//We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
this . refreshPositions ( ) ;
2008-06-04 02:34:33 +00:00
//Create and append the visible helper
2008-07-09 11:09:40 +00:00
this . helper = typeof o . helper == 'function' ? $ ( o . helper . apply ( this . element [ 0 ] , [ e , this . currentItem ] ) ) : ( o . helper == "original" ? this . currentItem : this . currentItem . clone ( ) ) ;
2008-06-29 14:34:14 +00:00
if ( ! this . helper . parents ( 'body' ) . length ) $ ( o . appendTo != 'parent' ? o . appendTo : this . currentItem [ 0 ] . parentNode ) [ 0 ] . appendChild ( this . helper [ 0 ] ) ; //Add the helper to the DOM if that didn't happen already
2008-06-04 02:34:33 +00:00
/ *
* - Position generation -
* This block generates everything position related - it ' s the core of draggables .
* /
this . margins = { //Cache the margins
left : ( parseInt ( this . currentItem . css ( "marginLeft" ) , 10 ) || 0 ) ,
top : ( parseInt ( this . currentItem . css ( "marginTop" ) , 10 ) || 0 )
} ;
this . offset = this . currentItem . offset ( ) ; //The element's absolute position on the page
this . offset = { //Substract the margins from the element's absolute offset
top : this . offset . top - this . margins . top ,
left : this . offset . left - this . margins . left
} ;
this . offset . click = { //Where the click happened, relative to the element
left : e . pageX - this . offset . left ,
top : e . pageY - this . offset . top
} ;
2008-06-25 13:30:22 +00:00
this . offsetParent = this . helper . offsetParent ( ) ; //Get the offsetParent and cache its position
var po = this . offsetParent . offset ( ) ;
2008-06-04 02:34:33 +00:00
2008-06-25 13:30:22 +00:00
this . offsetParentBorders = {
top : ( parseInt ( this . offsetParent . css ( "borderTopWidth" ) , 10 ) || 0 ) ,
left : ( parseInt ( this . offsetParent . css ( "borderLeftWidth" ) , 10 ) || 0 )
} ;
2008-06-04 02:34:33 +00:00
this . offset . parent = { //Store its position plus border
2008-06-25 13:30:22 +00:00
top : po . top + this . offsetParentBorders . top ,
left : po . left + this . offsetParentBorders . left
2008-06-04 02:34:33 +00:00
} ;
2008-08-17 02:15:55 +00:00
this . updateOriginalPosition = this . originalPosition = this . _generatePosition ( e ) ; //Generate the original position
2008-06-26 10:22:11 +00:00
this . domPosition = { prev : this . currentItem . prev ( ) [ 0 ] , parent : this . currentItem . parent ( ) [ 0 ] } ; //Cache the former DOM position
2008-06-16 12:19:31 +00:00
//If o.placeholder is used, create a new element at the given position with the class
2008-06-04 02:34:33 +00:00
this . helperProportions = { width : this . helper . outerWidth ( ) , height : this . helper . outerHeight ( ) } ; //Cache the helper size
2008-07-09 11:09:40 +00:00
if ( o . helper == "original" ) {
this . _storedCSS = { position : this . currentItem . css ( "position" ) , top : this . currentItem . css ( "top" ) , left : this . currentItem . css ( "left" ) , clear : this . currentItem . css ( "clear" ) } ;
}
2008-06-16 12:19:31 +00:00
2008-07-09 11:09:40 +00:00
if ( o . helper != "original" ) this . currentItem . hide ( ) ; //Hide the original, won't cause anything bad this way
this . helper . css ( { position : 'absolute' , clear : 'both' } ) . addClass ( 'ui-sortable-helper' ) ; //Position it absolutely and add a helper class
2008-08-17 02:19:14 +00:00
this . _createPlaceholder ( ) ;
2008-07-09 11:09:40 +00:00
2008-06-16 12:19:31 +00:00
//Call plugins and callbacks
2008-08-17 02:15:55 +00:00
this . _propagate ( "start" , e ) ;
2008-07-28 18:34:01 +00:00
if ( ! this . _preserveHelperProportions ) this . helperProportions = { width : this . helper . outerWidth ( ) , height : this . helper . outerHeight ( ) } ; //Recache the helper size
2008-06-04 02:34:33 +00:00
if ( o . cursorAt ) {
if ( o . cursorAt . left != undefined ) this . offset . click . left = o . cursorAt . left ;
if ( o . cursorAt . right != undefined ) this . offset . click . left = this . helperProportions . width - o . cursorAt . right ;
if ( o . cursorAt . top != undefined ) this . offset . click . top = o . cursorAt . top ;
if ( o . cursorAt . bottom != undefined ) this . offset . click . top = this . helperProportions . height - o . cursorAt . bottom ;
}
/ *
* - Position constraining -
* Here we prepare position constraining like grid and containment .
* /
if ( o . containment ) {
if ( o . containment == 'parent' ) o . containment = this . helper [ 0 ] . parentNode ;
2008-06-07 23:38:03 +00:00
if ( o . containment == 'document' || o . containment == 'window' ) this . containment = [
0 - this . offset . parent . left ,
0 - this . offset . parent . top ,
$ ( o . containment == 'document' ? document : window ) . width ( ) - this . offset . parent . left - this . helperProportions . width - this . margins . left - ( parseInt ( this . element . css ( "marginRight" ) , 10 ) || 0 ) ,
( $ ( o . containment == 'document' ? document : window ) . height ( ) || document . body . parentNode . scrollHeight ) - this . offset . parent . top - this . helperProportions . height - this . margins . top - ( parseInt ( this . element . css ( "marginBottom" ) , 10 ) || 0 )
] ;
2008-06-04 02:34:33 +00:00
if ( ! ( /^(document|window|parent)$/ ) . test ( o . containment ) ) {
var ce = $ ( o . containment ) [ 0 ] ;
var co = $ ( o . containment ) . offset ( ) ;
this . containment = [
co . left + ( parseInt ( $ ( ce ) . css ( "borderLeftWidth" ) , 10 ) || 0 ) - this . offset . parent . left ,
co . top + ( parseInt ( $ ( ce ) . css ( "borderTopWidth" ) , 10 ) || 0 ) - this . offset . parent . top ,
co . left + Math . max ( ce . scrollWidth , ce . offsetWidth ) - ( parseInt ( $ ( ce ) . css ( "borderLeftWidth" ) , 10 ) || 0 ) - this . offset . parent . left - this . helperProportions . width - this . margins . left - ( parseInt ( this . currentItem . css ( "marginRight" ) , 10 ) || 0 ) ,
co . top + Math . max ( ce . scrollHeight , ce . offsetHeight ) - ( parseInt ( $ ( ce ) . css ( "borderTopWidth" ) , 10 ) || 0 ) - this . offset . parent . top - this . helperProportions . height - this . margins . top - ( parseInt ( this . currentItem . css ( "marginBottom" ) , 10 ) || 0 )
] ;
}
}
2008-06-16 12:19:31 +00:00
//Post 'activate' events to possible containers
2008-06-04 02:34:33 +00:00
if ( ! noActivation ) {
2008-08-17 02:15:55 +00:00
for ( var i = this . containers . length - 1 ; i >= 0 ; i -- ) { this . containers [ i ] . _propagate ( "activate" , e , this ) ; }
2008-06-04 02:34:33 +00:00
}
//Prepare possible droppables
if ( $ . ui . ddmanager ) $ . ui . ddmanager . current = this ;
if ( $ . ui . ddmanager && ! o . dropBehaviour ) $ . ui . ddmanager . prepareOffsets ( this , e ) ;
this . dragging = true ;
2008-08-17 08:15:49 +00:00
this . _mouseDrag ( e ) ; //Execute the drag once - this causes the helper not to be visible before getting its correct position
2008-06-04 02:34:33 +00:00
return true ;
} ,
2008-07-19 15:05:37 +00:00
2008-08-17 02:15:55 +00:00
_convertPositionTo : function ( d , pos ) {
2008-06-04 02:34:33 +00:00
if ( ! pos ) pos = this . position ;
var mod = d == "absolute" ? 1 : - 1 ;
return {
top : (
pos . top // the calculated relative position
+ this . offset . parent . top * mod // The offsetParent's offset without borders (offset + border)
- ( this . offsetParent [ 0 ] == document . body ? 0 : this . offsetParent [ 0 ] . scrollTop ) * mod // The offsetParent's scroll position
+ this . margins . top * mod //Add the margin (you don't want the margin counting in intersection methods)
) ,
left : (
pos . left // the calculated relative position
+ this . offset . parent . left * mod // The offsetParent's offset without borders (offset + border)
- ( this . offsetParent [ 0 ] == document . body ? 0 : this . offsetParent [ 0 ] . scrollLeft ) * mod // The offsetParent's scroll position
+ this . margins . left * mod //Add the margin (you don't want the margin counting in intersection methods)
)
} ;
} ,
2008-07-19 15:05:37 +00:00
2008-08-17 02:15:55 +00:00
_generatePosition : function ( e ) {
2008-06-04 02:34:33 +00:00
var o = this . options ;
var position = {
top : (
e . pageY // The absolute mouse position
- this . offset . click . top // Click offset (relative to the element)
- this . offset . parent . top // The offsetParent's offset without borders (offset + border)
+ ( this . offsetParent [ 0 ] == document . body ? 0 : this . offsetParent [ 0 ] . scrollTop ) // The offsetParent's scroll position, not if the element is fixed
) ,
left : (
e . pageX // The absolute mouse position
- this . offset . click . left // Click offset (relative to the element)
- this . offset . parent . left // The offsetParent's offset without borders (offset + border)
+ ( this . offsetParent [ 0 ] == document . body ? 0 : this . offsetParent [ 0 ] . scrollLeft ) // The offsetParent's scroll position, not if the element is fixed
)
} ;
if ( ! this . originalPosition ) return position ; //If we are not dragging yet, we won't check for options
/ *
* - Position constraining -
* Constrain the position to a mix of grid , containment .
* /
if ( this . containment ) {
if ( position . left < this . containment [ 0 ] ) position . left = this . containment [ 0 ] ;
if ( position . top < this . containment [ 1 ] ) position . top = this . containment [ 1 ] ;
if ( position . left > this . containment [ 2 ] ) position . left = this . containment [ 2 ] ;
if ( position . top > this . containment [ 3 ] ) position . top = this . containment [ 3 ] ;
}
if ( o . grid ) {
var top = this . originalPosition . top + Math . round ( ( position . top - this . originalPosition . top ) / o . grid [ 1 ] ) * o . grid [ 1 ] ;
position . top = this . containment ? ( ! ( top < this . containment [ 1 ] || top > this . containment [ 3 ] ) ? top : ( ! ( top < this . containment [ 1 ] ) ? top - o . grid [ 1 ] : top + o . grid [ 1 ] ) ) : top ;
var left = this . originalPosition . left + Math . round ( ( position . left - this . originalPosition . left ) / o . grid [ 0 ] ) * o . grid [ 0 ] ;
position . left = this . containment ? ( ! ( left < this . containment [ 0 ] || left > this . containment [ 2 ] ) ? left : ( ! ( left < this . containment [ 0 ] ) ? left - o . grid [ 0 ] : left + o . grid [ 0 ] ) ) : left ;
}
return position ;
} ,
2008-07-19 15:05:37 +00:00
2008-08-17 08:15:49 +00:00
_mouseDrag : function ( e ) {
2008-06-04 02:34:33 +00:00
//Compute the helpers position
2008-08-17 02:15:55 +00:00
this . position = this . _generatePosition ( e ) ;
this . positionAbs = this . _convertPositionTo ( "absolute" ) ;
2008-06-04 02:34:33 +00:00
2008-06-30 14:08:06 +00:00
//Call the internal plugins
$ . ui . plugin . call ( this , "sort" , [ e , this . ui ( ) ] ) ;
//Regenerate the absolute position used for position checks
2008-08-17 02:15:55 +00:00
this . positionAbs = this . _convertPositionTo ( "absolute" ) ;
2008-06-30 14:08:06 +00:00
//Set the helper's position
this . helper [ 0 ] . style . left = this . position . left + 'px' ;
this . helper [ 0 ] . style . top = this . position . top + 'px' ;
2008-06-04 02:34:33 +00:00
//Rearrange
for ( var i = this . items . length - 1 ; i >= 0 ; i -- ) {
2008-08-17 02:19:14 +00:00
var intersection = this . _intersectsWithEdge ( this . items [ i ] ) ;
2008-06-04 02:34:33 +00:00
if ( ! intersection ) continue ;
if ( this . items [ i ] . item [ 0 ] != this . currentItem [ 0 ] //cannot intersect with itself
2008-07-09 11:09:40 +00:00
&& this . placeholder [ intersection == 1 ? "next" : "prev" ] ( ) [ 0 ] != this . items [ i ] . item [ 0 ] //no useless actions that have been done before
&& ! contains ( this . placeholder [ 0 ] , this . items [ i ] . item [ 0 ] ) //no action if the item moved is the parent of the item checked
2008-06-04 02:34:33 +00:00
&& ( this . options . type == 'semi-dynamic' ? ! contains ( this . element [ 0 ] , this . items [ i ] . item [ 0 ] ) : true )
) {
2008-08-17 02:15:55 +00:00
this . updateOriginalPosition = this . _generatePosition ( e ) ;
2008-07-10 17:09:41 +00:00
2008-06-04 02:34:33 +00:00
this . direction = intersection == 1 ? "down" : "up" ;
2008-07-09 11:09:40 +00:00
this . options . sortIndicator . call ( this , e , this . items [ i ] ) ;
2008-08-17 02:15:55 +00:00
this . _propagate ( "change" , e ) ; //Call plugins and callbacks
2008-06-04 02:34:33 +00:00
break ;
}
}
//Post events to containers
2008-08-17 02:19:14 +00:00
this . _contactContainers ( e ) ;
2008-06-04 02:34:33 +00:00
//Interconnect with droppables
if ( $ . ui . ddmanager ) $ . ui . ddmanager . drag ( this , e ) ;
2008-06-30 14:08:06 +00:00
//Call callbacks
this . element . triggerHandler ( "sort" , [ e , this . ui ( ) ] , this . options [ "sort" ] ) ;
2008-06-04 02:34:33 +00:00
return false ;
} ,
2008-07-19 15:05:37 +00:00
2008-08-17 02:19:14 +00:00
_rearrange : function ( e , i , a , hardRefresh ) {
2008-07-09 11:09:40 +00:00
a ? a [ 0 ] . appendChild ( this . placeholder [ 0 ] ) : i . item [ 0 ] . parentNode . insertBefore ( this . placeholder [ 0 ] , ( this . direction == 'down' ? i . item [ 0 ] : i . item [ 0 ] . nextSibling ) ) ;
2008-06-25 13:30:22 +00:00
//Various things done here to improve the performance:
// 1. we create a setTimeout, that calls refreshPositions
// 2. on the instance, we have a counter variable, that get's higher after every append
// 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
// 4. this lets only the last addition to the timeout stack through
this . counter = this . counter ? ++ this . counter : 1 ;
var self = this , counter = this . counter ;
window . setTimeout ( function ( ) {
if ( counter == self . counter ) self . refreshPositions ( ! hardRefresh ) ; //Precompute after each DOM insertion, NOT on mousemove
} , 0 ) ;
2008-07-09 11:09:40 +00:00
2008-06-24 12:51:15 +00:00
} ,
2008-07-19 15:05:37 +00:00
2008-08-17 08:15:49 +00:00
_mouseStop : function ( e , noPropagation ) {
2008-06-04 02:34:33 +00:00
//If we are using droppables, inform the manager about the drop
if ( $ . ui . ddmanager && ! this . options . dropBehaviour )
$ . ui . ddmanager . drop ( this , e ) ;
if ( this . options . revert ) {
var self = this ;
2008-07-09 11:09:40 +00:00
var cur = self . placeholder . offset ( ) ;
2008-06-04 02:34:33 +00:00
$ ( this . helper ) . animate ( {
left : cur . left - this . offset . parent . left - self . margins . left + ( this . offsetParent [ 0 ] == document . body ? 0 : this . offsetParent [ 0 ] . scrollLeft ) ,
top : cur . top - this . offset . parent . top - self . margins . top + ( this . offsetParent [ 0 ] == document . body ? 0 : this . offsetParent [ 0 ] . scrollTop )
} , parseInt ( this . options . revert , 10 ) || 500 , function ( ) {
2008-08-17 02:15:55 +00:00
self . _clear ( e ) ;
2008-06-04 02:34:33 +00:00
} ) ;
} else {
2008-08-17 02:15:55 +00:00
this . _clear ( e , noPropagation ) ;
2008-06-04 02:34:33 +00:00
}
return false ;
} ,
2008-07-19 15:05:37 +00:00
2008-08-17 02:15:55 +00:00
_clear : function ( e , noPropagation ) {
2008-06-07 14:01:33 +00:00
2008-07-09 11:09:40 +00:00
//We first have to update the dom position of the actual currentItem
if ( ! this . _noFinalSort ) this . placeholder . before ( this . currentItem ) ;
this . _noFinalSort = null ;
if ( this . options . helper == "original" )
this . currentItem . css ( this . _storedCSS ) . removeClass ( "ui-sortable-helper" ) ;
else
this . currentItem . show ( ) ;
2008-08-17 02:15:55 +00:00
if ( this . domPosition . prev != this . currentItem . prev ( ) . not ( ".ui-sortable-helper" ) [ 0 ] || this . domPosition . parent != this . currentItem . parent ( ) [ 0 ] ) this . _propagate ( "update" , e , null , noPropagation ) ; //Trigger update callback if the DOM position has changed
2008-06-04 02:34:33 +00:00
if ( ! contains ( this . element [ 0 ] , this . currentItem [ 0 ] ) ) { //Node was moved out of the current element
2008-08-17 02:15:55 +00:00
this . _propagate ( "remove" , e , null , noPropagation ) ;
2008-06-04 02:34:33 +00:00
for ( var i = this . containers . length - 1 ; i >= 0 ; i -- ) {
if ( contains ( this . containers [ i ] . element [ 0 ] , this . currentItem [ 0 ] ) ) {
2008-08-17 02:15:55 +00:00
this . containers [ i ] . _propagate ( "update" , e , this , noPropagation ) ;
this . containers [ i ] . _propagate ( "receive" , e , this , noPropagation ) ;
2008-06-04 02:34:33 +00:00
}
} ;
} ;
//Post events to containers
for ( var i = this . containers . length - 1 ; i >= 0 ; i -- ) {
2008-08-17 02:15:55 +00:00
this . containers [ i ] . _propagate ( "deactivate" , e , this , noPropagation ) ;
2008-06-04 02:34:33 +00:00
if ( this . containers [ i ] . containerCache . over ) {
2008-08-17 02:15:55 +00:00
this . containers [ i ] . _propagate ( "out" , e , this ) ;
2008-06-04 02:34:33 +00:00
this . containers [ i ] . containerCache . over = 0 ;
}
}
this . dragging = false ;
2008-06-24 11:47:42 +00:00
if ( this . cancelHelperRemoval ) {
2008-08-17 02:15:55 +00:00
this . _propagate ( "beforeStop" , e , null , noPropagation ) ;
this . _propagate ( "stop" , e , null , noPropagation ) ;
2008-06-24 11:47:42 +00:00
return false ;
}
2008-08-17 02:15:55 +00:00
this . _propagate ( "beforeStop" , e , null , noPropagation ) ;
2008-07-09 11:09:40 +00:00
this . placeholder . remove ( ) ;
if ( this . options . helper != "original" ) this . helper . remove ( ) ; this . helper = null ;
2008-08-17 02:15:55 +00:00
this . _propagate ( "stop" , e , null , noPropagation ) ;
2008-06-04 02:34:33 +00:00
return true ;
}
} ) ) ;
$ . extend ( $ . ui . sortable , {
getter : "serialize toArray" ,
defaults : {
2008-07-09 11:09:40 +00:00
helper : "original" ,
2008-06-04 02:34:33 +00:00
tolerance : "guess" ,
2008-06-24 14:29:29 +00:00
distance : 1 ,
2008-06-04 02:34:33 +00:00
delay : 0 ,
2008-06-24 12:51:15 +00:00
scroll : true ,
scrollSensitivity : 20 ,
scrollSpeed : 20 ,
2008-06-26 10:23:38 +00:00
cancel : ":input" ,
2008-06-04 02:34:33 +00:00
items : '> *' ,
zIndex : 1000 ,
dropOnEmpty : true ,
2008-07-09 11:09:40 +00:00
appendTo : "parent" ,
2008-08-17 02:19:14 +00:00
sortIndicator : $ . ui . sortable . prototype . _rearrange ,
2008-07-28 18:34:01 +00:00
scope : "default"
2008-06-04 02:34:33 +00:00
}
} ) ;
/ *
* Sortable Extensions
* /
$ . ui . plugin . add ( "sortable" , "cursor" , {
start : function ( e , ui ) {
var t = $ ( 'body' ) ;
if ( t . css ( "cursor" ) ) ui . options . _cursor = t . css ( "cursor" ) ;
t . css ( "cursor" , ui . options . cursor ) ;
} ,
2008-07-09 11:09:40 +00:00
beforeStop : function ( e , ui ) {
2008-06-04 02:34:33 +00:00
if ( ui . options . _cursor ) $ ( 'body' ) . css ( "cursor" , ui . options . _cursor ) ;
}
} ) ;
$ . ui . plugin . add ( "sortable" , "zIndex" , {
start : function ( e , ui ) {
var t = ui . helper ;
if ( t . css ( "zIndex" ) ) ui . options . _zIndex = t . css ( "zIndex" ) ;
t . css ( 'zIndex' , ui . options . zIndex ) ;
} ,
2008-07-09 11:09:40 +00:00
beforeStop : function ( e , ui ) {
2008-06-04 02:34:33 +00:00
if ( ui . options . _zIndex ) $ ( ui . helper ) . css ( 'zIndex' , ui . options . _zIndex ) ;
}
} ) ;
$ . ui . plugin . add ( "sortable" , "opacity" , {
start : function ( e , ui ) {
var t = ui . helper ;
if ( t . css ( "opacity" ) ) ui . options . _opacity = t . css ( "opacity" ) ;
t . css ( 'opacity' , ui . options . opacity ) ;
} ,
2008-07-09 11:09:40 +00:00
beforeStop : function ( e , ui ) {
2008-06-04 02:34:33 +00:00
if ( ui . options . _opacity ) $ ( ui . helper ) . css ( 'opacity' , ui . options . _opacity ) ;
}
} ) ;
$ . ui . plugin . add ( "sortable" , "scroll" , {
start : function ( e , ui ) {
var o = ui . options ;
var i = $ ( this ) . data ( "sortable" ) ;
i . overflowY = function ( el ) {
do { if ( /auto|scroll/ . test ( el . css ( 'overflow' ) ) || ( /auto|scroll/ ) . test ( el . css ( 'overflow-y' ) ) ) return el ; el = el . parent ( ) ; } while ( el [ 0 ] . parentNode ) ;
return $ ( document ) ;
} ( i . currentItem ) ;
i . overflowX = function ( el ) {
do { if ( /auto|scroll/ . test ( el . css ( 'overflow' ) ) || ( /auto|scroll/ ) . test ( el . css ( 'overflow-x' ) ) ) return el ; el = el . parent ( ) ; } while ( el [ 0 ] . parentNode ) ;
return $ ( document ) ;
} ( i . currentItem ) ;
if ( i . overflowY [ 0 ] != document && i . overflowY [ 0 ] . tagName != 'HTML' ) i . overflowYOffset = i . overflowY . offset ( ) ;
if ( i . overflowX [ 0 ] != document && i . overflowX [ 0 ] . tagName != 'HTML' ) i . overflowXOffset = i . overflowX . offset ( ) ;
} ,
sort : function ( e , ui ) {
var o = ui . options ;
var i = $ ( this ) . data ( "sortable" ) ;
if ( i . overflowY [ 0 ] != document && i . overflowY [ 0 ] . tagName != 'HTML' ) {
if ( ( i . overflowYOffset . top + i . overflowY [ 0 ] . offsetHeight ) - e . pageY < o . scrollSensitivity )
i . overflowY [ 0 ] . scrollTop = i . overflowY [ 0 ] . scrollTop + o . scrollSpeed ;
if ( e . pageY - i . overflowYOffset . top < o . scrollSensitivity )
i . overflowY [ 0 ] . scrollTop = i . overflowY [ 0 ] . scrollTop - o . scrollSpeed ;
} else {
if ( e . pageY - $ ( document ) . scrollTop ( ) < o . scrollSensitivity )
$ ( document ) . scrollTop ( $ ( document ) . scrollTop ( ) - o . scrollSpeed ) ;
if ( $ ( window ) . height ( ) - ( e . pageY - $ ( document ) . scrollTop ( ) ) < o . scrollSensitivity )
$ ( document ) . scrollTop ( $ ( document ) . scrollTop ( ) + o . scrollSpeed ) ;
}
if ( i . overflowX [ 0 ] != document && i . overflowX [ 0 ] . tagName != 'HTML' ) {
if ( ( i . overflowXOffset . left + i . overflowX [ 0 ] . offsetWidth ) - e . pageX < o . scrollSensitivity )
i . overflowX [ 0 ] . scrollLeft = i . overflowX [ 0 ] . scrollLeft + o . scrollSpeed ;
if ( e . pageX - i . overflowXOffset . left < o . scrollSensitivity )
i . overflowX [ 0 ] . scrollLeft = i . overflowX [ 0 ] . scrollLeft - o . scrollSpeed ;
} else {
if ( e . pageX - $ ( document ) . scrollLeft ( ) < o . scrollSensitivity )
$ ( document ) . scrollLeft ( $ ( document ) . scrollLeft ( ) - o . scrollSpeed ) ;
if ( $ ( window ) . width ( ) - ( e . pageX - $ ( document ) . scrollLeft ( ) ) < o . scrollSensitivity )
$ ( document ) . scrollLeft ( $ ( document ) . scrollLeft ( ) + o . scrollSpeed ) ;
}
}
} ) ;
2008-06-30 14:08:06 +00:00
$ . ui . plugin . add ( "sortable" , "axis" , {
sort : function ( e , ui ) {
var i = $ ( this ) . data ( "sortable" ) ;
if ( ui . options . axis == "y" ) i . position . left = i . originalPosition . left ;
if ( ui . options . axis == "x" ) i . position . top = i . originalPosition . top ;
}
} ) ;
2008-06-04 02:34:33 +00:00
} ) ( jQuery ) ;