2016-04-06 23:28:27 +08:00
// jQuery List DragSort v0.5.1
// Website: http://dragsort.codeplex.com/
// License: http://dragsort.codeplex.com/license
( function ( $ ) {
$ . fn . dragsort = function ( options ) {
if ( options == "destroy" ) {
var $selector = ( $ ( this . selector ) . length ) ? $ ( this . selector ) : $ ( this ) ;
$selector . trigger ( "dragsort-uninit" ) ;
return ;
}
var opts = $ . extend ( { } , $ . fn . dragsort . defaults , options ) ;
var lists = [ ] ;
var list = null , lastPos = null ;
this . each ( function ( i , cont ) {
//if list container is table, the browser automatically wraps rows in tbody if not specified so change list container to tbody so that children returns rows as user expected
if ( $ ( cont ) . is ( "table" ) && $ ( cont ) . children ( ) . size ( ) == 1 && $ ( cont ) . children ( ) . is ( "tbody" ) )
cont = $ ( cont ) . children ( ) . get ( 0 ) ;
var newList = {
draggedItem : null ,
placeHolderItem : null ,
pos : null ,
offset : null ,
offsetLimit : null ,
scroll : null ,
container : cont ,
init : function ( ) {
//set options to default values if not set
var tagName = $ ( this . container ) . children ( ) . size ( ) == 0 ? "li" : $ ( this . container ) . children ( ":first" ) . get ( 0 ) . tagName . toLowerCase ( ) ;
if ( opts . itemSelector == "" )
opts . itemSelector = tagName ;
if ( opts . dragSelector == "" )
opts . dragSelector = tagName ;
if ( opts . placeHolderTemplate == "" )
opts . placeHolderTemplate = "<" + tagName + "> </" + tagName + ">" ;
//listidx allows reference back to correct list variable instance
$ ( this . container ) . attr ( "data-listidx" , i ) . mousedown ( this . grabItem ) . bind ( "dragsort-uninit" , this . uninit ) ;
this . styleDragHandlers ( true ) ;
} ,
uninit : function ( ) {
var list = lists [ $ ( this ) . attr ( "data-listidx" ) ] ;
$ ( list . container ) . unbind ( "mousedown" , list . grabItem ) . unbind ( "dragsort-uninit" ) ;
list . styleDragHandlers ( false ) ;
} ,
getItems : function ( ) {
return $ ( this . container ) . children ( opts . itemSelector ) ;
} ,
styleDragHandlers : function ( cursor ) {
this . getItems ( ) . map ( function ( ) { return $ ( this ) . is ( opts . dragSelector ) ? this : $ ( this ) . find ( opts . dragSelector ) . get ( ) ; } ) . css ( "cursor" , cursor ? "pointer" : "" ) ;
} ,
grabItem : function ( e ) {
//if not left click or if clicked on excluded element (e.g. text box) or not a moveable list item return
if ( e . which != 1 || $ ( e . target ) . is ( opts . dragSelectorExclude ) || $ ( e . target ) . closest ( opts . dragSelectorExclude ) . size ( ) > 0 || $ ( e . target ) . closest ( opts . itemSelector ) . size ( ) == 0 )
return ;
//prevents selection, stops issue on Fx where dragging hyperlink doesn't work and on IE where it triggers mousemove even though mouse hasn't moved,
//does also stop being able to click text boxes hence dragging on text boxes by default is disabled in dragSelectorExclude
e . preventDefault ( ) ;
//change cursor to move while dragging
var dragHandle = e . target ;
while ( ! $ ( dragHandle ) . is ( opts . dragSelector ) ) {
if ( dragHandle == this ) return ;
dragHandle = dragHandle . parentNode ;
}
$ ( dragHandle ) . attr ( "data-cursor" , $ ( dragHandle ) . css ( "cursor" ) ) ;
$ ( dragHandle ) . css ( "cursor" , "move" ) ;
//on mousedown wait for movement of mouse before triggering dragsort script (dragStart) to allow clicking of hyperlinks to work
var list = lists [ $ ( this ) . attr ( "data-listidx" ) ] ;
var item = this ;
var trigger = function ( ) {
list . dragStart . call ( item , e ) ;
$ ( list . container ) . unbind ( "mousemove" , trigger ) ;
} ;
$ ( list . container ) . mousemove ( trigger ) . mouseup ( function ( ) { $ ( list . container ) . unbind ( "mousemove" , trigger ) ; $ ( dragHandle ) . css ( "cursor" , $ ( dragHandle ) . attr ( "data-cursor" ) ) ; } ) ;
} ,
dragStart : function ( e ) {
if ( list != null && list . draggedItem != null )
list . dropItem ( ) ;
list = lists [ $ ( this ) . attr ( "data-listidx" ) ] ;
list . draggedItem = $ ( e . target ) . closest ( opts . itemSelector ) ;
//record current position so on dragend we know if the dragged item changed position or not
list . draggedItem . attr ( "data-origpos" , $ ( this ) . attr ( "data-listidx" ) + "-" + list . getItems ( ) . index ( list . draggedItem ) ) ;
//calculate mouse offset relative to draggedItem
var mt = parseInt ( list . draggedItem . css ( "marginTop" ) ) ;
var ml = parseInt ( list . draggedItem . css ( "marginLeft" ) ) ;
list . offset = list . draggedItem . offset ( ) ;
list . offset . top = e . pageY - list . offset . top + ( isNaN ( mt ) ? 0 : mt ) - 1 ;
list . offset . left = e . pageX - list . offset . left + ( isNaN ( ml ) ? 0 : ml ) - 1 ;
//calculate box the dragged item can't be dragged outside of
if ( ! opts . dragBetween ) {
var containerHeight = $ ( list . container ) . outerHeight ( ) == 0 ? Math . max ( 1 , Math . round ( 0.5 + list . getItems ( ) . size ( ) * list . draggedItem . outerWidth ( ) / $ ( list . container ) . outerWidth ( ) ) ) * list . draggedItem . outerHeight ( ) : $ ( list . container ) . outerHeight ( ) ;
list . offsetLimit = $ ( list . container ) . offset ( ) ;
list . offsetLimit . right = list . offsetLimit . left + $ ( list . container ) . outerWidth ( ) - list . draggedItem . outerWidth ( ) ;
list . offsetLimit . bottom = list . offsetLimit . top + containerHeight - list . draggedItem . outerHeight ( ) ;
}
//create placeholder item
var h = list . draggedItem . height ( ) ;
var w = list . draggedItem . width ( ) ;
if ( opts . itemSelector == "tr" ) {
list . draggedItem . children ( ) . each ( function ( ) { $ ( this ) . width ( $ ( this ) . width ( ) ) ; } ) ;
list . placeHolderItem = list . draggedItem . clone ( ) . attr ( "data-placeholder" , true ) ;
list . draggedItem . after ( list . placeHolderItem ) ;
list . placeHolderItem . children ( ) . each ( function ( ) { $ ( this ) . css ( { borderWidth : 0 , width : $ ( this ) . width ( ) + 1 , height : $ ( this ) . height ( ) + 1 } ) . html ( " " ) ; } ) ;
} else {
list . draggedItem . after ( opts . placeHolderTemplate ) ;
list . placeHolderItem = list . draggedItem . next ( ) . css ( { height : h , width : w } ) . attr ( "data-placeholder" , true ) ;
}
if ( opts . itemSelector == "td" ) {
var listTable = list . draggedItem . closest ( "table" ) . get ( 0 ) ;
$ ( "<table id='" + listTable . id + "' style='border-width: 0px;' class='dragSortItem " + listTable . className + "'><tr></tr></table>" ) . appendTo ( "body" ) . children ( ) . append ( list . draggedItem ) ;
}
//style draggedItem while dragging
var orig = list . draggedItem . attr ( "style" ) ;
list . draggedItem . attr ( "data-origstyle" , orig ? orig : "" ) ;
list . draggedItem . css ( { position : "absolute" , opacity : 0.8 , "z-index" : 999 , height : h , width : w } ) ;
//auto-scroll setup
list . scroll = { moveX : 0 , moveY : 0 , maxX : $ ( document ) . width ( ) - $ ( window ) . width ( ) , maxY : $ ( document ) . height ( ) - $ ( window ) . height ( ) } ;
list . scroll . scrollY = window . setInterval ( function ( ) {
if ( opts . scrollContainer != window ) {
$ ( opts . scrollContainer ) . scrollTop ( $ ( opts . scrollContainer ) . scrollTop ( ) + list . scroll . moveY ) ;
return ;
}
var t = $ ( opts . scrollContainer ) . scrollTop ( ) ;
if ( list . scroll . moveY > 0 && t < list . scroll . maxY || list . scroll . moveY < 0 && t > 0 ) {
$ ( opts . scrollContainer ) . scrollTop ( t + list . scroll . moveY ) ;
list . draggedItem . css ( "top" , list . draggedItem . offset ( ) . top + list . scroll . moveY + 1 ) ;
}
} , 10 ) ;
list . scroll . scrollX = window . setInterval ( function ( ) {
if ( opts . scrollContainer != window ) {
$ ( opts . scrollContainer ) . scrollLeft ( $ ( opts . scrollContainer ) . scrollLeft ( ) + list . scroll . moveX ) ;
return ;
}
var l = $ ( opts . scrollContainer ) . scrollLeft ( ) ;
if ( list . scroll . moveX > 0 && l < list . scroll . maxX || list . scroll . moveX < 0 && l > 0 ) {
$ ( opts . scrollContainer ) . scrollLeft ( l + list . scroll . moveX ) ;
list . draggedItem . css ( "left" , list . draggedItem . offset ( ) . left + list . scroll . moveX + 1 ) ;
}
} , 10 ) ;
//misc
$ ( lists ) . each ( function ( i , l ) { l . createDropTargets ( ) ; l . buildPositionTable ( ) ; } ) ;
list . setPos ( e . pageX , e . pageY ) ;
$ ( document ) . bind ( "mousemove" , list . swapItems ) ;
$ ( document ) . bind ( "mouseup" , list . dropItem ) ;
if ( opts . scrollContainer != window )
$ ( window ) . bind ( "DOMMouseScroll mousewheel" , list . wheel ) ;
} ,
//set position of draggedItem
setPos : function ( x , y ) {
//remove mouse offset so mouse cursor remains in same place on draggedItem instead of top left corner
var top = y - this . offset . top ;
var left = x - this . offset . left ;
//limit top, left to within box draggedItem can't be dragged outside of
if ( ! opts . dragBetween ) {
top = Math . min ( this . offsetLimit . bottom , Math . max ( top , this . offsetLimit . top ) ) ;
left = Math . min ( this . offsetLimit . right , Math . max ( left , this . offsetLimit . left ) ) ;
}
//adjust top, left calculations to parent element instead of window if it's relative or absolute
this . draggedItem . parents ( ) . each ( function ( ) {
if ( $ ( this ) . css ( "position" ) != "static" && ( ! $ . browser . mozilla || $ ( this ) . css ( "display" ) != "table" ) ) {
var offset = $ ( this ) . offset ( ) ;
top -= offset . top ;
left -= offset . left ;
return false ;
}
} ) ;
//set x or y auto-scroll amount
if ( opts . scrollContainer == window ) {
y -= $ ( window ) . scrollTop ( ) ;
x -= $ ( window ) . scrollLeft ( ) ;
y = Math . max ( 0 , y - $ ( window ) . height ( ) + 5 ) + Math . min ( 0 , y - 5 ) ;
x = Math . max ( 0 , x - $ ( window ) . width ( ) + 5 ) + Math . min ( 0 , x - 5 ) ;
} else {
var cont = $ ( opts . scrollContainer ) ;
var offset = cont . offset ( ) ;
y = Math . max ( 0 , y - cont . height ( ) - offset . top ) + Math . min ( 0 , y - offset . top ) ;
x = Math . max ( 0 , x - cont . width ( ) - offset . left ) + Math . min ( 0 , x - offset . left ) ;
}
list . scroll . moveX = x == 0 ? 0 : x * opts . scrollSpeed / Math . abs ( x ) ;
list . scroll . moveY = y == 0 ? 0 : y * opts . scrollSpeed / Math . abs ( y ) ;
//move draggedItem to new mouse cursor location
this . draggedItem . css ( { top : top , left : left } ) ;
} ,
//if scroll container is a div allow mouse wheel to scroll div instead of window when mouse is hovering over
wheel : function ( e ) {
if ( ( $ . browser . safari || $ . browser . mozilla ) && list && opts . scrollContainer != window ) {
var cont = $ ( opts . scrollContainer ) ;
var offset = cont . offset ( ) ;
if ( e . pageX > offset . left && e . pageX < offset . left + cont . width ( ) && e . pageY > offset . top && e . pageY < offset . top + cont . height ( ) ) {
var delta = e . detail ? e . detail * 5 : e . wheelDelta / - 2 ;
cont . scrollTop ( cont . scrollTop ( ) + delta ) ;
e . preventDefault ( ) ;
}
}
} ,
//build a table recording all the positions of the moveable list items
buildPositionTable : function ( ) {
var pos = [ ] ;
this . getItems ( ) . not ( [ list . draggedItem [ 0 ] , list . placeHolderItem [ 0 ] ] ) . each ( function ( i ) {
var loc = $ ( this ) . offset ( ) ;
loc . right = loc . left + $ ( this ) . outerWidth ( ) ;
loc . bottom = loc . top + $ ( this ) . outerHeight ( ) ;
loc . elm = this ;
pos [ i ] = loc ;
} ) ;
this . pos = pos ;
} ,
dropItem : function ( ) {
if ( list . draggedItem == null )
return ;
//list.draggedItem.attr("style", "") doesn't work on IE8 and jQuery 1.5 or lower
//list.draggedItem.removeAttr("style") doesn't work on chrome and jQuery 1.6 (works jQuery 1.5 or lower)
var orig = list . draggedItem . attr ( "data-origstyle" ) ;
list . draggedItem . attr ( "style" , orig ) ;
if ( orig == "" )
list . draggedItem . removeAttr ( "style" ) ;
list . draggedItem . removeAttr ( "data-origstyle" ) ;
list . styleDragHandlers ( true ) ;
list . placeHolderItem . before ( list . draggedItem ) ;
list . placeHolderItem . remove ( ) ;
$ ( "[data-droptarget], .dragSortItem" ) . remove ( ) ;
window . clearInterval ( list . scroll . scrollY ) ;
window . clearInterval ( list . scroll . scrollX ) ;
//if position changed call dragEnd
if ( list . draggedItem . attr ( "data-origpos" ) != $ ( lists ) . index ( list ) + "-" + list . getItems ( ) . index ( list . draggedItem ) )
opts . dragEnd . apply ( list . draggedItem ) ;
list . draggedItem . removeAttr ( "data-origpos" ) ;
list . draggedItem = null ;
$ ( document ) . unbind ( "mousemove" , list . swapItems ) ;
$ ( document ) . unbind ( "mouseup" , list . dropItem ) ;
if ( opts . scrollContainer != window )
$ ( window ) . unbind ( "DOMMouseScroll mousewheel" , list . wheel ) ;
return false ;
} ,
//swap the draggedItem (represented visually by placeholder) with the list item the it has been dragged on top of
swapItems : function ( e ) {
if ( list . draggedItem == null )
return false ;
//move draggedItem to mouse location
list . setPos ( e . pageX , e . pageY ) ;
//retrieve list and item position mouse cursor is over
var ei = list . findPos ( e . pageX , e . pageY ) ;
var nlist = list ;
for ( var i = 0 ; ei == - 1 && opts . dragBetween && i < lists . length ; i ++ ) {
ei = lists [ i ] . findPos ( e . pageX , e . pageY ) ;
nlist = lists [ i ] ;
}
//if not over another moveable list item return
if ( ei == - 1 )
return false ;
//save fixed items locations
var children = function ( ) { return $ ( nlist . container ) . children ( ) . not ( nlist . draggedItem ) ; } ;
var fixed = children ( ) . not ( opts . itemSelector ) . each ( function ( i ) { this . idx = children ( ) . index ( this ) ; } ) ;
//if moving draggedItem up or left place placeHolder before list item the dragged item is hovering over otherwise place it after
if ( lastPos == null || lastPos . top > list . draggedItem . offset ( ) . top || lastPos . left > list . draggedItem . offset ( ) . left )
$ ( nlist . pos [ ei ] . elm ) . before ( list . placeHolderItem ) ;
else
$ ( nlist . pos [ ei ] . elm ) . after ( list . placeHolderItem ) ;
//restore fixed items location
fixed . each ( function ( ) {
var elm = children ( ) . eq ( this . idx ) . get ( 0 ) ;
if ( this != elm && children ( ) . index ( this ) < this . idx )
$ ( this ) . insertAfter ( elm ) ;
else if ( this != elm )
$ ( this ) . insertBefore ( elm ) ;
} ) ;
//misc
$ ( lists ) . each ( function ( i , l ) { l . createDropTargets ( ) ; l . buildPositionTable ( ) ; } ) ;
lastPos = list . draggedItem . offset ( ) ;
return false ;
} ,
//returns the index of the list item the mouse is over
findPos : function ( x , y ) {
for ( var i = 0 ; i < this . pos . length ; i ++ ) {
if ( this . pos [ i ] . left < x && this . pos [ i ] . right > x && this . pos [ i ] . top < y && this . pos [ i ] . bottom > y )
return i ;
}
return - 1 ;
} ,
//create drop targets which are placeholders at the end of other lists to allow dragging straight to the last position
createDropTargets : function ( ) {
if ( ! opts . dragBetween )
return ;
$ ( lists ) . each ( function ( ) {
var ph = $ ( this . container ) . find ( "[data-placeholder]" ) ;
var dt = $ ( this . container ) . find ( "[data-droptarget]" ) ;
if ( ph . size ( ) > 0 && dt . size ( ) > 0 )
dt . remove ( ) ;
else if ( ph . size ( ) == 0 && dt . size ( ) == 0 ) {
if ( opts . itemSelector == "td" )
$ ( opts . placeHolderTemplate ) . attr ( "data-droptarget" , true ) . appendTo ( this . container ) ;
else
//list.placeHolderItem.clone().removeAttr("data-placeholder") crashes in IE7 and jquery 1.5.1 (doesn't in jquery 1.4.2 or IE8)
$ ( this . container ) . append ( list . placeHolderItem . removeAttr ( "data-placeholder" ) . clone ( ) . attr ( "data-droptarget" , true ) ) ;
list . placeHolderItem . attr ( "data-placeholder" , true ) ;
}
} ) ;
}
} ;
newList . init ( ) ;
lists . push ( newList ) ;
} ) ;
return this ;
} ;
$ . fn . dragsort . defaults = {
itemSelector : "" ,
dragSelector : "" ,
dragSelectorExclude : "input, textarea" ,
dragEnd : function ( ) { } ,
dragBetween : false ,
placeHolderTemplate : "" ,
scrollContainer : window ,
scrollSpeed : 5
} ;
} ) ( jQuery ) ;