2018-12-20 04:08:22 -05:00
/ * * !
* Sortable
* @ author RubaXa < trash @ rubaxa . org >
* @ author owenm < owen23355 @ gmail . com >
* @ license MIT
* /
( function sortableModule ( factory ) {
"use strict" ;
if ( typeof define === "function" && define . amd ) {
define ( factory ) ;
}
else if ( typeof module != "undefined" && typeof module . exports != "undefined" ) {
module . exports = factory ( ) ;
}
else {
/* jshint sub:true */
window [ "Sortable" ] = factory ( ) ;
}
} ) ( function sortableFactory ( ) {
"use strict" ;
if ( typeof window === "undefined" || ! window . document ) {
return function sortableError ( ) {
throw new Error ( "Sortable.js requires a window with a document" ) ;
} ;
}
var dragEl ,
parentEl ,
ghostEl ,
cloneEl ,
rootEl ,
nextEl ,
lastDownEl ,
scrollEl ,
scrollParentEl ,
scrollCustomFn ,
oldIndex ,
newIndex ,
activeGroup ,
putSortable ,
autoScrolls = [ ] ,
scrolling = false ,
2019-01-23 06:23:14 -05:00
awaitingDragStarted = false ,
ignoreNextClick = false ,
sortables = [ ] ,
2018-12-20 04:08:22 -05:00
pointerElemChangedInterval ,
lastPointerElemX ,
lastPointerElemY ,
tapEvt ,
touchEvt ,
moved ,
2019-01-23 06:23:14 -05:00
2018-12-20 04:08:22 -05:00
lastTarget ,
lastDirection ,
pastFirstInvertThresh = false ,
isCircumstantialInvert = false ,
2019-01-23 06:23:14 -05:00
lastMode , // 'swap' or 'insert'
targetMoveDistance ,
2018-12-20 04:08:22 -05:00
forRepaintDummy ,
realDragElRect , // dragEl rect after current animation
/** @const */
R _SPACE = /\s+/g ,
expando = 'Sortable' + ( new Date ) . getTime ( ) ,
win = window ,
document = win . document ,
parseInt = win . parseInt ,
setTimeout = win . setTimeout ,
$ = win . jQuery || win . Zepto ,
Polymer = win . Polymer ,
captureMode = {
capture : false ,
passive : false
} ,
2019-01-23 06:23:14 -05:00
IE11OrLess = ! ! navigator . userAgent . match ( /(?:Trident.*rv[ :]?11\.|msie|iemobile)/i ) ,
Edge = ! ! navigator . userAgent . match ( /Edge/i ) ,
// FireFox = !!navigator.userAgent.match(/firefox/i),
CSSFloatProperty = Edge || IE11OrLess ? 'cssFloat' : 'float' ,
// This will not pass for IE9, because IE9 DnD only works on anchors
2018-12-20 04:08:22 -05:00
supportDraggable = ( 'draggable' in document . createElement ( 'div' ) ) ,
2019-01-23 06:23:14 -05:00
supportCssPointerEvents = ( function ( ) {
// false when <= IE11
if ( IE11OrLess ) {
2018-12-20 04:08:22 -05:00
return false ;
}
2019-01-23 06:23:14 -05:00
var el = document . createElement ( 'x' ) ;
2018-12-20 04:08:22 -05:00
el . style . cssText = 'pointer-events:auto' ;
return el . style . pointerEvents === 'auto' ;
} ) ( ) ,
_silent = false ,
_alignedSilent = false ,
abs = Math . abs ,
min = Math . min ,
savedInputChecked = [ ] ,
_detectDirection = function ( el , options ) {
var elCSS = _css ( el ) ,
elWidth = parseInt ( elCSS . width ) ,
child1 = _getChild ( el , 0 , options ) ,
child2 = _getChild ( el , 1 , options ) ,
firstChildCSS = child1 && _css ( child1 ) ,
secondChildCSS = child2 && _css ( child2 ) ,
2019-01-23 06:23:14 -05:00
firstChildWidth = firstChildCSS && parseInt ( firstChildCSS . marginLeft ) + parseInt ( firstChildCSS . marginRight ) + _getRect ( child1 ) . width ,
secondChildWidth = secondChildCSS && parseInt ( secondChildCSS . marginLeft ) + parseInt ( secondChildCSS . marginRight ) + _getRect ( child2 ) . width ;
2018-12-20 04:08:22 -05:00
if ( elCSS . display === 'flex' ) {
return elCSS . flexDirection === 'column' || elCSS . flexDirection === 'column-reverse'
? 'vertical' : 'horizontal' ;
}
2019-02-27 08:01:44 -05:00
if ( child1 && firstChildCSS . float !== 'none' ) {
var touchingSideChild2 = firstChildCSS . float === 'left' ? 'left' : 'right' ;
return child2 && ( secondChildCSS . clear === 'both' || secondChildCSS . clear === touchingSideChild2 ) ?
'vertical' : 'horizontal' ;
}
2018-12-20 04:08:22 -05:00
return ( child1 &&
(
firstChildCSS . display === 'block' ||
2019-01-23 06:23:14 -05:00
firstChildCSS . display === 'flex' ||
firstChildCSS . display === 'table' ||
2018-12-20 04:08:22 -05:00
firstChildCSS . display === 'grid' ||
firstChildWidth >= elWidth &&
2019-01-23 06:23:14 -05:00
elCSS [ CSSFloatProperty ] === 'none' ||
2018-12-20 04:08:22 -05:00
child2 &&
2019-01-23 06:23:14 -05:00
elCSS [ CSSFloatProperty ] === 'none' &&
2018-12-20 04:08:22 -05:00
firstChildWidth + secondChildWidth > elWidth
) ?
'vertical' : 'horizontal'
) ;
} ,
2019-01-23 06:23:14 -05:00
/ * *
* Detects first nearest empty sortable to X and Y position using emptyInsertThreshold .
* @ param { Number } x X position
* @ param { Number } y Y position
* @ return { HTMLElement } Element of the first found nearest Sortable
* /
_detectNearestEmptySortable = function ( x , y ) {
for ( var i = 0 ; i < sortables . length ; i ++ ) {
if ( sortables [ i ] . children . length ) continue ;
var rect = _getRect ( sortables [ i ] ) ,
threshold = sortables [ i ] [ expando ] . options . emptyInsertThreshold ,
insideHorizontally = x >= ( rect . left - threshold ) && x <= ( rect . right + threshold ) ,
insideVertically = y >= ( rect . top - threshold ) && y <= ( rect . bottom + threshold ) ;
if ( insideHorizontally && insideVertically ) {
return sortables [ i ] ;
}
}
} ,
_isClientInRowColumn = function ( x , y , el , axis , options ) {
var targetRect = _getRect ( el ) ,
2018-12-20 04:08:22 -05:00
targetS1Opp = axis === 'vertical' ? targetRect . left : targetRect . top ,
targetS2Opp = axis === 'vertical' ? targetRect . right : targetRect . bottom ,
2019-01-23 06:23:14 -05:00
mouseOnOppAxis = axis === 'vertical' ? x : y ;
2018-12-20 04:08:22 -05:00
return targetS1Opp < mouseOnOppAxis && mouseOnOppAxis < targetS2Opp ;
} ,
2019-01-23 06:23:14 -05:00
_isElInRowColumn = function ( el1 , el2 , axis ) {
var el1Rect = el1 === dragEl && realDragElRect || _getRect ( el1 ) ,
el2Rect = el2 === dragEl && realDragElRect || _getRect ( el2 ) ,
el1S1Opp = axis === 'vertical' ? el1Rect . left : el1Rect . top ,
el1S2Opp = axis === 'vertical' ? el1Rect . right : el1Rect . bottom ,
el1OppLength = axis === 'vertical' ? el1Rect . width : el1Rect . height ,
el2S1Opp = axis === 'vertical' ? el2Rect . left : el2Rect . top ,
el2S2Opp = axis === 'vertical' ? el2Rect . right : el2Rect . bottom ,
el2OppLength = axis === 'vertical' ? el2Rect . width : el2Rect . height ;
return (
el1S1Opp === el2S1Opp ||
el1S2Opp === el2S2Opp ||
( el1S1Opp + el1OppLength / 2 ) === ( el2S1Opp + el2OppLength / 2 )
) ;
} ,
2018-12-20 04:08:22 -05:00
_getParentAutoScrollElement = function ( el , includeSelf ) {
// skip to window
if ( ! el || ! el . getBoundingClientRect ) return win ;
var elem = el ;
var gotSelf = false ;
do {
// we don't need to get elem css if it isn't even overflowing in the first place (performance)
if ( elem . clientWidth < elem . scrollWidth || elem . clientHeight < elem . scrollHeight ) {
var elemCSS = _css ( elem ) ;
if (
elem . clientWidth < elem . scrollWidth && ( elemCSS . overflowX == 'auto' || elemCSS . overflowX == 'scroll' ) ||
elem . clientHeight < elem . scrollHeight && ( elemCSS . overflowY == 'auto' || elemCSS . overflowY == 'scroll' )
) {
if ( ! elem || ! elem . getBoundingClientRect || elem === document . body ) return win ;
if ( gotSelf || includeSelf ) return elem ;
gotSelf = true ;
}
}
/* jshint boss:true */
} while ( elem = elem . parentNode ) ;
return win ;
} ,
_autoScroll = _throttle ( function ( /**Event*/ evt , /**Object*/ options , /**HTMLElement*/ rootEl , /**Boolean*/ isFallback ) {
// Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521
if ( options . scroll ) {
var _this = rootEl ? rootEl [ expando ] : window ,
sens = options . scrollSensitivity ,
speed = options . scrollSpeed ,
x = evt . clientX ,
y = evt . clientY ,
winWidth = window . innerWidth ,
winHeight = window . innerHeight ,
2019-01-23 06:23:14 -05:00
scrollThisInstance = false ;
2018-12-20 04:08:22 -05:00
// Detect scrollEl
if ( scrollParentEl !== rootEl ) {
_clearAutoScrolls ( ) ;
scrollEl = options . scroll ;
scrollCustomFn = options . scrollFn ;
if ( scrollEl === true ) {
scrollEl = _getParentAutoScrollElement ( rootEl , true ) ;
scrollParentEl = scrollEl ;
}
}
var layersOut = 0 ;
var currentParent = scrollEl ;
do {
2019-01-23 06:23:14 -05:00
var el = currentParent ,
rect = _getRect ( el ) ,
top = rect . top ,
bottom = rect . bottom ,
left = rect . left ,
right = rect . right ,
width = rect . width ,
height = rect . height ,
scrollWidth ,
scrollHeight ,
css ,
vx ,
vy ,
canScrollX ,
canScrollY ,
scrollPosX ,
scrollPosY ;
if ( el !== win ) {
scrollWidth = el . scrollWidth ;
scrollHeight = el . scrollHeight ;
2018-12-20 04:08:22 -05:00
css = _css ( el ) ;
2019-01-23 06:23:14 -05:00
canScrollX = width < scrollWidth && ( css . overflowX === 'auto' || css . overflowX === 'scroll' ) ;
canScrollY = height < scrollHeight && ( css . overflowY === 'auto' || css . overflowY === 'scroll' ) ;
scrollPosX = el . scrollLeft ;
scrollPosY = el . scrollTop ;
} else {
scrollWidth = document . documentElement . scrollWidth ;
scrollHeight = document . documentElement . scrollHeight ;
css = _css ( document . documentElement ) ;
canScrollX = width < scrollWidth && ( css . overflowX === 'auto' || css . overflowX === 'scroll' || css . overflowX === 'visible' ) ;
canScrollY = height < scrollHeight && ( css . overflowY === 'auto' || css . overflowY === 'scroll' || css . overflowY === 'visible' ) ;
scrollPosX = document . documentElement . scrollLeft ;
scrollPosY = document . documentElement . scrollTop ;
2018-12-20 04:08:22 -05:00
}
2019-01-23 06:23:14 -05:00
vx = canScrollX && ( abs ( right - x ) <= sens && ( scrollPosX + width ) < scrollWidth ) - ( abs ( left - x ) <= sens && ! ! scrollPosX ) ;
vy = canScrollY && ( abs ( bottom - y ) <= sens && ( scrollPosY + height ) < scrollHeight ) - ( abs ( top - y ) <= sens && ! ! scrollPosY ) ;
2018-12-20 04:08:22 -05:00
if ( ! autoScrolls [ layersOut ] ) {
for ( var i = 0 ; i <= layersOut ; i ++ ) {
if ( ! autoScrolls [ i ] ) {
autoScrolls [ i ] = { } ;
}
}
}
if ( autoScrolls [ layersOut ] . vx != vx || autoScrolls [ layersOut ] . vy != vy || autoScrolls [ layersOut ] . el !== el ) {
autoScrolls [ layersOut ] . el = el ;
autoScrolls [ layersOut ] . vx = vx ;
autoScrolls [ layersOut ] . vy = vy ;
clearInterval ( autoScrolls [ layersOut ] . pid ) ;
if ( el && ( vx != 0 || vy != 0 ) ) {
scrollThisInstance = true ;
/* jshint loopfunc:true */
autoScrolls [ layersOut ] . pid = setInterval ( ( function ( ) {
// emulate drag over during autoscroll (fallback), emulating native DnD behaviour
if ( isFallback && this . layer === 0 ) {
Sortable . active . _emulateDragOver ( true ) ;
}
var scrollOffsetY = autoScrolls [ this . layer ] . vy ? autoScrolls [ this . layer ] . vy * speed : 0 ;
var scrollOffsetX = autoScrolls [ this . layer ] . vx ? autoScrolls [ this . layer ] . vx * speed : 0 ;
if ( 'function' === typeof ( scrollCustomFn ) ) {
if ( scrollCustomFn . call ( _this , scrollOffsetX , scrollOffsetY , evt , touchEvt , autoScrolls [ this . layer ] . el ) !== 'continue' ) {
return ;
}
}
if ( autoScrolls [ this . layer ] . el === win ) {
win . scrollTo ( win . pageXOffset + scrollOffsetX , win . pageYOffset + scrollOffsetY ) ;
} else {
autoScrolls [ this . layer ] . el . scrollTop += scrollOffsetY ;
autoScrolls [ this . layer ] . el . scrollLeft += scrollOffsetX ;
}
} ) . bind ( { layer : layersOut } ) , 24 ) ;
}
}
layersOut ++ ;
} while ( options . bubbleScroll && currentParent !== win && ( currentParent = _getParentAutoScrollElement ( currentParent , false ) ) ) ;
scrolling = scrollThisInstance ; // in case another function catches scrolling as false in between when it is not
}
} , 30 ) ,
_clearAutoScrolls = function ( ) {
autoScrolls . forEach ( function ( autoScroll ) {
clearInterval ( autoScroll . pid ) ;
} ) ;
autoScrolls = [ ] ;
} ,
_prepareGroup = function ( options ) {
function toFn ( value , pull ) {
return function ( to , from , dragEl , evt ) {
2019-01-23 06:23:14 -05:00
var sameGroup = to . options . group . name &&
from . options . group . name &&
to . options . group . name === from . options . group . name ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
if ( value == null && ( pull || sameGroup ) ) {
// Default pull value
// Default pull and put value if same group
return true ;
2018-12-20 04:08:22 -05:00
} else if ( value == null || value === false ) {
2019-01-23 06:23:14 -05:00
return false ;
2018-12-20 04:08:22 -05:00
} else if ( pull && value === 'clone' ) {
2019-01-23 06:23:14 -05:00
return value ;
2018-12-20 04:08:22 -05:00
} else if ( typeof value === 'function' ) {
2019-01-23 06:23:14 -05:00
return toFn ( value ( to , from , dragEl , evt ) , pull ) ( to , from , dragEl , evt ) ;
2018-12-20 04:08:22 -05:00
} else {
var otherGroup = ( pull ? to : from ) . options . group . name ;
2019-01-23 06:23:14 -05:00
return ( value === true ||
2018-12-20 04:08:22 -05:00
( typeof value === 'string' && value === otherGroup ) ||
( value . join && value . indexOf ( otherGroup ) > - 1 ) ) ;
}
} ;
}
var group = { } ;
var originalGroup = options . group ;
if ( ! originalGroup || typeof originalGroup != 'object' ) {
originalGroup = { name : originalGroup } ;
}
group . name = originalGroup . name ;
group . checkPull = toFn ( originalGroup . pull , true ) ;
group . checkPut = toFn ( originalGroup . put ) ;
group . revertClone = originalGroup . revertClone ;
options . group = group ;
} ,
_checkAlignment = function ( evt ) {
2019-01-23 06:23:14 -05:00
if ( ! dragEl || ! dragEl . parentNode ) return ;
2018-12-20 04:08:22 -05:00
dragEl . parentNode [ expando ] && dragEl . parentNode [ expando ] . _computeIsAligned ( evt ) ;
2019-01-23 06:23:14 -05:00
} ,
_isTrueParentSortable = function ( el , target ) {
var trueParent = target ;
while ( ! trueParent [ expando ] ) {
trueParent = trueParent . parentNode ;
}
return el === trueParent ;
} ,
_artificalBubble = function ( sortable , originalEvt , method ) {
// Artificial IE bubbling
var nextParent = sortable . parentNode ;
while ( nextParent && ! nextParent [ expando ] ) {
nextParent = nextParent . parentNode ;
}
if ( nextParent ) {
nextParent [ expando ] [ method ] ( _extend ( originalEvt , {
artificialBubble : true
} ) ) ;
}
} ,
_hideGhostForTarget = function ( ) {
if ( ! supportCssPointerEvents && ghostEl ) {
_css ( ghostEl , 'display' , 'none' ) ;
}
} ,
_unhideGhostForTarget = function ( ) {
if ( ! supportCssPointerEvents && ghostEl ) {
_css ( ghostEl , 'display' , '' ) ;
}
} ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
// #1184 fix - Prevent click event on fallback if dragged but item not changed position
document . addEventListener ( 'click' , function ( evt ) {
if ( ignoreNextClick ) {
evt . preventDefault ( ) ;
evt . stopPropagation && evt . stopPropagation ( ) ;
evt . stopImmediatePropagation && evt . stopImmediatePropagation ( ) ;
ignoreNextClick = false ;
return false ;
}
} , true ) ;
var nearestEmptyInsertDetectEvent = function ( evt ) {
2019-02-27 08:01:44 -05:00
evt = evt . touches ? evt . touches [ 0 ] : evt ;
2019-01-23 06:23:14 -05:00
if ( dragEl ) {
var nearest = _detectNearestEmptySortable ( evt . clientX , evt . clientY ) ;
if ( nearest ) {
nearest [ expando ] . _onDragOver ( {
clientX : evt . clientX ,
clientY : evt . clientY ,
target : nearest ,
rootEl : nearest
} ) ;
}
}
} ;
// We do not want this to be triggered if completed (bubbling canceled), so only define it here
2019-02-27 08:01:44 -05:00
_on ( document , 'dragover' , nearestEmptyInsertDetectEvent ) ;
_on ( document , 'mousemove' , nearestEmptyInsertDetectEvent ) ;
_on ( document , 'touchmove' , nearestEmptyInsertDetectEvent ) ;
2019-01-23 06:23:14 -05:00
2018-12-20 04:08:22 -05:00
/ * *
* @ class Sortable
* @ param { HTMLElement } el
* @ param { Object } [ options ]
* /
function Sortable ( el , options ) {
if ( ! ( el && el . nodeType && el . nodeType === 1 ) ) {
2019-01-23 06:23:14 -05:00
throw 'Sortable: `el` must be HTMLElement, not ' + { } . toString . call ( el ) ;
2018-12-20 04:08:22 -05:00
}
this . el = el ; // root element
this . options = options = _extend ( { } , options ) ;
// Export instance
el [ expando ] = this ;
// Default options
var defaults = {
group : null ,
sort : true ,
disabled : false ,
store : null ,
handle : null ,
scroll : true ,
scrollSensitivity : 30 ,
scrollSpeed : 10 ,
bubbleScroll : true ,
2019-01-25 01:30:56 -05:00
draggable : /[uo]l/i . test ( el . nodeName ) ? '>li' : '>*' ,
2018-12-20 04:08:22 -05:00
swapThreshold : 1 , // percentage; 0 <= x <= 1
invertSwap : false , // invert always
invertedSwapThreshold : null , // will be set to same as swapThreshold if default
2019-01-23 06:23:14 -05:00
removeCloneOnHide : true ,
direction : function ( ) {
return _detectDirection ( el , this . options ) ;
} ,
2018-12-20 04:08:22 -05:00
ghostClass : 'sortable-ghost' ,
chosenClass : 'sortable-chosen' ,
dragClass : 'sortable-drag' ,
ignore : 'a, img' ,
filter : null ,
preventOnFilter : true ,
animation : 0 ,
2019-01-23 06:23:14 -05:00
easing : null ,
2018-12-20 04:08:22 -05:00
setData : function ( dataTransfer , dragEl ) {
dataTransfer . setData ( 'Text' , dragEl . textContent ) ;
} ,
dropBubble : false ,
dragoverBubble : false ,
dataIdAttr : 'data-id' ,
delay : 0 ,
touchStartThreshold : parseInt ( window . devicePixelRatio , 10 ) || 1 ,
forceFallback : false ,
fallbackClass : 'sortable-fallback' ,
fallbackOnBody : false ,
fallbackTolerance : 0 ,
fallbackOffset : { x : 0 , y : 0 } ,
supportPointer : Sortable . supportPointer !== false && (
( 'PointerEvent' in window ) ||
window . navigator && ( 'msPointerEnabled' in window . navigator ) // microsoft
2019-01-23 06:23:14 -05:00
) ,
emptyInsertThreshold : 5
2018-12-20 04:08:22 -05:00
} ;
// Set default options
for ( var name in defaults ) {
! ( name in options ) && ( options [ name ] = defaults [ name ] ) ;
}
_prepareGroup ( options ) ;
// Bind all private methods
for ( var fn in this ) {
if ( fn . charAt ( 0 ) === '_' && typeof this [ fn ] === 'function' ) {
this [ fn ] = this [ fn ] . bind ( this ) ;
}
}
// Setup drag mode
this . nativeDraggable = options . forceFallback ? false : supportDraggable ;
// Bind events
2019-01-23 06:23:14 -05:00
if ( options . supportPointer ) {
_on ( el , 'pointerdown' , this . _onTapStart ) ;
} else {
_on ( el , 'mousedown' , this . _onTapStart ) ;
_on ( el , 'touchstart' , this . _onTapStart ) ;
}
2018-12-20 04:08:22 -05:00
if ( this . nativeDraggable ) {
_on ( el , 'dragover' , this ) ;
_on ( el , 'dragenter' , this ) ;
}
2019-01-23 06:23:14 -05:00
sortables . push ( this . el ) ;
2018-12-20 04:08:22 -05:00
// Restore sorting
options . store && options . store . get && this . sort ( options . store . get ( this ) || [ ] ) ;
}
Sortable . prototype = /** @lends Sortable.prototype */ {
constructor : Sortable ,
2019-01-23 06:23:14 -05:00
_computeIsAligned : function ( evt ) {
var target ;
if ( ghostEl && ! supportCssPointerEvents ) {
_hideGhostForTarget ( ) ;
target = document . elementFromPoint ( evt . clientX , evt . clientY ) ;
_unhideGhostForTarget ( ) ;
} else {
target = evt . target ;
}
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
target = _closest ( target , this . options . draggable , this . el , false ) ;
2018-12-20 04:08:22 -05:00
if ( _alignedSilent ) return ;
if ( ! dragEl || dragEl . parentNode !== this . el ) return ;
2019-01-23 06:23:14 -05:00
var children = this . el . children ;
for ( var i = 0 ; i < children . length ; i ++ ) {
// Don't change for target in case it is changed to aligned before onDragOver is fired
if ( _closest ( children [ i ] , this . options . draggable , this . el , false ) && children [ i ] !== target ) {
children [ i ] . sortableMouseAligned = _isClientInRowColumn ( evt . clientX , evt . clientY , children [ i ] , this . _getDirection ( evt , null ) , this . options ) ;
}
2018-12-20 04:08:22 -05:00
}
2019-01-23 06:23:14 -05:00
// Used for nulling last target when not in element, nothing to do with checking if aligned
if ( ! _closest ( target , this . options . draggable , this . el , true ) ) {
lastTarget = null ;
}
2018-12-20 04:08:22 -05:00
_alignedSilent = true ;
setTimeout ( function ( ) {
_alignedSilent = false ;
} , 30 ) ;
2019-01-23 06:23:14 -05:00
2018-12-20 04:08:22 -05:00
} ,
_getDirection : function ( evt , target ) {
return ( typeof this . options . direction === 'function' ) ? this . options . direction . call ( this , evt , target , dragEl ) : this . options . direction ;
} ,
_onTapStart : function ( /** Event|TouchEvent */ evt ) {
2019-01-23 06:23:14 -05:00
if ( ! evt . cancelable ) return ;
2018-12-20 04:08:22 -05:00
var _this = this ,
el = this . el ,
options = this . options ,
preventOnFilter = options . preventOnFilter ,
type = evt . type ,
touch = evt . touches && evt . touches [ 0 ] ,
target = ( touch || evt ) . target ,
originalTarget = evt . target . shadowRoot && ( ( evt . path && evt . path [ 0 ] ) || ( evt . composedPath && evt . composedPath ( ) [ 0 ] ) ) || target ,
filter = options . filter ,
startIndex ;
_saveInputCheckedState ( el ) ;
2019-01-23 06:23:14 -05:00
// IE: Calls events in capture mode if event element is nested. This ensures only correct element's _onTapStart goes through.
// This process is also done in _onDragOver
if ( IE11OrLess && ! evt . artificialBubble && ! _isTrueParentSortable ( el , target ) ) {
return ;
}
2018-12-20 04:08:22 -05:00
// Don't trigger start event when an element is been dragged, otherwise the evt.oldindex always wrong when set option.group.
if ( dragEl ) {
return ;
}
if ( /mousedown|pointerdown/ . test ( type ) && evt . button !== 0 || options . disabled ) {
2019-01-23 06:23:14 -05:00
return ; // only left button and enabled
2018-12-20 04:08:22 -05:00
}
// cancel dnd if original target is content editable
if ( originalTarget . isContentEditable ) {
return ;
}
2019-01-23 06:23:14 -05:00
target = _closest ( target , options . draggable , el , false ) ;
2018-12-20 04:08:22 -05:00
if ( ! target ) {
2019-01-23 06:23:14 -05:00
if ( IE11OrLess ) {
_artificalBubble ( el , evt , '_onTapStart' ) ;
}
2018-12-20 04:08:22 -05:00
return ;
}
if ( lastDownEl === target ) {
// Ignoring duplicate `down`
return ;
}
// Get the index of the dragged element within its parent
startIndex = _index ( target , options . draggable ) ;
// Check filter
if ( typeof filter === 'function' ) {
if ( filter . call ( this , evt , target , this ) ) {
_dispatchEvent ( _this , originalTarget , 'filter' , target , el , el , startIndex ) ;
preventOnFilter && evt . cancelable && evt . preventDefault ( ) ;
return ; // cancel dnd
}
}
else if ( filter ) {
filter = filter . split ( ',' ) . some ( function ( criteria ) {
criteria = _closest ( originalTarget , criteria . trim ( ) , el , false ) ;
if ( criteria ) {
_dispatchEvent ( _this , criteria , 'filter' , target , el , el , startIndex ) ;
return true ;
}
} ) ;
if ( filter ) {
preventOnFilter && evt . cancelable && evt . preventDefault ( ) ;
return ; // cancel dnd
}
}
if ( options . handle && ! _closest ( originalTarget , options . handle , el , false ) ) {
return ;
}
// Prepare `dragstart`
this . _prepareDragStart ( evt , touch , target , startIndex ) ;
} ,
_handleAutoScroll : function ( evt , fallback ) {
if ( ! dragEl || ! this . options . scroll ) return ;
var x = evt . clientX ,
y = evt . clientY ,
elem = document . elementFromPoint ( x , y ) ,
2019-01-23 06:23:14 -05:00
_this = this ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
// IE does not seem to have native autoscroll,
// Edge's autoscroll seems too conditional,
// Firefox and Chrome are good
if ( fallback || Edge || IE11OrLess ) {
_autoScroll ( evt , _this . options , elem , fallback ) ;
2018-12-20 04:08:22 -05:00
// Listener for pointer element change
var ogElemScroller = _getParentAutoScrollElement ( elem , true ) ;
if (
scrolling &&
(
! pointerElemChangedInterval ||
x !== lastPointerElemX ||
y !== lastPointerElemY
)
) {
pointerElemChangedInterval && clearInterval ( pointerElemChangedInterval ) ;
// Detect for pointer elem change, emulating native DnD behaviour
pointerElemChangedInterval = setInterval ( function ( ) {
if ( ! dragEl ) return ;
// could also check if scroll direction on newElem changes due to parent autoscrolling
var newElem = _getParentAutoScrollElement ( document . elementFromPoint ( x , y ) , true ) ;
if ( newElem !== ogElemScroller ) {
ogElemScroller = newElem ;
_clearAutoScrolls ( ) ;
2019-01-23 06:23:14 -05:00
_autoScroll ( evt , _this . options , ogElemScroller , fallback ) ;
2018-12-20 04:08:22 -05:00
}
} , 10 ) ;
lastPointerElemX = x ;
lastPointerElemY = y ;
}
} else {
2019-01-23 06:23:14 -05:00
// if DnD is enabled (and browser has good autoscrolling), first autoscroll will already scroll, so get parent autoscroll of first autoscroll
2018-12-20 04:08:22 -05:00
if ( ! _this . options . bubbleScroll || _getParentAutoScrollElement ( elem , true ) === window ) {
_clearAutoScrolls ( ) ;
return ;
}
2019-01-23 06:23:14 -05:00
_autoScroll ( evt , _this . options , _getParentAutoScrollElement ( elem , false ) , false ) ;
2018-12-20 04:08:22 -05:00
}
} ,
_prepareDragStart : function ( /** Event */ evt , /** Touch */ touch , /** HTMLElement */ target , /** Number */ startIndex ) {
var _this = this ,
el = _this . el ,
options = _this . options ,
ownerDocument = el . ownerDocument ,
dragStartFn ;
if ( target && ! dragEl && ( target . parentNode === el ) ) {
rootEl = el ;
dragEl = target ;
parentEl = dragEl . parentNode ;
nextEl = dragEl . nextSibling ;
lastDownEl = target ;
activeGroup = options . group ;
oldIndex = startIndex ;
2019-01-23 06:23:14 -05:00
tapEvt = {
target : dragEl ,
clientX : ( touch || evt ) . clientX ,
clientY : ( touch || evt ) . clientY
} ;
2018-12-20 04:08:22 -05:00
this . _lastX = ( touch || evt ) . clientX ;
this . _lastY = ( touch || evt ) . clientY ;
dragEl . style [ 'will-change' ] = 'all' ;
2019-01-23 06:23:14 -05:00
// undo animation if needed
dragEl . style . transition = '' ;
dragEl . style . transform = '' ;
2018-12-20 04:08:22 -05:00
dragStartFn = function ( ) {
// Delayed drag has been triggered
// we can re-enable the events: touchmove/mousemove
_this . _disableDelayedDrag ( ) ;
// Make the element draggable
dragEl . draggable = _this . nativeDraggable ;
// Bind the events: dragstart/dragend
_this . _triggerDragStart ( evt , touch ) ;
// Drag start event
_dispatchEvent ( _this , rootEl , 'choose' , dragEl , rootEl , rootEl , oldIndex ) ;
// Chosen item
_toggleClass ( dragEl , options . chosenClass , true ) ;
} ;
// Disable "draggable"
options . ignore . split ( ',' ) . forEach ( function ( criteria ) {
_find ( dragEl , criteria . trim ( ) , _disableDraggable ) ;
} ) ;
2019-01-23 06:23:14 -05:00
if ( options . supportPointer ) {
_on ( ownerDocument , 'pointerup' , _this . _onDrop ) ;
} else {
_on ( ownerDocument , 'mouseup' , _this . _onDrop ) ;
_on ( ownerDocument , 'touchend' , _this . _onDrop ) ;
_on ( ownerDocument , 'touchcancel' , _this . _onDrop ) ;
}
2018-12-20 04:08:22 -05:00
if ( options . delay ) {
// If the user moves the pointer or let go the click or touch
// before the delay has been reached:
// disable the delayed drag
_on ( ownerDocument , 'mouseup' , _this . _disableDelayedDrag ) ;
_on ( ownerDocument , 'touchend' , _this . _disableDelayedDrag ) ;
_on ( ownerDocument , 'touchcancel' , _this . _disableDelayedDrag ) ;
_on ( ownerDocument , 'mousemove' , _this . _delayedDragTouchMoveHandler ) ;
_on ( ownerDocument , 'touchmove' , _this . _delayedDragTouchMoveHandler ) ;
options . supportPointer && _on ( ownerDocument , 'pointermove' , _this . _delayedDragTouchMoveHandler ) ;
2019-01-23 06:23:14 -05:00
_this . _dragStartTimer = setTimeout ( dragStartFn , options . delay ) ;
2018-12-20 04:08:22 -05:00
} else {
dragStartFn ( ) ;
}
}
} ,
_delayedDragTouchMoveHandler : function ( /** TouchEvent|PointerEvent **/ e ) {
var touch = e . touches ? e . touches [ 0 ] : e ;
if ( min ( abs ( touch . clientX - this . _lastX ) , abs ( touch . clientY - this . _lastY ) )
>= this . options . touchStartThreshold
) {
this . _disableDelayedDrag ( ) ;
}
} ,
_disableDelayedDrag : function ( ) {
var ownerDocument = this . el . ownerDocument ;
clearTimeout ( this . _dragStartTimer ) ;
_off ( ownerDocument , 'mouseup' , this . _disableDelayedDrag ) ;
_off ( ownerDocument , 'touchend' , this . _disableDelayedDrag ) ;
_off ( ownerDocument , 'touchcancel' , this . _disableDelayedDrag ) ;
_off ( ownerDocument , 'mousemove' , this . _delayedDragTouchMoveHandler ) ;
_off ( ownerDocument , 'touchmove' , this . _delayedDragTouchMoveHandler ) ;
_off ( ownerDocument , 'pointermove' , this . _delayedDragTouchMoveHandler ) ;
} ,
_triggerDragStart : function ( /** Event */ evt , /** Touch */ touch ) {
touch = touch || ( evt . pointerType == 'touch' ? evt : null ) ;
2019-01-23 06:23:14 -05:00
if ( ! this . nativeDraggable || touch ) {
if ( this . options . supportPointer ) {
_on ( document , 'pointermove' , this . _onTouchMove ) ;
} else if ( touch ) {
_on ( document , 'touchmove' , this . _onTouchMove ) ;
} else {
_on ( document , 'mousemove' , this . _onTouchMove ) ;
}
} else {
2018-12-20 04:08:22 -05:00
_on ( dragEl , 'dragend' , this ) ;
_on ( rootEl , 'dragstart' , this . _onDragStart ) ;
}
try {
if ( document . selection ) {
// Timeout neccessary for IE9
_nextTick ( function ( ) {
document . selection . empty ( ) ;
} ) ;
} else {
window . getSelection ( ) . removeAllRanges ( ) ;
}
} catch ( err ) {
}
} ,
2019-01-23 06:23:14 -05:00
_dragStarted : function ( fallback ) {
awaitingDragStarted = false ;
2018-12-20 04:08:22 -05:00
if ( rootEl && dragEl ) {
if ( this . nativeDraggable ) {
_on ( document , 'dragover' , this . _handleAutoScroll ) ;
_on ( document , 'dragover' , _checkAlignment ) ;
}
var options = this . options ;
// Apply effect
2019-01-23 06:23:14 -05:00
! fallback && _toggleClass ( dragEl , options . dragClass , false ) ;
2018-12-20 04:08:22 -05:00
_toggleClass ( dragEl , options . ghostClass , true ) ;
2019-01-23 06:23:14 -05:00
// In case dragging an animated element
2018-12-20 04:08:22 -05:00
_css ( dragEl , 'transform' , '' ) ;
Sortable . active = this ;
2019-01-23 06:23:14 -05:00
fallback && this . _appendGhost ( ) ;
2018-12-20 04:08:22 -05:00
// Drag start event
_dispatchEvent ( this , rootEl , 'start' , dragEl , rootEl , rootEl , oldIndex ) ;
} else {
this . _nulling ( ) ;
}
} ,
_emulateDragOver : function ( bypassLastTouchCheck ) {
if ( touchEvt ) {
if ( this . _lastX === touchEvt . clientX && this . _lastY === touchEvt . clientY && ! bypassLastTouchCheck ) {
return ;
}
this . _lastX = touchEvt . clientX ;
this . _lastY = touchEvt . clientY ;
2019-01-23 06:23:14 -05:00
_hideGhostForTarget ( ) ;
2018-12-20 04:08:22 -05:00
var target = document . elementFromPoint ( touchEvt . clientX , touchEvt . clientY ) ;
var parent = target ;
while ( target && target . shadowRoot ) {
target = target . shadowRoot . elementFromPoint ( touchEvt . clientX , touchEvt . clientY ) ;
parent = target ;
}
if ( parent ) {
do {
if ( parent [ expando ] ) {
2019-01-23 06:23:14 -05:00
var inserted ;
inserted = parent [ expando ] . _onDragOver ( {
clientX : touchEvt . clientX ,
clientY : touchEvt . clientY ,
target : target ,
rootEl : parent
} ) ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
if ( inserted && ! this . options . dragoverBubble ) {
2018-12-20 04:08:22 -05:00
break ;
}
}
target = parent ; // store last element
}
/* jshint boss:true */
while ( parent = parent . parentNode ) ;
}
2019-01-23 06:23:14 -05:00
dragEl . parentNode [ expando ] . _computeIsAligned ( touchEvt ) ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
_unhideGhostForTarget ( ) ;
2018-12-20 04:08:22 -05:00
}
} ,
_onTouchMove : function ( /**TouchEvent*/ evt ) {
if ( tapEvt ) {
var options = this . options ,
fallbackTolerance = options . fallbackTolerance ,
fallbackOffset = options . fallbackOffset ,
touch = evt . touches ? evt . touches [ 0 ] : evt ,
2019-01-23 06:23:14 -05:00
matrix = ghostEl && _matrix ( ghostEl ) ,
scaleX = ghostEl && matrix && matrix . a ,
scaleY = ghostEl && matrix && matrix . d ,
dx = ( ( touch . clientX - tapEvt . clientX ) + fallbackOffset . x ) / ( scaleX ? scaleX : 1 ) ,
dy = ( ( touch . clientY - tapEvt . clientY ) + fallbackOffset . y ) / ( scaleY ? scaleY : 1 ) ,
2018-12-20 04:08:22 -05:00
translate3d = evt . touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)' ;
// only set the status to dragging, when we are actually dragging
2019-01-23 06:23:14 -05:00
if ( ! Sortable . active && ! awaitingDragStarted ) {
2018-12-20 04:08:22 -05:00
if ( fallbackTolerance &&
min ( abs ( touch . clientX - this . _lastX ) , abs ( touch . clientY - this . _lastY ) ) < fallbackTolerance
) {
return ;
}
2019-01-23 06:23:14 -05:00
this . _onDragStart ( evt , true ) ;
2018-12-20 04:08:22 -05:00
}
this . _handleAutoScroll ( touch , true ) ;
moved = true ;
touchEvt = touch ;
2019-01-23 06:23:14 -05:00
2018-12-20 04:08:22 -05:00
_css ( ghostEl , 'webkitTransform' , translate3d ) ;
_css ( ghostEl , 'mozTransform' , translate3d ) ;
_css ( ghostEl , 'msTransform' , translate3d ) ;
_css ( ghostEl , 'transform' , translate3d ) ;
evt . cancelable && evt . preventDefault ( ) ;
}
} ,
_appendGhost : function ( ) {
if ( ! ghostEl ) {
2019-01-23 06:23:14 -05:00
var rect = _getRect ( dragEl , this . options . fallbackOnBody ? document . body : rootEl , true ) ,
2018-12-20 04:08:22 -05:00
css = _css ( dragEl ) ,
options = this . options ;
ghostEl = dragEl . cloneNode ( true ) ;
_toggleClass ( ghostEl , options . ghostClass , false ) ;
_toggleClass ( ghostEl , options . fallbackClass , true ) ;
_toggleClass ( ghostEl , options . dragClass , true ) ;
_css ( ghostEl , 'box-sizing' , 'border-box' ) ;
_css ( ghostEl , 'margin' , 0 ) ;
_css ( ghostEl , 'top' , rect . top ) ;
_css ( ghostEl , 'left' , rect . left ) ;
_css ( ghostEl , 'width' , rect . width ) ;
_css ( ghostEl , 'height' , rect . height ) ;
_css ( ghostEl , 'opacity' , '0.8' ) ;
_css ( ghostEl , 'position' , 'fixed' ) ;
_css ( ghostEl , 'zIndex' , '100000' ) ;
_css ( ghostEl , 'pointerEvents' , 'none' ) ;
options . fallbackOnBody && document . body . appendChild ( ghostEl ) || rootEl . appendChild ( ghostEl ) ;
}
} ,
2019-01-23 06:23:14 -05:00
_onDragStart : function ( /**Event*/ evt , /**boolean*/ fallback ) {
2018-12-20 04:08:22 -05:00
var _this = this ;
var dataTransfer = evt . dataTransfer ;
var options = _this . options ;
2019-01-23 06:23:14 -05:00
// Setup clone
cloneEl = _clone ( dragEl ) ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
cloneEl . draggable = false ;
cloneEl . style [ 'will-change' ] = '' ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
this . _hideClone ( ) ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
_toggleClass ( cloneEl , _this . options . chosenClass , false ) ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
// #1143: IFrame support workaround
_this . _cloneId = _nextTick ( function ( ) {
if ( ! _this . options . removeCloneOnHide ) {
rootEl . insertBefore ( cloneEl , dragEl ) ;
}
_dispatchEvent ( _this , rootEl , 'clone' , dragEl ) ;
} ) ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
! fallback && _toggleClass ( dragEl , options . dragClass , true ) ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
// Set proper drop events
if ( fallback ) {
ignoreNextClick = true ;
2018-12-20 04:08:22 -05:00
_this . _loopId = setInterval ( _this . _emulateDragOver , 50 ) ;
2019-01-23 06:23:14 -05:00
} else {
// Undo what was set in _prepareDragStart before drag started
_off ( document , 'mouseup' , _this . _onDrop ) ;
_off ( document , 'touchend' , _this . _onDrop ) ;
_off ( document , 'touchcancel' , _this . _onDrop ) ;
2018-12-20 04:08:22 -05:00
if ( dataTransfer ) {
dataTransfer . effectAllowed = 'move' ;
options . setData && options . setData . call ( _this , dataTransfer , dragEl ) ;
}
_on ( document , 'drop' , _this ) ;
// #1276 fix:
_css ( dragEl , 'transform' , 'translateZ(0)' ) ;
}
2019-01-23 06:23:14 -05:00
awaitingDragStarted = true ;
_this . _dragStartId = _nextTick ( _this . _dragStarted . bind ( _this , fallback ) ) ;
2018-12-20 04:08:22 -05:00
_on ( document , 'selectstart' , _this ) ;
} ,
2019-01-23 06:23:14 -05:00
// Returns true - if no further action is needed (either inserted or another condition)
2018-12-20 04:08:22 -05:00
_onDragOver : function ( /**Event*/ evt ) {
var el = this . el ,
2019-01-23 06:23:14 -05:00
target = evt . target ,
2018-12-20 04:08:22 -05:00
dragRect ,
targetRect ,
revert ,
options = this . options ,
group = options . group ,
activeSortable = Sortable . active ,
isOwner = ( activeGroup === group ) ,
2019-01-23 06:23:14 -05:00
canSort = options . sort ,
_this = this ;
if ( _silent ) return ;
// IE event order fix
if ( IE11OrLess && ! evt . rootEl && ! evt . artificialBubble && ! _isTrueParentSortable ( el , target ) ) {
return ;
}
// Return invocation when no further action is needed in another sortable
function completed ( ) {
if ( activeSortable ) {
// Set ghost class to new sortable's ghost class
_toggleClass ( dragEl , putSortable ? putSortable . options . ghostClass : activeSortable . options . ghostClass , false ) ;
_toggleClass ( dragEl , options . ghostClass , true ) ;
}
if ( putSortable !== _this && _this !== Sortable . active ) {
putSortable = _this ;
} else if ( _this === Sortable . active ) {
putSortable = null ;
}
// Null lastTarget if it is not inside a previously swapped element
if ( ( target === dragEl && ! dragEl . animated ) || ( target === el && ! target . animated ) ) {
lastTarget = null ;
}
// no bubbling and not fallback
if ( ! options . dragoverBubble && ! evt . rootEl && target !== document ) {
_this . _handleAutoScroll ( evt ) ;
dragEl . parentNode [ expando ] . _computeIsAligned ( evt ) ;
}
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
! options . dragoverBubble && evt . stopPropagation && evt . stopPropagation ( ) ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
return true ;
}
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
// Call when dragEl has been inserted
function changed ( ) {
_dispatchEvent ( _this , rootEl , 'change' , target , el , rootEl , oldIndex , _index ( dragEl , options . draggable ) , evt ) ;
2018-12-20 04:08:22 -05:00
}
2019-01-23 06:23:14 -05:00
2018-12-20 04:08:22 -05:00
if ( evt . preventDefault !== void 0 ) {
evt . cancelable && evt . preventDefault ( ) ;
}
moved = true ;
2019-01-23 06:23:14 -05:00
target = _closest ( target , options . draggable , el , true ) ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
// target is dragEl or target is animated
if ( ! ! _closest ( evt . target , null , dragEl , true ) || target . animated ) {
return completed ( ) ;
2018-12-20 04:08:22 -05:00
}
2019-01-23 06:23:14 -05:00
if ( target !== dragEl ) {
ignoreNextClick = false ;
2018-12-20 04:08:22 -05:00
}
if ( activeSortable && ! options . disabled &&
( isOwner
? canSort || ( revert = ! rootEl . contains ( dragEl ) ) // Reverting item into the original list
: (
putSortable === this ||
(
( this . lastPutMode = activeGroup . checkPull ( this , activeSortable , dragEl , evt ) ) &&
group . checkPut ( this , activeSortable , dragEl , evt )
)
)
)
) {
var axis = this . _getDirection ( evt , target ) ;
2019-01-23 06:23:14 -05:00
dragRect = _getRect ( dragEl ) ;
2018-12-20 04:08:22 -05:00
if ( revert ) {
this . _hideClone ( ) ;
parentEl = rootEl ; // actualization
2019-01-23 06:23:14 -05:00
if ( nextEl ) {
rootEl . insertBefore ( dragEl , nextEl ) ;
} else {
2018-12-20 04:08:22 -05:00
rootEl . appendChild ( dragEl ) ;
}
2019-01-23 06:23:14 -05:00
return completed ( ) ;
2018-12-20 04:08:22 -05:00
}
if ( ( el . children . length === 0 ) || ( el . children [ 0 ] === ghostEl ) ||
2019-01-23 06:23:14 -05:00
_ghostIsLast ( evt , axis , el ) && ! dragEl . animated
2018-12-20 04:08:22 -05:00
) {
//assign target only if condition is true
if ( el . children . length !== 0 && el . children [ 0 ] !== ghostEl && el === evt . target ) {
target = _lastChild ( el ) ;
}
if ( target ) {
2019-01-23 06:23:14 -05:00
targetRect = _getRect ( target ) ;
2018-12-20 04:08:22 -05:00
}
if ( isOwner ) {
activeSortable . _hideClone ( ) ;
} else {
activeSortable . _showClone ( this ) ;
}
if ( _onMove ( rootEl , el , dragEl , dragRect , target , targetRect , evt , ! ! target ) !== false ) {
2019-01-23 06:23:14 -05:00
el . appendChild ( dragEl ) ;
parentEl = el ; // actualization
realDragElRect = null ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
changed ( ) ;
2018-12-20 04:08:22 -05:00
this . _animate ( dragRect , dragEl ) ;
target && this . _animate ( targetRect , target ) ;
2019-01-23 06:23:14 -05:00
return completed ( ) ;
2018-12-20 04:08:22 -05:00
}
}
2019-01-25 01:30:56 -05:00
else if ( target && target !== dragEl && target . parentNode === el ) {
2019-01-23 06:23:14 -05:00
var direction = 0 ,
targetBeforeFirstSwap ,
aligned = target . sortableMouseAligned ,
differentLevel = dragEl . parentNode !== el ,
scrolledPastTop = _isScrolledPast ( target , axis === 'vertical' ? 'top' : 'left' ) ;
if ( lastTarget !== target ) {
lastMode = null ;
targetBeforeFirstSwap = _getRect ( target ) [ axis === 'vertical' ? 'top' : 'left' ] ;
pastFirstInvertThresh = false ;
}
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
// Reference: https://www.lucidchart.com/documents/view/10fa0e93-e362-4126-aca2-b709ee56bd8b/0
if (
_isElInRowColumn ( dragEl , target , axis ) && aligned ||
differentLevel ||
scrolledPastTop ||
options . invertSwap ||
lastMode === 'insert' ||
// Needed, in the case that we are inside target and inserted because not aligned... aligned will stay false while inside
// and lastMode will change to 'insert', but we must swap
lastMode === 'swap'
) {
// New target that we will be inside
if ( lastMode !== 'swap' ) {
isCircumstantialInvert = options . invertSwap || differentLevel || scrolling || scrolledPastTop ;
}
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
direction = _getSwapDirection ( evt , target , axis ,
options . swapThreshold , options . invertedSwapThreshold == null ? options . swapThreshold : options . invertedSwapThreshold ,
isCircumstantialInvert ,
lastTarget === target ) ;
lastMode = 'swap' ;
} else {
// Insert at position
direction = _getInsertDirection ( target , options ) ;
lastMode = 'insert' ;
2018-12-20 04:08:22 -05:00
}
2019-01-23 06:23:14 -05:00
if ( direction === 0 ) return completed ( ) ;
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
realDragElRect = null ;
lastTarget = target ;
2018-12-20 04:08:22 -05:00
lastDirection = direction ;
2019-01-23 06:23:14 -05:00
targetRect = _getRect ( target ) ;
2018-12-20 04:08:22 -05:00
var nextSibling = target . nextElementSibling ,
2019-01-23 06:23:14 -05:00
after = false ;
2018-12-20 04:08:22 -05:00
after = direction === 1 ;
var moveVector = _onMove ( rootEl , el , dragEl , dragRect , target , targetRect , evt , after ) ;
if ( moveVector !== false ) {
if ( moveVector === 1 || moveVector === - 1 ) {
after = ( moveVector === 1 ) ;
}
_silent = true ;
setTimeout ( _unsilent , 30 ) ;
if ( isOwner ) {
activeSortable . _hideClone ( ) ;
} else {
activeSortable . _showClone ( this ) ;
}
2019-01-23 06:23:14 -05:00
if ( after && ! nextSibling ) {
el . appendChild ( dragEl ) ;
} else {
target . parentNode . insertBefore ( dragEl , after ? nextSibling : target ) ;
2018-12-20 04:08:22 -05:00
}
parentEl = dragEl . parentNode ; // actualization
2019-01-23 06:23:14 -05:00
// must be done before animation
if ( targetBeforeFirstSwap !== undefined && ! isCircumstantialInvert ) {
targetMoveDistance = abs ( targetBeforeFirstSwap - _getRect ( target ) [ axis === 'vertical' ? 'top' : 'left' ] ) ;
}
changed ( ) ;
! differentLevel && this . _animate ( targetRect , target ) ;
2018-12-20 04:08:22 -05:00
this . _animate ( dragRect , dragEl ) ;
2019-01-23 06:23:14 -05:00
return completed ( ) ;
2018-12-20 04:08:22 -05:00
}
}
2019-01-23 06:23:14 -05:00
if ( el . contains ( dragEl ) ) {
return completed ( ) ;
}
}
if ( IE11OrLess && ! evt . rootEl ) {
_artificalBubble ( el , evt , '_onDragOver' ) ;
2018-12-20 04:08:22 -05:00
}
2019-01-23 06:23:14 -05:00
return false ;
2018-12-20 04:08:22 -05:00
} ,
_animate : function ( prevRect , target ) {
var ms = this . options . animation ;
if ( ms ) {
2019-01-23 06:23:14 -05:00
var currentRect = _getRect ( target ) ;
2018-12-20 04:08:22 -05:00
if ( target === dragEl ) {
realDragElRect = currentRect ;
}
if ( prevRect . nodeType === 1 ) {
2019-01-23 06:23:14 -05:00
prevRect = _getRect ( prevRect ) ;
2018-12-20 04:08:22 -05:00
}
// Check if actually moving position
2019-01-23 06:23:14 -05:00
if ( ( prevRect . left + prevRect . width / 2 ) !== ( currentRect . left + currentRect . width / 2 )
|| ( prevRect . top + prevRect . height / 2 ) !== ( currentRect . top + currentRect . height / 2 )
) {
var matrix = _matrix ( this . el ) ,
scaleX = matrix && matrix . a ,
scaleY = matrix && matrix . d ;
_css ( target , 'transition' , 'none' ) ;
_css ( target , 'transform' , 'translate3d('
+ ( prevRect . left - currentRect . left ) / ( scaleX ? scaleX : 1 ) + 'px,'
+ ( prevRect . top - currentRect . top ) / ( scaleY ? scaleY : 1 ) + 'px,0)'
) ;
forRepaintDummy = target . offsetWidth ; // repaint
_css ( target , 'transition' , 'transform ' + ms + 'ms' + ( this . options . easing ? ' ' + this . options . easing : '' ) ) ;
_css ( target , 'transform' , 'translate3d(0,0,0)' ) ;
}
2018-12-20 04:08:22 -05:00
2019-01-23 06:23:14 -05:00
( typeof target . animated === 'number' ) && clearTimeout ( target . animated ) ;
2018-12-20 04:08:22 -05:00
target . animated = setTimeout ( function ( ) {
_css ( target , 'transition' , '' ) ;
_css ( target , 'transform' , '' ) ;
target . animated = false ;
} , ms ) ;
}
} ,
_offUpEvents : function ( ) {
var ownerDocument = this . el . ownerDocument ;
_off ( document , 'touchmove' , this . _onTouchMove ) ;
_off ( document , 'pointermove' , this . _onTouchMove ) ;
_off ( ownerDocument , 'mouseup' , this . _onDrop ) ;
_off ( ownerDocument , 'touchend' , this . _onDrop ) ;
_off ( ownerDocument , 'pointerup' , this . _onDrop ) ;
_off ( ownerDocument , 'touchcancel' , this . _onDrop ) ;
_off ( document , 'selectstart' , this ) ;
} ,
_onDrop : function ( /**Event*/ evt ) {
var el = this . el ,
options = this . options ;
2019-01-23 06:23:14 -05:00
awaitingDragStarted = false ;
2018-12-20 04:08:22 -05:00
scrolling = false ;
isCircumstantialInvert = false ;
pastFirstInvertThresh = false ;
clearInterval ( this . _loopId ) ;
clearInterval ( pointerElemChangedInterval ) ;
_clearAutoScrolls ( ) ;
_cancelThrottle ( ) ;
clearTimeout ( this . _dragStartTimer ) ;
_cancelNextTick ( this . _cloneId ) ;
_cancelNextTick ( this . _dragStartId ) ;
// Unbind events
_off ( document , 'mousemove' , this . _onTouchMove ) ;
if ( this . nativeDraggable ) {
_off ( document , 'drop' , this ) ;
_off ( el , 'dragstart' , this . _onDragStart ) ;
_off ( document , 'dragover' , this . _handleAutoScroll ) ;
_off ( document , 'dragover' , _checkAlignment ) ;
}
this . _offUpEvents ( ) ;
if ( evt ) {
if ( moved ) {
evt . cancelable && evt . preventDefault ( ) ;
! options . dropBubble && evt . stopPropagation ( ) ;
}
ghostEl && ghostEl . parentNode && ghostEl . parentNode . removeChild ( ghostEl ) ;
if ( rootEl === parentEl || ( putSortable && putSortable . lastPutMode !== 'clone' ) ) {
// Remove clone
cloneEl && cloneEl . parentNode && cloneEl . parentNode . removeChild ( cloneEl ) ;
}
if ( dragEl ) {
if ( this . nativeDraggable ) {
_off ( dragEl , 'dragend' , this ) ;
}
_disableDraggable ( dragEl ) ;
dragEl . style [ 'will-change' ] = '' ;
// Remove class's
2019-01-23 06:23:14 -05:00
_toggleClass ( dragEl , putSortable ? putSortable . options . ghostClass : this . options . ghostClass , false ) ;
2018-12-20 04:08:22 -05:00
_toggleClass ( dragEl , this . options . chosenClass , false ) ;
// Drag stop event
_dispatchEvent ( this , rootEl , 'unchoose' , dragEl , parentEl , rootEl , oldIndex , null , evt ) ;
if ( rootEl !== parentEl ) {
newIndex = _index ( dragEl , options . draggable ) ;
if ( newIndex >= 0 ) {
// Add event
_dispatchEvent ( null , parentEl , 'add' , dragEl , parentEl , rootEl , oldIndex , newIndex , evt ) ;
// Remove event
_dispatchEvent ( this , rootEl , 'remove' , dragEl , parentEl , rootEl , oldIndex , newIndex , evt ) ;
// drag from one list and drop into another
_dispatchEvent ( null , parentEl , 'sort' , dragEl , parentEl , rootEl , oldIndex , newIndex , evt ) ;
_dispatchEvent ( this , rootEl , 'sort' , dragEl , parentEl , rootEl , oldIndex , newIndex , evt ) ;
}
putSortable && putSortable . save ( ) ;
}
else {
if ( dragEl . nextSibling !== nextEl ) {
// Get the index of the dragged element within its parent
newIndex = _index ( dragEl , options . draggable ) ;
if ( newIndex >= 0 ) {
// drag & drop within the same list
_dispatchEvent ( this , rootEl , 'update' , dragEl , parentEl , rootEl , oldIndex , newIndex , evt ) ;
_dispatchEvent ( this , rootEl , 'sort' , dragEl , parentEl , rootEl , oldIndex , newIndex , evt ) ;
}
}
}
if ( Sortable . active ) {
/* jshint eqnull:true */
if ( newIndex == null || newIndex === - 1 ) {
newIndex = oldIndex ;
}
_dispatchEvent ( this , rootEl , 'end' , dragEl , parentEl , rootEl , oldIndex , newIndex , evt ) ;
// Save sorting
this . save ( ) ;
}
}
}
this . _nulling ( ) ;
} ,
_nulling : function ( ) {
rootEl =
dragEl =
parentEl =
ghostEl =
nextEl =
cloneEl =
lastDownEl =
scrollEl =
scrollParentEl =
autoScrolls . length =
pointerElemChangedInterval =
lastPointerElemX =
lastPointerElemY =
tapEvt =
touchEvt =
moved =
newIndex =
oldIndex =
lastTarget =
lastDirection =
forRepaintDummy =
realDragElRect =
putSortable =
activeGroup =
Sortable . active = null ;
savedInputChecked . forEach ( function ( el ) {
el . checked = true ;
} ) ;
2019-01-23 06:23:14 -05:00
2018-12-20 04:08:22 -05:00
savedInputChecked . length = 0 ;
} ,
handleEvent : function ( /**Event*/ evt ) {
switch ( evt . type ) {
case 'drop' :
case 'dragend' :
this . _onDrop ( evt ) ;
break ;
case 'dragenter' :
case 'dragover' :
if ( dragEl ) {
this . _onDragOver ( evt ) ;
_globalDragOver ( evt ) ;
}
break ;
case 'selectstart' :
evt . preventDefault ( ) ;
break ;
}
} ,
/ * *
* Serializes the item into an array of string .
* @ returns { String [ ] }
* /
toArray : function ( ) {
var order = [ ] ,
el ,
children = this . el . children ,
i = 0 ,
n = children . length ,
options = this . options ;
for ( ; i < n ; i ++ ) {
el = children [ i ] ;
if ( _closest ( el , options . draggable , this . el , false ) ) {
order . push ( el . getAttribute ( options . dataIdAttr ) || _generateId ( el ) ) ;
}
}
return order ;
} ,
/ * *
* Sorts the elements according to the array .
* @ param { String [ ] } order order of the items
* /
sort : function ( order ) {
var items = { } , rootEl = this . el ;
this . toArray ( ) . forEach ( function ( id , i ) {
var el = rootEl . children [ i ] ;
if ( _closest ( el , this . options . draggable , rootEl , false ) ) {
items [ id ] = el ;
}
} , this ) ;
order . forEach ( function ( id ) {
if ( items [ id ] ) {
rootEl . removeChild ( items [ id ] ) ;
rootEl . appendChild ( items [ id ] ) ;
}
} ) ;
} ,
/ * *
* Save the current sorting
* /
save : function ( ) {
var store = this . options . store ;
store && store . set && store . set ( this ) ;
} ,
/ * *
* For each element in the set , get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree .
* @ param { HTMLElement } el
* @ param { String } [ selector ] default : ` options.draggable `
* @ returns { HTMLElement | null }
* /
closest : function ( el , selector ) {
return _closest ( el , selector || this . options . draggable , this . el , false ) ;
} ,
/ * *
* Set / get option
* @ param { string } name
* @ param { * } [ value ]
* @ returns { * }
* /
option : function ( name , value ) {
var options = this . options ;
if ( value === void 0 ) {
return options [ name ] ;
} else {
options [ name ] = value ;
if ( name === 'group' ) {
_prepareGroup ( options ) ;
}
}
} ,
/ * *
* Destroy
* /
destroy : function ( ) {
var el = this . el ;
el [ expando ] = null ;
_off ( el , 'mousedown' , this . _onTapStart ) ;
_off ( el , 'touchstart' , this . _onTapStart ) ;
_off ( el , 'pointerdown' , this . _onTapStart ) ;
if ( this . nativeDraggable ) {
_off ( el , 'dragover' , this ) ;
_off ( el , 'dragenter' , this ) ;
}
// Remove draggable attributes
Array . prototype . forEach . call ( el . querySelectorAll ( '[draggable]' ) , function ( el ) {
el . removeAttribute ( 'draggable' ) ;
} ) ;
this . _onDrop ( ) ;
2019-01-23 06:23:14 -05:00
sortables . splice ( sortables . indexOf ( this . el ) , 1 ) ;
2018-12-20 04:08:22 -05:00
this . el = el = null ;
} ,
_hideClone : function ( ) {
if ( ! cloneEl . cloneHidden ) {
_css ( cloneEl , 'display' , 'none' ) ;
cloneEl . cloneHidden = true ;
2019-01-23 06:23:14 -05:00
if ( cloneEl . parentNode && this . options . removeCloneOnHide ) {
cloneEl . parentNode . removeChild ( cloneEl ) ;
}
2018-12-20 04:08:22 -05:00
}
} ,
_showClone : function ( putSortable ) {
if ( putSortable . lastPutMode !== 'clone' ) {
this . _hideClone ( ) ;
return ;
}
if ( cloneEl . cloneHidden ) {
// show clone at dragEl or original position
if ( rootEl . contains ( dragEl ) && ! this . options . group . revertClone ) {
rootEl . insertBefore ( cloneEl , dragEl ) ;
} else if ( nextEl ) {
rootEl . insertBefore ( cloneEl , nextEl ) ;
} else {
rootEl . appendChild ( cloneEl ) ;
}
if ( this . options . group . revertClone ) {
this . _animate ( dragEl , cloneEl ) ;
}
_css ( cloneEl , 'display' , '' ) ;
cloneEl . cloneHidden = false ;
}
}
} ;
function _closest ( /**HTMLElement*/ el , /**String*/ selector , /**HTMLElement*/ ctx , includeCTX ) {
if ( el ) {
ctx = ctx || document ;
do {
2019-01-25 01:30:56 -05:00
if (
selector != null &&
(
selector [ 0 ] === '>' && el . parentNode === ctx && _matches ( el , selector . substring ( 1 ) ) ||
2019-02-27 08:01:44 -05:00
_matches ( el , selector )
) ||
includeCTX && el === ctx
2019-01-25 01:30:56 -05:00
) {
2018-12-20 04:08:22 -05:00
return el ;
}
if ( el === ctx ) break ;
/* jshint boss:true */
} while ( el = _getParentOrHost ( el ) ) ;
}
return null ;
}
function _getParentOrHost ( el ) {
return ( el . host && el !== document && el . host . nodeType )
? el . host
: el . parentNode ;
}
function _globalDragOver ( /**Event*/ evt ) {
if ( evt . dataTransfer ) {
2019-02-27 08:01:44 -05:00
evt . dataTransfer . dropEffect = 'move' ;
2018-12-20 04:08:22 -05:00
}
evt . cancelable && evt . preventDefault ( ) ;
}
function _on ( el , event , fn ) {
el . addEventListener ( event , fn , captureMode ) ;
}
function _off ( el , event , fn ) {
el . removeEventListener ( event , fn , captureMode ) ;
}
function _toggleClass ( el , name , state ) {
if ( el && name ) {
if ( el . classList ) {
el . classList [ state ? 'add' : 'remove' ] ( name ) ;
}
else {
var className = ( ' ' + el . className + ' ' ) . replace ( R _SPACE , ' ' ) . replace ( ' ' + name + ' ' , ' ' ) ;
el . className = ( className + ( state ? ' ' + name : '' ) ) . replace ( R _SPACE , ' ' ) ;
}
}
}
function _css ( el , prop , val ) {
var style = el && el . style ;
if ( style ) {
if ( val === void 0 ) {
if ( document . defaultView && document . defaultView . getComputedStyle ) {
val = document . defaultView . getComputedStyle ( el , '' ) ;
}
else if ( el . currentStyle ) {
val = el . currentStyle ;
}
return prop === void 0 ? val : val [ prop ] ;
}
else {
2019-01-23 06:23:14 -05:00
if ( ! ( prop in style ) && prop . indexOf ( 'webkit' ) === - 1 ) {
2018-12-20 04:08:22 -05:00
prop = '-webkit-' + prop ;
}
style [ prop ] = val + ( typeof val === 'string' ? '' : 'px' ) ;
}
}
}
2019-01-23 06:23:14 -05:00
function _matrix ( el ) {
var appliedTransforms = '' ;
do {
var transform = _css ( el , 'transform' ) ;
if ( transform && transform !== 'none' ) {
appliedTransforms = transform + ' ' + appliedTransforms ;
}
/* jshint boss:true */
} while ( el = el . parentNode ) ;
if ( window . DOMMatrix ) {
return new DOMMatrix ( appliedTransforms ) ;
} else if ( window . WebKitCSSMatrix ) {
return new WebKitCSSMatrix ( appliedTransforms ) ;
} else if ( window . CSSMatrix ) {
return new CSSMatrix ( appliedTransforms ) ;
}
}
2018-12-20 04:08:22 -05:00
function _find ( ctx , tagName , iterator ) {
if ( ctx ) {
var list = ctx . getElementsByTagName ( tagName ) , i = 0 , n = list . length ;
if ( iterator ) {
for ( ; i < n ; i ++ ) {
iterator ( list [ i ] , i ) ;
}
}
return list ;
}
return [ ] ;
}
function _dispatchEvent ( sortable , rootEl , name , targetEl , toEl , fromEl , startIndex , newIndex , originalEvt ) {
sortable = ( sortable || rootEl [ expando ] ) ;
var evt ,
options = sortable . options ,
onName = 'on' + name . charAt ( 0 ) . toUpperCase ( ) + name . substr ( 1 ) ;
// Support for new CustomEvent feature
2019-01-23 06:23:14 -05:00
if ( window . CustomEvent && ! IE11OrLess && ! Edge ) {
2018-12-20 04:08:22 -05:00
evt = new CustomEvent ( name , {
bubbles : true ,
cancelable : true
} ) ;
} else {
evt = document . createEvent ( 'Event' ) ;
evt . initEvent ( name , true , true ) ;
}
evt . to = toEl || rootEl ;
evt . from = fromEl || rootEl ;
evt . item = targetEl || rootEl ;
evt . clone = cloneEl ;
evt . oldIndex = startIndex ;
evt . newIndex = newIndex ;
evt . originalEvent = originalEvt ;
2019-01-23 06:23:14 -05:00
if ( rootEl ) {
rootEl . dispatchEvent ( evt ) ;
}
2018-12-20 04:08:22 -05:00
if ( options [ onName ] ) {
options [ onName ] . call ( sortable , evt ) ;
}
}
function _onMove ( fromEl , toEl , dragEl , dragRect , targetEl , targetRect , originalEvt , willInsertAfter ) {
var evt ,
sortable = fromEl [ expando ] ,
onMoveFn = sortable . options . onMove ,
retVal ;
// Support for new CustomEvent feature
2019-01-23 06:23:14 -05:00
if ( window . CustomEvent && ! IE11OrLess && ! Edge ) {
2018-12-20 04:08:22 -05:00
evt = new CustomEvent ( 'move' , {
bubbles : true ,
cancelable : true
} ) ;
} else {
evt = document . createEvent ( 'Event' ) ;
evt . initEvent ( 'move' , true , true ) ;
}
evt . to = toEl ;
evt . from = fromEl ;
evt . dragged = dragEl ;
evt . draggedRect = dragRect ;
evt . related = targetEl || toEl ;
2019-01-23 06:23:14 -05:00
evt . relatedRect = targetRect || _getRect ( toEl ) ;
2018-12-20 04:08:22 -05:00
evt . willInsertAfter = willInsertAfter ;
evt . originalEvent = originalEvt ;
fromEl . dispatchEvent ( evt ) ;
if ( onMoveFn ) {
retVal = onMoveFn . call ( sortable , evt , originalEvt ) ;
}
return retVal ;
}
function _disableDraggable ( el ) {
el . draggable = false ;
}
function _unsilent ( ) {
_silent = false ;
}
2019-01-23 06:23:14 -05:00
/ * *
* Gets nth child of el , ignoring hidden children , sortable 's elements (does not ignore clone if it' s visible )
* and non - draggable elements
* @ param { HTMLElement } el The parent element
* @ param { Number } childNum The index of the child
* @ param { Object } options Parent Sortable ' s options
* @ return { HTMLElement } The child at index childNum , or null if not found
* /
2018-12-20 04:08:22 -05:00
function _getChild ( el , childNum , options ) {
var currentChild = 0 ,
i = 0 ,
2019-01-23 06:23:14 -05:00
children = el . children ;
2018-12-20 04:08:22 -05:00
while ( i < children . length ) {
if (
children [ i ] . style . display !== 'none' &&
children [ i ] !== ghostEl &&
children [ i ] !== dragEl &&
_closest ( children [ i ] , options . draggable , el , false )
) {
if ( currentChild === childNum ) {
return children [ i ] ;
}
currentChild ++ ;
}
i ++ ;
}
return null ;
}
2019-01-23 06:23:14 -05:00
/ * *
2019-02-27 08:01:44 -05:00
* Gets the last child in the el , ignoring ghostEl or invisible elements ( clones )
2019-01-23 06:23:14 -05:00
* @ param { HTMLElement } el Parent element
* @ return { HTMLElement } The last child , ignoring ghostEl
* /
2018-12-20 04:08:22 -05:00
function _lastChild ( el ) {
var last = el . lastElementChild ;
2019-02-27 08:01:44 -05:00
while ( last === ghostEl || last . style . display === 'none' ) {
last = last . previousElementSibling ;
if ( ! last ) break ;
2018-12-20 04:08:22 -05:00
}
return last || null ;
}
function _ghostIsLast ( evt , axis , el ) {
2019-01-23 06:23:14 -05:00
var elRect = _getRect ( _lastChild ( el ) ) ,
2018-12-20 04:08:22 -05:00
mouseOnAxis = axis === 'vertical' ? evt . clientY : evt . clientX ,
mouseOnOppAxis = axis === 'vertical' ? evt . clientX : evt . clientY ,
targetS2 = axis === 'vertical' ? elRect . bottom : elRect . right ,
targetS1Opp = axis === 'vertical' ? elRect . left : elRect . top ,
2019-02-27 08:01:44 -05:00
targetS2Opp = axis === 'vertical' ? elRect . right : elRect . bottom ,
spacer = 10 ;
2019-01-23 06:23:14 -05:00
2018-12-20 04:08:22 -05:00
return (
2019-02-27 08:01:44 -05:00
axis === 'vertical' ?
( mouseOnOppAxis > targetS2Opp + spacer || mouseOnOppAxis <= targetS2Opp && mouseOnAxis > targetS2 && mouseOnOppAxis >= targetS1Opp ) :
( mouseOnAxis > targetS2 && mouseOnOppAxis > targetS1Opp || mouseOnAxis <= targetS2 && mouseOnOppAxis > targetS2Opp + spacer )
2018-12-20 04:08:22 -05:00
) ;
}
2019-01-23 06:23:14 -05:00
function _getSwapDirection ( evt , target , axis , swapThreshold , invertedSwapThreshold , invertSwap , isLastTarget ) {
var targetRect = _getRect ( target ) ,
2018-12-20 04:08:22 -05:00
mouseOnAxis = axis === 'vertical' ? evt . clientY : evt . clientX ,
targetLength = axis === 'vertical' ? targetRect . height : targetRect . width ,
targetS1 = axis === 'vertical' ? targetRect . top : targetRect . left ,
targetS2 = axis === 'vertical' ? targetRect . bottom : targetRect . right ,
2019-01-23 06:23:14 -05:00
dragRect = _getRect ( dragEl ) ,
invert = false ;
2018-12-20 04:08:22 -05:00
if ( ! invertSwap ) {
2019-01-23 06:23:14 -05:00
// Never invert or create dragEl shadow when target movemenet causes mouse to move past the end of regular swapThreshold
if ( isLastTarget && targetMoveDistance < targetLength * swapThreshold ) { // multiplied only by swapThreshold because mouse will already be inside target by (1 - threshold) * targetLength / 2
2018-12-20 04:08:22 -05:00
// check if past first invert threshold on side opposite of lastDirection
if ( ! pastFirstInvertThresh &&
( lastDirection === 1 ?
(
mouseOnAxis > targetS1 + targetLength * invertedSwapThreshold / 2
) :
(
mouseOnAxis < targetS2 - targetLength * invertedSwapThreshold / 2
)
)
)
{
// past first invert threshold, do not restrict inverted threshold to dragEl shadow
pastFirstInvertThresh = true ;
}
if ( ! pastFirstInvertThresh ) {
var dragS1 = axis === 'vertical' ? dragRect . top : dragRect . left ,
2019-01-23 06:23:14 -05:00
dragS2 = axis === 'vertical' ? dragRect . bottom : dragRect . right ;
// dragEl shadow (target move distance shadow)
2018-12-20 04:08:22 -05:00
if (
lastDirection === 1 ?
(
2019-01-23 06:23:14 -05:00
mouseOnAxis < targetS1 + targetMoveDistance // over dragEl shadow
2018-12-20 04:08:22 -05:00
) :
(
2019-01-23 06:23:14 -05:00
mouseOnAxis > targetS2 - targetMoveDistance
2018-12-20 04:08:22 -05:00
)
)
{
return lastDirection * - 1 ;
}
} else {
invert = true ;
}
} else {
// Regular
if (
mouseOnAxis > targetS1 + ( targetLength * ( 1 - swapThreshold ) / 2 ) &&
mouseOnAxis < targetS2 - ( targetLength * ( 1 - swapThreshold ) / 2 )
) {
return ( ( mouseOnAxis > targetS1 + targetLength / 2 ) ? - 1 : 1 ) ;
}
}
}
invert = invert || invertSwap ;
if ( invert ) {
// Invert of regular
if (
mouseOnAxis < targetS1 + ( targetLength * invertedSwapThreshold / 2 ) ||
mouseOnAxis > targetS2 - ( targetLength * invertedSwapThreshold / 2 )
)
{
return ( ( mouseOnAxis > targetS1 + targetLength / 2 ) ? 1 : - 1 ) ;
}
}
return 0 ;
}
2019-01-23 06:23:14 -05:00
/ * *
* Gets the direction dragEl must be swapped relative to target in order to make it
* seem that dragEl has been "inserted" into that element ' s position
* @ param { HTMLElement } target The target whose position dragEl is being inserted at
* @ param { Object } options options of the parent sortable
* @ return { Number } Direction dragEl must be swapped
* /
function _getInsertDirection ( target , options ) {
var dragElIndex = _index ( dragEl , options . draggable ) ,
targetIndex = _index ( target , options . draggable ) ;
if ( dragElIndex < targetIndex ) {
return 1 ;
} else {
return - 1 ;
}
}
2018-12-20 04:08:22 -05:00
/ * *
* Generate id
* @ param { HTMLElement } el
* @ returns { String }
* @ private
* /
function _generateId ( el ) {
var str = el . tagName + el . className + el . src + el . href + el . textContent ,
i = str . length ,
sum = 0 ;
while ( i -- ) {
sum += str . charCodeAt ( i ) ;
}
return sum . toString ( 36 ) ;
}
/ * *
* Returns the index of an element within its parent for a selected set of
* elements
* @ param { HTMLElement } el
* @ param { selector } selector
* @ return { number }
* /
function _index ( el , selector ) {
var index = 0 ;
if ( ! el || ! el . parentNode ) {
return - 1 ;
}
while ( el && ( el = el . previousElementSibling ) ) {
2019-01-23 06:23:14 -05:00
if ( ( el . nodeName . toUpperCase ( ) !== 'TEMPLATE' ) && el !== cloneEl ) {
2018-12-20 04:08:22 -05:00
index ++ ;
}
}
return index ;
}
function _matches ( /**HTMLElement*/ el , /**String*/ selector ) {
if ( el ) {
try {
if ( el . matches ) {
return el . matches ( selector ) ;
} else if ( el . msMatchesSelector ) {
return el . msMatchesSelector ( selector ) ;
2019-01-23 06:23:14 -05:00
} else if ( el . webkitMatchesSelector ) {
return el . webkitMatchesSelector ( selector ) ;
2018-12-20 04:08:22 -05:00
}
} catch ( _ ) {
return false ;
}
}
return false ;
}
var _throttleTimeout ;
function _throttle ( callback , ms ) {
return function ( ) {
if ( ! _throttleTimeout ) {
var args = arguments ,
2019-01-23 06:23:14 -05:00
_this = this ;
2018-12-20 04:08:22 -05:00
_throttleTimeout = setTimeout ( function ( ) {
if ( args . length === 1 ) {
callback . call ( _this , args [ 0 ] ) ;
} else {
callback . apply ( _this , args ) ;
}
_throttleTimeout = void 0 ;
} , ms ) ;
}
} ;
}
function _cancelThrottle ( ) {
clearTimeout ( _throttleTimeout ) ;
_throttleTimeout = void 0 ;
}
function _extend ( dst , src ) {
if ( dst && src ) {
for ( var key in src ) {
if ( src . hasOwnProperty ( key ) ) {
dst [ key ] = src [ key ] ;
}
}
}
return dst ;
}
function _clone ( el ) {
if ( Polymer && Polymer . dom ) {
return Polymer . dom ( el ) . cloneNode ( true ) ;
}
else if ( $ ) {
return $ ( el ) . clone ( true ) [ 0 ] ;
}
else {
return el . cloneNode ( true ) ;
}
}
function _saveInputCheckedState ( root ) {
savedInputChecked . length = 0 ;
var inputs = root . getElementsByTagName ( 'input' ) ;
var idx = inputs . length ;
while ( idx -- ) {
var el = inputs [ idx ] ;
el . checked && savedInputChecked . push ( el ) ;
}
}
function _nextTick ( fn ) {
return setTimeout ( fn , 0 ) ;
}
function _cancelNextTick ( id ) {
return clearTimeout ( id ) ;
}
2019-01-23 06:23:14 -05:00
/ * *
* Returns the "bounding client rect" of given element
* @ param { HTMLElement } el The element whose boundingClientRect is wanted
* @ param { [ HTMLElement ] } container the parent the element will be placed in
* @ param { [ Boolean ] } adjustForTransform Whether the rect should compensate for parent ' s transform
* ( used for fixed positioning on el )
* @ return { Object } The boundingClientRect of el
* /
function _getRect ( el , container , adjustForTransform ) {
if ( ! el . getBoundingClientRect && el !== win ) return ;
var elRect ,
top ,
left ,
bottom ,
right ,
height ,
width ;
if ( el !== win ) {
elRect = el . getBoundingClientRect ( ) ;
top = elRect . top ;
left = elRect . left ;
bottom = elRect . bottom ;
right = elRect . right ;
height = elRect . height ;
width = elRect . width ;
} else {
top = 0 ;
left = 0 ;
bottom = window . innerHeight ;
right = window . innerWidth ;
height = window . innerHeight ;
width = window . innerWidth ;
2018-12-20 04:08:22 -05:00
}
2019-01-23 06:23:14 -05:00
if ( adjustForTransform && el !== win ) {
// Adjust for translate()
container = container || el . parentNode ;
// solves #1123 (see: https://stackoverflow.com/a/37953806/6088312)
// Not needed on <= IE11
if ( ! IE11OrLess ) {
do {
if ( container && container . getBoundingClientRect && _css ( container , 'transform' ) !== 'none' ) {
var containerRect = container . getBoundingClientRect ( ) ;
// Set relative to edges of padding box of container
top -= containerRect . top + parseInt ( _css ( container , 'border-top-width' ) ) ;
left -= containerRect . left + parseInt ( _css ( container , 'border-left-width' ) ) ;
bottom = top + elRect . height ;
right = left + elRect . width ;
break ;
}
/* jshint boss:true */
} while ( container = container . parentNode ) ;
}
// Adjust for scale()
var matrix = _matrix ( el ) ,
scaleX = matrix && matrix . a ,
scaleY = matrix && matrix . d ;
if ( matrix ) {
top /= scaleY ;
left /= scaleX ;
width /= scaleX ;
height /= scaleY ;
bottom = top + height ;
right = left + width ;
}
}
return {
top : top ,
left : left ,
bottom : bottom ,
right : right ,
width : width ,
height : height
} ;
2018-12-20 04:08:22 -05:00
}
2019-01-23 06:23:14 -05:00
/ * *
* Checks if a side of an element is scrolled past a side of it ' s parents
* @ param { HTMLElement } el The element who ' s side being scrolled out of view is in question
* @ param { String } side Side of the element in question ( 'top' , 'left' , 'right' , 'bottom' )
* @ return { Boolean } Whether the element is overflowing the viewport on the given side of it ' s parent
* /
function _isScrolledPast ( el , side ) {
var parent = _getParentAutoScrollElement ( parent , true ) ,
elSide = _getRect ( el ) [ side ] ;
/* jshint boss:true */
while ( parent ) {
var parentSide = _getRect ( parent ) [ side ] ,
visible ;
if ( side === 'top' || side === 'left' ) {
visible = elSide >= parentSide ;
} else {
visible = elSide <= parentSide ;
}
if ( ! visible ) return true ;
if ( parent === win ) break ;
parent = _getParentAutoScrollElement ( parent , false ) ;
}
return false ;
}
// Fixed #973:
_on ( document , 'touchmove' , function ( evt ) {
if ( ( Sortable . active || awaitingDragStarted ) && evt . cancelable ) {
evt . preventDefault ( ) ;
}
} ) ;
2018-12-20 04:08:22 -05:00
// Export utils
Sortable . utils = {
on : _on ,
off : _off ,
css : _css ,
find : _find ,
is : function ( el , selector ) {
return ! ! _closest ( el , selector , el , false ) ;
} ,
extend : _extend ,
throttle : _throttle ,
closest : _closest ,
toggleClass : _toggleClass ,
clone : _clone ,
index : _index ,
nextTick : _nextTick ,
cancelNextTick : _cancelNextTick ,
detectDirection : _detectDirection ,
getChild : _getChild
} ;
/ * *
* Create sortable instance
* @ param { HTMLElement } el
* @ param { Object } [ options ]
* /
Sortable . create = function ( el , options ) {
return new Sortable ( el , options ) ;
} ;
// Export
2019-02-27 08:01:44 -05:00
Sortable . version = '1.8.3' ;
2018-12-20 04:08:22 -05:00
return Sortable ;
} ) ;