sortablejs 1.8.1 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Sortable.js CHANGED
@@ -41,6 +41,8 @@
41
41
 
42
42
  oldIndex,
43
43
  newIndex,
44
+ oldDraggableIndex,
45
+ newDraggableIndex,
44
46
 
45
47
  activeGroup,
46
48
  putSortable,
@@ -70,8 +72,10 @@
70
72
 
71
73
  targetMoveDistance,
72
74
 
75
+ // For positioning ghost absolutely
76
+ ghostRelativeParent,
77
+ ghostRelativeParentInitialScroll = [], // (left, top)
73
78
 
74
- forRepaintDummy,
75
79
  realDragElRect, // dragEl rect after current animation
76
80
 
77
81
  /** @const */
@@ -94,7 +98,11 @@
94
98
 
95
99
  IE11OrLess = !!navigator.userAgent.match(/(?:Trident.*rv[ :]?11\.|msie|iemobile)/i),
96
100
  Edge = !!navigator.userAgent.match(/Edge/i),
97
- // FireFox = !!navigator.userAgent.match(/firefox/i),
101
+ FireFox = !!navigator.userAgent.match(/firefox/i),
102
+ Safari = !!(navigator.userAgent.match(/safari/i) && !navigator.userAgent.match(/chrome/i) && !navigator.userAgent.match(/android/i)),
103
+ IOS = !!(navigator.userAgent.match(/iP(ad|od|hone)/i)),
104
+
105
+ PositionGhostAbsolutely = IOS,
98
106
 
99
107
  CSSFloatProperty = Edge || IE11OrLess ? 'cssFloat' : 'float',
100
108
 
@@ -116,22 +124,40 @@
116
124
 
117
125
  abs = Math.abs,
118
126
  min = Math.min,
127
+ max = Math.max,
119
128
 
120
129
  savedInputChecked = [],
121
130
 
122
131
  _detectDirection = function(el, options) {
123
132
  var elCSS = _css(el),
124
- elWidth = parseInt(elCSS.width),
133
+ elWidth = parseInt(elCSS.width)
134
+ - parseInt(elCSS.paddingLeft)
135
+ - parseInt(elCSS.paddingRight)
136
+ - parseInt(elCSS.borderLeftWidth)
137
+ - parseInt(elCSS.borderRightWidth),
125
138
  child1 = _getChild(el, 0, options),
126
139
  child2 = _getChild(el, 1, options),
127
140
  firstChildCSS = child1 && _css(child1),
128
141
  secondChildCSS = child2 && _css(child2),
129
142
  firstChildWidth = firstChildCSS && parseInt(firstChildCSS.marginLeft) + parseInt(firstChildCSS.marginRight) + _getRect(child1).width,
130
143
  secondChildWidth = secondChildCSS && parseInt(secondChildCSS.marginLeft) + parseInt(secondChildCSS.marginRight) + _getRect(child2).width;
144
+
131
145
  if (elCSS.display === 'flex') {
132
146
  return elCSS.flexDirection === 'column' || elCSS.flexDirection === 'column-reverse'
133
147
  ? 'vertical' : 'horizontal';
134
148
  }
149
+
150
+ if (elCSS.display === 'grid') {
151
+ return elCSS.gridTemplateColumns.split(' ').length <= 1 ? 'vertical' : 'horizontal';
152
+ }
153
+
154
+ if (child1 && firstChildCSS.float !== 'none') {
155
+ var touchingSideChild2 = firstChildCSS.float === 'left' ? 'left' : 'right';
156
+
157
+ return child2 && (secondChildCSS.clear === 'both' || secondChildCSS.clear === touchingSideChild2) ?
158
+ 'vertical' : 'horizontal';
159
+ }
160
+
135
161
  return (child1 &&
136
162
  (
137
163
  firstChildCSS.display === 'block' ||
@@ -156,14 +182,14 @@
156
182
  */
157
183
  _detectNearestEmptySortable = function(x, y) {
158
184
  for (var i = 0; i < sortables.length; i++) {
159
- if (sortables[i].children.length) continue;
185
+ if (_lastChild(sortables[i])) continue;
160
186
 
161
187
  var rect = _getRect(sortables[i]),
162
188
  threshold = sortables[i][expando].options.emptyInsertThreshold,
163
189
  insideHorizontally = x >= (rect.left - threshold) && x <= (rect.right + threshold),
164
190
  insideVertically = y >= (rect.top - threshold) && y <= (rect.bottom + threshold);
165
191
 
166
- if (insideHorizontally && insideVertically) {
192
+ if (threshold && insideHorizontally && insideVertically) {
167
193
  return sortables[i];
168
194
  }
169
195
  }
@@ -197,7 +223,7 @@
197
223
 
198
224
  _getParentAutoScrollElement = function(el, includeSelf) {
199
225
  // skip to window
200
- if (!el || !el.getBoundingClientRect) return win;
226
+ if (!el || !el.getBoundingClientRect) return _getWindowScrollingElement();
201
227
 
202
228
  var elem = el;
203
229
  var gotSelf = false;
@@ -209,7 +235,7 @@
209
235
  elem.clientWidth < elem.scrollWidth && (elemCSS.overflowX == 'auto' || elemCSS.overflowX == 'scroll') ||
210
236
  elem.clientHeight < elem.scrollHeight && (elemCSS.overflowY == 'auto' || elemCSS.overflowY == 'scroll')
211
237
  ) {
212
- if (!elem || !elem.getBoundingClientRect || elem === document.body) return win;
238
+ if (!elem || !elem.getBoundingClientRect || elem === document.body) return _getWindowScrollingElement();
213
239
 
214
240
  if (gotSelf || includeSelf) return elem;
215
241
  gotSelf = true;
@@ -218,7 +244,20 @@
218
244
  /* jshint boss:true */
219
245
  } while (elem = elem.parentNode);
220
246
 
221
- return win;
247
+ return _getWindowScrollingElement();
248
+ },
249
+
250
+ _getWindowScrollingElement = function() {
251
+ if (IE11OrLess) {
252
+ return document.documentElement;
253
+ } else {
254
+ return document.scrollingElement;
255
+ }
256
+ },
257
+
258
+ _scrollBy = function(el, x, y) {
259
+ el.scrollLeft += x;
260
+ el.scrollTop += y;
222
261
  },
223
262
 
224
263
  _autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl, /**Boolean*/isFallback) {
@@ -231,8 +270,7 @@
231
270
  x = evt.clientX,
232
271
  y = evt.clientY,
233
272
 
234
- winWidth = window.innerWidth,
235
- winHeight = window.innerHeight,
273
+ winScroller = _getWindowScrollingElement(),
236
274
 
237
275
  scrollThisInstance = false;
238
276
 
@@ -279,28 +317,20 @@
279
317
  scrollPosY;
280
318
 
281
319
 
282
- if (el !== win) {
283
- scrollWidth = el.scrollWidth;
284
- scrollHeight = el.scrollHeight;
285
-
286
- css = _css(el);
320
+ scrollWidth = el.scrollWidth;
321
+ scrollHeight = el.scrollHeight;
287
322
 
288
- canScrollX = width < scrollWidth && (css.overflowX === 'auto' || css.overflowX === 'scroll');
289
- canScrollY = height < scrollHeight && (css.overflowY === 'auto' || css.overflowY === 'scroll');
290
-
291
- scrollPosX = el.scrollLeft;
292
- scrollPosY = el.scrollTop;
293
- } else {
294
- scrollWidth = document.documentElement.scrollWidth;
295
- scrollHeight = document.documentElement.scrollHeight;
323
+ css = _css(el);
296
324
 
297
- css = _css(document.documentElement);
325
+ scrollPosX = el.scrollLeft;
326
+ scrollPosY = el.scrollTop;
298
327
 
328
+ if (el === winScroller) {
299
329
  canScrollX = width < scrollWidth && (css.overflowX === 'auto' || css.overflowX === 'scroll' || css.overflowX === 'visible');
300
330
  canScrollY = height < scrollHeight && (css.overflowY === 'auto' || css.overflowY === 'scroll' || css.overflowY === 'visible');
301
-
302
- scrollPosX = document.documentElement.scrollLeft;
303
- scrollPosY = document.documentElement.scrollTop;
331
+ } else {
332
+ canScrollX = width < scrollWidth && (css.overflowX === 'auto' || css.overflowX === 'scroll');
333
+ canScrollY = height < scrollHeight && (css.overflowY === 'auto' || css.overflowY === 'scroll');
304
334
  }
305
335
 
306
336
  vx = canScrollX && (abs(right - x) <= sens && (scrollPosX + width) < scrollWidth) - (abs(left - x) <= sens && !!scrollPosX);
@@ -330,6 +360,7 @@
330
360
  // emulate drag over during autoscroll (fallback), emulating native DnD behaviour
331
361
  if (isFallback && this.layer === 0) {
332
362
  Sortable.active._emulateDragOver(true);
363
+ Sortable.active._onTouchMove(touchEvt, true);
333
364
  }
334
365
  var scrollOffsetY = autoScrolls[this.layer].vy ? autoScrolls[this.layer].vy * speed : 0;
335
366
  var scrollOffsetX = autoScrolls[this.layer].vx ? autoScrolls[this.layer].vx * speed : 0;
@@ -339,17 +370,13 @@
339
370
  return;
340
371
  }
341
372
  }
342
- if (autoScrolls[this.layer].el === win) {
343
- win.scrollTo(win.pageXOffset + scrollOffsetX, win.pageYOffset + scrollOffsetY);
344
- } else {
345
- autoScrolls[this.layer].el.scrollTop += scrollOffsetY;
346
- autoScrolls[this.layer].el.scrollLeft += scrollOffsetX;
347
- }
373
+
374
+ _scrollBy(autoScrolls[this.layer].el, scrollOffsetX, scrollOffsetY);
348
375
  }).bind({layer: layersOut}), 24);
349
376
  }
350
377
  }
351
378
  layersOut++;
352
- } while (options.bubbleScroll && currentParent !== win && (currentParent = _getParentAutoScrollElement(currentParent, false)));
379
+ } while (options.bubbleScroll && currentParent !== winScroller && (currentParent = _getParentAutoScrollElement(currentParent, false)));
353
380
  scrolling = scrollThisInstance; // in case another function catches scrolling as false in between when it is not
354
381
  }
355
382
  }, 30),
@@ -408,29 +435,6 @@
408
435
  dragEl.parentNode[expando] && dragEl.parentNode[expando]._computeIsAligned(evt);
409
436
  },
410
437
 
411
- _isTrueParentSortable = function(el, target) {
412
- var trueParent = target;
413
- while (!trueParent[expando]) {
414
- trueParent = trueParent.parentNode;
415
- }
416
-
417
- return el === trueParent;
418
- },
419
-
420
- _artificalBubble = function(sortable, originalEvt, method) {
421
- // Artificial IE bubbling
422
- var nextParent = sortable.parentNode;
423
- while (nextParent && !nextParent[expando]) {
424
- nextParent = nextParent.parentNode;
425
- }
426
-
427
- if (nextParent) {
428
- nextParent[expando][method](_extend(originalEvt, {
429
- artificialBubble: true
430
- }));
431
- }
432
- },
433
-
434
438
  _hideGhostForTarget = function() {
435
439
  if (!supportCssPointerEvents && ghostEl) {
436
440
  _css(ghostEl, 'display', 'none');
@@ -457,21 +461,22 @@
457
461
 
458
462
  var nearestEmptyInsertDetectEvent = function(evt) {
459
463
  if (dragEl) {
464
+ evt = evt.touches ? evt.touches[0] : evt;
460
465
  var nearest = _detectNearestEmptySortable(evt.clientX, evt.clientY);
461
466
 
462
467
  if (nearest) {
463
- nearest[expando]._onDragOver({
464
- clientX: evt.clientX,
465
- clientY: evt.clientY,
466
- target: nearest,
467
- rootEl: nearest
468
- });
468
+ // Create imitation event
469
+ var event = {};
470
+ for (var i in evt) {
471
+ event[i] = evt[i];
472
+ }
473
+ event.target = event.rootEl = nearest;
474
+ event.preventDefault = void 0;
475
+ event.stopPropagation = void 0;
476
+ nearest[expando]._onDragOver(event);
469
477
  }
470
478
  }
471
479
  };
472
- // We do not want this to be triggered if completed (bubbling canceled), so only define it here
473
- document.addEventListener('dragover', nearestEmptyInsertDetectEvent);
474
- document.addEventListener('mousemove', nearestEmptyInsertDetectEvent);
475
480
 
476
481
  /**
477
482
  * @class Sortable
@@ -501,7 +506,7 @@
501
506
  scrollSensitivity: 30,
502
507
  scrollSpeed: 10,
503
508
  bubbleScroll: true,
504
- draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*',
509
+ draggable: /[uo]l/i.test(el.nodeName) ? '>li' : '>*',
505
510
  swapThreshold: 1, // percentage; 0 <= x <= 1
506
511
  invertSwap: false, // invert always
507
512
  invertedSwapThreshold: null, // will be set to same as swapThreshold if default
@@ -524,16 +529,14 @@
524
529
  dragoverBubble: false,
525
530
  dataIdAttr: 'data-id',
526
531
  delay: 0,
532
+ delayOnTouchOnly: false,
527
533
  touchStartThreshold: parseInt(window.devicePixelRatio, 10) || 1,
528
534
  forceFallback: false,
529
535
  fallbackClass: 'sortable-fallback',
530
536
  fallbackOnBody: false,
531
537
  fallbackTolerance: 0,
532
538
  fallbackOffset: {x: 0, y: 0},
533
- supportPointer: Sortable.supportPointer !== false && (
534
- ('PointerEvent' in window) ||
535
- window.navigator && ('msPointerEnabled' in window.navigator) // microsoft
536
- ),
539
+ supportPointer: Sortable.supportPointer !== false && ('PointerEvent' in window),
537
540
  emptyInsertThreshold: 5
538
541
  };
539
542
 
@@ -555,6 +558,11 @@
555
558
  // Setup drag mode
556
559
  this.nativeDraggable = options.forceFallback ? false : supportDraggable;
557
560
 
561
+ if (this.nativeDraggable) {
562
+ // Touch start threshold cannot be greater than the native dragstart threshold
563
+ this.options.touchStartThreshold = 1;
564
+ }
565
+
558
566
  // Bind events
559
567
  if (options.supportPointer) {
560
568
  _on(el, 'pointerdown', this._onTapStart);
@@ -617,7 +625,6 @@
617
625
 
618
626
  _onTapStart: function (/** Event|TouchEvent */evt) {
619
627
  if (!evt.cancelable) return;
620
-
621
628
  var _this = this,
622
629
  el = this.el,
623
630
  options = this.options,
@@ -627,17 +634,11 @@
627
634
  target = (touch || evt).target,
628
635
  originalTarget = evt.target.shadowRoot && ((evt.path && evt.path[0]) || (evt.composedPath && evt.composedPath()[0])) || target,
629
636
  filter = options.filter,
630
- startIndex;
637
+ startIndex,
638
+ startDraggableIndex;
631
639
 
632
640
  _saveInputCheckedState(el);
633
641
 
634
-
635
- // IE: Calls events in capture mode if event element is nested. This ensures only correct element's _onTapStart goes through.
636
- // This process is also done in _onDragOver
637
- if (IE11OrLess && !evt.artificialBubble && !_isTrueParentSortable(el, target)) {
638
- return;
639
- }
640
-
641
642
  // Don't trigger start event when an element is been dragged, otherwise the evt.oldindex always wrong when set option.group.
642
643
  if (dragEl) {
643
644
  return;
@@ -654,12 +655,6 @@
654
655
 
655
656
  target = _closest(target, options.draggable, el, false);
656
657
 
657
- if (!target) {
658
- if (IE11OrLess) {
659
- _artificalBubble(el, evt, '_onTapStart');
660
- }
661
- return;
662
- }
663
658
 
664
659
  if (lastDownEl === target) {
665
660
  // Ignoring duplicate `down`
@@ -667,12 +662,13 @@
667
662
  }
668
663
 
669
664
  // Get the index of the dragged element within its parent
670
- startIndex = _index(target, options.draggable);
665
+ startIndex = _index(target);
666
+ startDraggableIndex = _index(target, options.draggable);
671
667
 
672
668
  // Check filter
673
669
  if (typeof filter === 'function') {
674
670
  if (filter.call(this, evt, target, this)) {
675
- _dispatchEvent(_this, originalTarget, 'filter', target, el, el, startIndex);
671
+ _dispatchEvent(_this, originalTarget, 'filter', target, el, el, startIndex, undefined, startDraggableIndex);
676
672
  preventOnFilter && evt.cancelable && evt.preventDefault();
677
673
  return; // cancel dnd
678
674
  }
@@ -682,7 +678,7 @@
682
678
  criteria = _closest(originalTarget, criteria.trim(), el, false);
683
679
 
684
680
  if (criteria) {
685
- _dispatchEvent(_this, criteria, 'filter', target, el, el, startIndex);
681
+ _dispatchEvent(_this, criteria, 'filter', target, el, el, startIndex, undefined, startDraggableIndex);
686
682
  return true;
687
683
  }
688
684
  });
@@ -698,7 +694,7 @@
698
694
  }
699
695
 
700
696
  // Prepare `dragstart`
701
- this._prepareDragStart(evt, touch, target, startIndex);
697
+ this._prepareDragStart(evt, touch, target, startIndex, startDraggableIndex);
702
698
  },
703
699
 
704
700
 
@@ -712,8 +708,9 @@
712
708
 
713
709
  // IE does not seem to have native autoscroll,
714
710
  // Edge's autoscroll seems too conditional,
711
+ // MACOS Safari does not have autoscroll,
715
712
  // Firefox and Chrome are good
716
- if (fallback || Edge || IE11OrLess) {
713
+ if (fallback || Edge || IE11OrLess || Safari) {
717
714
  _autoScroll(evt, _this.options, elem, fallback);
718
715
 
719
716
  // Listener for pointer element change
@@ -745,7 +742,7 @@
745
742
 
746
743
  } else {
747
744
  // if DnD is enabled (and browser has good autoscrolling), first autoscroll will already scroll, so get parent autoscroll of first autoscroll
748
- if (!_this.options.bubbleScroll || _getParentAutoScrollElement(elem, true) === window) {
745
+ if (!_this.options.bubbleScroll || _getParentAutoScrollElement(elem, true) === _getWindowScrollingElement()) {
749
746
  _clearAutoScrolls();
750
747
  return;
751
748
  }
@@ -753,7 +750,7 @@
753
750
  }
754
751
  },
755
752
 
756
- _prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target, /** Number */startIndex) {
753
+ _prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target, /** Number */startIndex, /** Number */startDraggableIndex) {
757
754
  var _this = this,
758
755
  el = _this.el,
759
756
  options = _this.options,
@@ -768,6 +765,7 @@
768
765
  lastDownEl = target;
769
766
  activeGroup = options.group;
770
767
  oldIndex = startIndex;
768
+ oldDraggableIndex = startDraggableIndex;
771
769
 
772
770
  tapEvt = {
773
771
  target: dragEl,
@@ -786,16 +784,17 @@
786
784
  dragStartFn = function () {
787
785
  // Delayed drag has been triggered
788
786
  // we can re-enable the events: touchmove/mousemove
789
- _this._disableDelayedDrag();
787
+ _this._disableDelayedDragEvents();
790
788
 
791
- // Make the element draggable
792
- dragEl.draggable = _this.nativeDraggable;
789
+ if (!FireFox && _this.nativeDraggable) {
790
+ dragEl.draggable = true;
791
+ }
793
792
 
794
793
  // Bind the events: dragstart/dragend
795
794
  _this._triggerDragStart(evt, touch);
796
795
 
797
796
  // Drag start event
798
- _dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, rootEl, oldIndex);
797
+ _dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, rootEl, oldIndex, undefined, oldDraggableIndex);
799
798
 
800
799
  // Chosen item
801
800
  _toggleClass(dragEl, options.chosenClass, true);
@@ -806,16 +805,22 @@
806
805
  _find(dragEl, criteria.trim(), _disableDraggable);
807
806
  });
808
807
 
809
- if (options.supportPointer) {
810
- _on(ownerDocument, 'pointerup', _this._onDrop);
811
- _on(ownerDocument, 'pointercancel', _this._onDrop);
812
- } else {
813
- _on(ownerDocument, 'mouseup', _this._onDrop);
814
- _on(ownerDocument, 'touchend', _this._onDrop);
815
- _on(ownerDocument, 'touchcancel', _this._onDrop);
808
+ _on(ownerDocument, 'dragover', nearestEmptyInsertDetectEvent);
809
+ _on(ownerDocument, 'mousemove', nearestEmptyInsertDetectEvent);
810
+ _on(ownerDocument, 'touchmove', nearestEmptyInsertDetectEvent);
811
+
812
+ _on(ownerDocument, 'mouseup', _this._onDrop);
813
+ _on(ownerDocument, 'touchend', _this._onDrop);
814
+ _on(ownerDocument, 'touchcancel', _this._onDrop);
815
+
816
+ // Make dragEl draggable (must be before delay for FireFox)
817
+ if (FireFox && this.nativeDraggable) {
818
+ this.options.touchStartThreshold = 4;
819
+ dragEl.draggable = true;
816
820
  }
817
821
 
818
- if (options.delay) {
822
+ // Delay is impossible for native DnD in Edge or IE
823
+ if (options.delay && (options.delayOnTouchOnly ? touch : true) && (!this.nativeDraggable || !(Edge || IE11OrLess))) {
819
824
  // If the user moves the pointer or let go the click or touch
820
825
  // before the delay has been reached:
821
826
  // disable the delayed drag
@@ -835,17 +840,22 @@
835
840
 
836
841
  _delayedDragTouchMoveHandler: function (/** TouchEvent|PointerEvent **/e) {
837
842
  var touch = e.touches ? e.touches[0] : e;
838
- if (min(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY))
839
- >= this.options.touchStartThreshold
843
+ if (max(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY))
844
+ >= Math.floor(this.options.touchStartThreshold / (this.nativeDraggable && window.devicePixelRatio || 1))
840
845
  ) {
841
846
  this._disableDelayedDrag();
842
847
  }
843
848
  },
844
849
 
845
850
  _disableDelayedDrag: function () {
846
- var ownerDocument = this.el.ownerDocument;
847
-
851
+ dragEl && _disableDraggable(dragEl);
848
852
  clearTimeout(this._dragStartTimer);
853
+
854
+ this._disableDelayedDragEvents();
855
+ },
856
+
857
+ _disableDelayedDragEvents: function () {
858
+ var ownerDocument = this.el.ownerDocument;
849
859
  _off(ownerDocument, 'mouseup', this._disableDelayedDrag);
850
860
  _off(ownerDocument, 'touchend', this._disableDelayedDrag);
851
861
  _off(ownerDocument, 'touchcancel', this._disableDelayedDrag);
@@ -883,7 +893,7 @@
883
893
  }
884
894
  },
885
895
 
886
- _dragStarted: function (fallback) {
896
+ _dragStarted: function (fallback, evt) {
887
897
  awaitingDragStarted = false;
888
898
  if (rootEl && dragEl) {
889
899
  if (this.nativeDraggable) {
@@ -904,15 +914,15 @@
904
914
  fallback && this._appendGhost();
905
915
 
906
916
  // Drag start event
907
- _dispatchEvent(this, rootEl, 'start', dragEl, rootEl, rootEl, oldIndex);
917
+ _dispatchEvent(this, rootEl, 'start', dragEl, rootEl, rootEl, oldIndex, undefined, oldDraggableIndex, undefined, evt);
908
918
  } else {
909
919
  this._nulling();
910
920
  }
911
921
  },
912
922
 
913
- _emulateDragOver: function (bypassLastTouchCheck) {
923
+ _emulateDragOver: function (forAutoScroll) {
914
924
  if (touchEvt) {
915
- if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY && !bypassLastTouchCheck) {
925
+ if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY && !forAutoScroll) {
916
926
  return;
917
927
  }
918
928
  this._lastX = touchEvt.clientX;
@@ -925,6 +935,7 @@
925
935
 
926
936
  while (target && target.shadowRoot) {
927
937
  target = target.shadowRoot.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
938
+ if (target === parent) break;
928
939
  parent = target;
929
940
  }
930
941
 
@@ -957,9 +968,8 @@
957
968
  },
958
969
 
959
970
 
960
- _onTouchMove: function (/**TouchEvent*/evt) {
971
+ _onTouchMove: function (/**TouchEvent*/evt, forAutoScroll) {
961
972
  if (tapEvt) {
962
- if (!evt.cancelable) return;
963
973
  var options = this.options,
964
974
  fallbackTolerance = options.fallbackTolerance,
965
975
  fallbackOffset = options.fallbackOffset,
@@ -967,11 +977,15 @@
967
977
  matrix = ghostEl && _matrix(ghostEl),
968
978
  scaleX = ghostEl && matrix && matrix.a,
969
979
  scaleY = ghostEl && matrix && matrix.d,
970
- dx = ((touch.clientX - tapEvt.clientX) + fallbackOffset.x) / (scaleX ? scaleX : 1),
971
- dy = ((touch.clientY - tapEvt.clientY) + fallbackOffset.y) / (scaleY ? scaleY : 1),
980
+ relativeScrollOffset = PositionGhostAbsolutely && ghostRelativeParent && _getRelativeScrollOffset(ghostRelativeParent),
981
+ dx = ((touch.clientX - tapEvt.clientX)
982
+ + fallbackOffset.x) / (scaleX || 1)
983
+ + (relativeScrollOffset ? (relativeScrollOffset[0] - ghostRelativeParentInitialScroll[0]) : 0) / (scaleX || 1),
984
+ dy = ((touch.clientY - tapEvt.clientY)
985
+ + fallbackOffset.y) / (scaleY || 1)
986
+ + (relativeScrollOffset ? (relativeScrollOffset[1] - ghostRelativeParentInitialScroll[1]) : 0) / (scaleY || 1),
972
987
  translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';
973
988
 
974
-
975
989
  // only set the status to dragging, when we are actually dragging
976
990
  if (!Sortable.active && !awaitingDragStarted) {
977
991
  if (fallbackTolerance &&
@@ -982,13 +996,11 @@
982
996
  this._onDragStart(evt, true);
983
997
  }
984
998
 
985
- this._handleAutoScroll(touch, true);
986
-
999
+ !forAutoScroll && this._handleAutoScroll(touch, true);
987
1000
 
988
1001
  moved = true;
989
1002
  touchEvt = touch;
990
1003
 
991
-
992
1004
  _css(ghostEl, 'webkitTransform', translate3d);
993
1005
  _css(ghostEl, 'mozTransform', translate3d);
994
1006
  _css(ghostEl, 'msTransform', translate3d);
@@ -999,11 +1011,46 @@
999
1011
  },
1000
1012
 
1001
1013
  _appendGhost: function () {
1014
+ // Bug if using scale(): https://stackoverflow.com/questions/2637058
1015
+ // Not being adjusted for
1002
1016
  if (!ghostEl) {
1003
- var rect = _getRect(dragEl, this.options.fallbackOnBody ? document.body : rootEl, true),
1017
+ var container = this.options.fallbackOnBody ? document.body : rootEl,
1018
+ rect = _getRect(dragEl, true, container, !PositionGhostAbsolutely),
1004
1019
  css = _css(dragEl),
1005
1020
  options = this.options;
1006
1021
 
1022
+ // Position absolutely
1023
+ if (PositionGhostAbsolutely) {
1024
+ // Get relatively positioned parent
1025
+ ghostRelativeParent = container;
1026
+
1027
+ while (
1028
+ _css(ghostRelativeParent, 'position') === 'static' &&
1029
+ _css(ghostRelativeParent, 'transform') === 'none' &&
1030
+ ghostRelativeParent !== document
1031
+ ) {
1032
+ ghostRelativeParent = ghostRelativeParent.parentNode;
1033
+ }
1034
+
1035
+ if (ghostRelativeParent !== document) {
1036
+ var ghostRelativeParentRect = _getRect(ghostRelativeParent, true);
1037
+
1038
+ rect.top -= ghostRelativeParentRect.top;
1039
+ rect.left -= ghostRelativeParentRect.left;
1040
+ }
1041
+
1042
+ if (ghostRelativeParent !== document.body && ghostRelativeParent !== document.documentElement) {
1043
+ if (ghostRelativeParent === document) ghostRelativeParent = _getWindowScrollingElement();
1044
+
1045
+ rect.top += ghostRelativeParent.scrollTop;
1046
+ rect.left += ghostRelativeParent.scrollLeft;
1047
+ } else {
1048
+ ghostRelativeParent = _getWindowScrollingElement();
1049
+ }
1050
+ ghostRelativeParentInitialScroll = _getRelativeScrollOffset(ghostRelativeParent);
1051
+ }
1052
+
1053
+
1007
1054
  ghostEl = dragEl.cloneNode(true);
1008
1055
 
1009
1056
  _toggleClass(ghostEl, options.ghostClass, false);
@@ -1017,11 +1064,11 @@
1017
1064
  _css(ghostEl, 'width', rect.width);
1018
1065
  _css(ghostEl, 'height', rect.height);
1019
1066
  _css(ghostEl, 'opacity', '0.8');
1020
- _css(ghostEl, 'position', 'fixed');
1067
+ _css(ghostEl, 'position', (PositionGhostAbsolutely ? 'absolute' : 'fixed'));
1021
1068
  _css(ghostEl, 'zIndex', '100000');
1022
1069
  _css(ghostEl, 'pointerEvents', 'none');
1023
1070
 
1024
- options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
1071
+ container.appendChild(ghostEl);
1025
1072
  }
1026
1073
  },
1027
1074
 
@@ -1061,7 +1108,6 @@
1061
1108
  _off(document, 'mouseup', _this._onDrop);
1062
1109
  _off(document, 'touchend', _this._onDrop);
1063
1110
  _off(document, 'touchcancel', _this._onDrop);
1064
- _off(document, 'pointercancel', _this._onDrop);
1065
1111
 
1066
1112
  if (dataTransfer) {
1067
1113
  dataTransfer.effectAllowed = 'move';
@@ -1076,10 +1122,14 @@
1076
1122
 
1077
1123
  awaitingDragStarted = true;
1078
1124
 
1079
- _this._dragStartId = _nextTick(_this._dragStarted.bind(_this, fallback));
1125
+ _this._dragStartId = _nextTick(_this._dragStarted.bind(_this, fallback, evt));
1080
1126
  _on(document, 'selectstart', _this);
1127
+ if (Safari) {
1128
+ _css(document.body, 'user-select', 'none');
1129
+ }
1081
1130
  },
1082
1131
 
1132
+
1083
1133
  // Returns true - if no further action is needed (either inserted or another condition)
1084
1134
  _onDragOver: function (/**Event*/evt) {
1085
1135
  var el = this.el,
@@ -1096,23 +1146,30 @@
1096
1146
 
1097
1147
  if (_silent) return;
1098
1148
 
1099
- // IE event order fix
1100
- if (IE11OrLess && !evt.rootEl && !evt.artificialBubble && !_isTrueParentSortable(el, target)) {
1101
- return;
1102
- }
1149
+ // Return invocation when dragEl is inserted (or completed)
1150
+ function completed(insertion) {
1151
+ if (insertion) {
1152
+ if (isOwner) {
1153
+ activeSortable._hideClone();
1154
+ } else {
1155
+ activeSortable._showClone(_this);
1156
+ }
1103
1157
 
1104
- // Return invocation when no further action is needed in another sortable
1105
- function completed() {
1106
- if (activeSortable) {
1107
- // Set ghost class to new sortable's ghost class
1108
- _toggleClass(dragEl, putSortable ? putSortable.options.ghostClass : activeSortable.options.ghostClass, false);
1109
- _toggleClass(dragEl, options.ghostClass, true);
1110
- }
1158
+ if (activeSortable) {
1159
+ // Set ghost class to new sortable's ghost class
1160
+ _toggleClass(dragEl, putSortable ? putSortable.options.ghostClass : activeSortable.options.ghostClass, false);
1161
+ _toggleClass(dragEl, options.ghostClass, true);
1162
+ }
1111
1163
 
1112
- if (putSortable !== _this && _this !== Sortable.active) {
1113
- putSortable = _this;
1114
- } else if (_this === Sortable.active) {
1115
- putSortable = null;
1164
+ if (putSortable !== _this && _this !== Sortable.active) {
1165
+ putSortable = _this;
1166
+ } else if (_this === Sortable.active) {
1167
+ putSortable = null;
1168
+ }
1169
+
1170
+ // Animation
1171
+ dragRect && _this._animate(dragRect, dragEl);
1172
+ target && targetRect && _this._animate(targetRect, target);
1116
1173
  }
1117
1174
 
1118
1175
 
@@ -1120,10 +1177,14 @@
1120
1177
  if ((target === dragEl && !dragEl.animated) || (target === el && !target.animated)) {
1121
1178
  lastTarget = null;
1122
1179
  }
1180
+
1123
1181
  // no bubbling and not fallback
1124
1182
  if (!options.dragoverBubble && !evt.rootEl && target !== document) {
1125
1183
  _this._handleAutoScroll(evt);
1126
1184
  dragEl.parentNode[expando]._computeIsAligned(evt);
1185
+
1186
+ // Do not detect for empty insert if already inserted
1187
+ !insertion && nearestEmptyInsertDetectEvent(evt);
1127
1188
  }
1128
1189
 
1129
1190
  !options.dragoverBubble && evt.stopPropagation && evt.stopPropagation();
@@ -1133,7 +1194,7 @@
1133
1194
 
1134
1195
  // Call when dragEl has been inserted
1135
1196
  function changed() {
1136
- _dispatchEvent(_this, rootEl, 'change', target, el, rootEl, oldIndex, _index(dragEl, options.draggable), evt);
1197
+ _dispatchEvent(_this, rootEl, 'change', target, el, rootEl, oldIndex, _index(dragEl), oldDraggableIndex, _index(dragEl, options.draggable), evt);
1137
1198
  }
1138
1199
 
1139
1200
 
@@ -1147,8 +1208,8 @@
1147
1208
  target = _closest(target, options.draggable, el, true);
1148
1209
 
1149
1210
  // target is dragEl or target is animated
1150
- if (!!_closest(evt.target, null, dragEl, true) || target.animated) {
1151
- return completed();
1211
+ if (dragEl.contains(evt.target) || target.animated) {
1212
+ return completed(false);
1152
1213
  }
1153
1214
 
1154
1215
  if (target !== dragEl) {
@@ -1181,15 +1242,15 @@
1181
1242
  rootEl.appendChild(dragEl);
1182
1243
  }
1183
1244
 
1184
- return completed();
1245
+ return completed(true);
1185
1246
  }
1186
1247
 
1187
- if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
1188
- _ghostIsLast(evt, axis, el) && !dragEl.animated
1189
- ) {
1190
- //assign target only if condition is true
1191
- if (el.children.length !== 0 && el.children[0] !== ghostEl && el === evt.target) {
1192
- target = _lastChild(el);
1248
+ var elLastChild = _lastChild(el);
1249
+
1250
+ if (!elLastChild || _ghostIsLast(evt, axis, el) && !elLastChild.animated) {
1251
+ // assign target only if condition is true
1252
+ if (elLastChild && el === evt.target) {
1253
+ target = elLastChild;
1193
1254
  }
1194
1255
 
1195
1256
  if (target) {
@@ -1208,21 +1269,22 @@
1208
1269
  realDragElRect = null;
1209
1270
 
1210
1271
  changed();
1211
- this._animate(dragRect, dragEl);
1212
- target && this._animate(targetRect, target);
1213
- return completed();
1272
+ return completed(true);
1214
1273
  }
1215
1274
  }
1216
- else if (target && target !== dragEl && (target.parentNode[expando] !== void 0) && target !== el) {
1275
+ else if (target && target !== dragEl && target.parentNode === el) {
1217
1276
  var direction = 0,
1218
1277
  targetBeforeFirstSwap,
1219
1278
  aligned = target.sortableMouseAligned,
1220
1279
  differentLevel = dragEl.parentNode !== el,
1221
- scrolledPastTop = _isScrolledPast(target, axis === 'vertical' ? 'top' : 'left');
1280
+ side1 = axis === 'vertical' ? 'top' : 'left',
1281
+ scrolledPastTop = _isScrolledPast(target, 'top') || _isScrolledPast(dragEl, 'top'),
1282
+ scrollBefore = scrolledPastTop ? scrolledPastTop.scrollTop : void 0;
1283
+
1222
1284
 
1223
1285
  if (lastTarget !== target) {
1224
1286
  lastMode = null;
1225
- targetBeforeFirstSwap = _getRect(target)[axis === 'vertical' ? 'top' : 'left'];
1287
+ targetBeforeFirstSwap = _getRect(target)[side1];
1226
1288
  pastFirstInvertThresh = false;
1227
1289
  }
1228
1290
 
@@ -1239,7 +1301,7 @@
1239
1301
  ) {
1240
1302
  // New target that we will be inside
1241
1303
  if (lastMode !== 'swap') {
1242
- isCircumstantialInvert = options.invertSwap || differentLevel || scrolling || scrolledPastTop;
1304
+ isCircumstantialInvert = options.invertSwap || differentLevel;
1243
1305
  }
1244
1306
 
1245
1307
  direction = _getSwapDirection(evt, target, axis,
@@ -1249,10 +1311,10 @@
1249
1311
  lastMode = 'swap';
1250
1312
  } else {
1251
1313
  // Insert at position
1252
- direction = _getInsertDirection(target, options);
1314
+ direction = _getInsertDirection(target);
1253
1315
  lastMode = 'insert';
1254
1316
  }
1255
- if (direction === 0) return completed();
1317
+ if (direction === 0) return completed(false);
1256
1318
 
1257
1319
  realDragElRect = null;
1258
1320
  lastTarget = target;
@@ -1288,29 +1350,28 @@
1288
1350
  target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
1289
1351
  }
1290
1352
 
1353
+ // Undo chrome's scroll adjustment
1354
+ if (scrolledPastTop) {
1355
+ _scrollBy(scrolledPastTop, 0, scrollBefore - scrolledPastTop.scrollTop);
1356
+ }
1357
+
1291
1358
  parentEl = dragEl.parentNode; // actualization
1292
1359
 
1293
1360
  // must be done before animation
1294
1361
  if (targetBeforeFirstSwap !== undefined && !isCircumstantialInvert) {
1295
- targetMoveDistance = abs(targetBeforeFirstSwap - _getRect(target)[axis === 'vertical' ? 'top' : 'left']);
1362
+ targetMoveDistance = abs(targetBeforeFirstSwap - _getRect(target)[side1]);
1296
1363
  }
1297
1364
  changed();
1298
- !differentLevel && this._animate(targetRect, target);
1299
- this._animate(dragRect, dragEl);
1300
1365
 
1301
- return completed();
1366
+ return completed(true);
1302
1367
  }
1303
1368
  }
1304
1369
 
1305
1370
  if (el.contains(dragEl)) {
1306
- return completed();
1371
+ return completed(false);
1307
1372
  }
1308
1373
  }
1309
1374
 
1310
- if (IE11OrLess && !evt.rootEl) {
1311
- _artificalBubble(el, evt, '_onDragOver');
1312
- }
1313
-
1314
1375
  return false;
1315
1376
  },
1316
1377
 
@@ -1342,7 +1403,7 @@
1342
1403
  + (prevRect.top - currentRect.top) / (scaleY ? scaleY : 1) + 'px,0)'
1343
1404
  );
1344
1405
 
1345
- forRepaintDummy = target.offsetWidth; // repaint
1406
+ this._repaint(target);
1346
1407
  _css(target, 'transition', 'transform ' + ms + 'ms' + (this.options.easing ? ' ' + this.options.easing : ''));
1347
1408
  _css(target, 'transform', 'translate3d(0,0,0)');
1348
1409
  }
@@ -1356,23 +1417,31 @@
1356
1417
  }
1357
1418
  },
1358
1419
 
1359
- _offUpEvents: function () {
1360
- var ownerDocument = this.el.ownerDocument;
1420
+ _repaint: function(target) {
1421
+ return target.offsetWidth;
1422
+ },
1361
1423
 
1424
+ _offMoveEvents: function() {
1362
1425
  _off(document, 'touchmove', this._onTouchMove);
1363
1426
  _off(document, 'pointermove', this._onTouchMove);
1427
+ _off(document, 'dragover', nearestEmptyInsertDetectEvent);
1428
+ _off(document, 'mousemove', nearestEmptyInsertDetectEvent);
1429
+ _off(document, 'touchmove', nearestEmptyInsertDetectEvent);
1430
+ },
1431
+
1432
+ _offUpEvents: function () {
1433
+ var ownerDocument = this.el.ownerDocument;
1434
+
1364
1435
  _off(ownerDocument, 'mouseup', this._onDrop);
1365
1436
  _off(ownerDocument, 'touchend', this._onDrop);
1366
1437
  _off(ownerDocument, 'pointerup', this._onDrop);
1367
1438
  _off(ownerDocument, 'touchcancel', this._onDrop);
1368
- _off(ownerDocument, 'pointercancel', this._onDrop);
1369
1439
  _off(document, 'selectstart', this);
1370
1440
  },
1371
1441
 
1372
1442
  _onDrop: function (/**Event*/evt) {
1373
1443
  var el = this.el,
1374
1444
  options = this.options;
1375
-
1376
1445
  awaitingDragStarted = false;
1377
1446
  scrolling = false;
1378
1447
  isCircumstantialInvert = false;
@@ -1400,6 +1469,11 @@
1400
1469
  _off(document, 'dragover', _checkAlignment);
1401
1470
  }
1402
1471
 
1472
+ if (Safari) {
1473
+ _css(document.body, 'user-select', '');
1474
+ }
1475
+
1476
+ this._offMoveEvents();
1403
1477
  this._offUpEvents();
1404
1478
 
1405
1479
  if (evt) {
@@ -1428,21 +1502,22 @@
1428
1502
  _toggleClass(dragEl, this.options.chosenClass, false);
1429
1503
 
1430
1504
  // Drag stop event
1431
- _dispatchEvent(this, rootEl, 'unchoose', dragEl, parentEl, rootEl, oldIndex, null, evt);
1505
+ _dispatchEvent(this, rootEl, 'unchoose', dragEl, parentEl, rootEl, oldIndex, null, oldDraggableIndex, null, evt);
1432
1506
 
1433
1507
  if (rootEl !== parentEl) {
1434
- newIndex = _index(dragEl, options.draggable);
1508
+ newIndex = _index(dragEl);
1509
+ newDraggableIndex = _index(dragEl, options.draggable);
1435
1510
 
1436
1511
  if (newIndex >= 0) {
1437
1512
  // Add event
1438
- _dispatchEvent(null, parentEl, 'add', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
1513
+ _dispatchEvent(null, parentEl, 'add', dragEl, parentEl, rootEl, oldIndex, newIndex, oldDraggableIndex, newDraggableIndex, evt);
1439
1514
 
1440
1515
  // Remove event
1441
- _dispatchEvent(this, rootEl, 'remove', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
1516
+ _dispatchEvent(this, rootEl, 'remove', dragEl, parentEl, rootEl, oldIndex, newIndex, oldDraggableIndex, newDraggableIndex, evt);
1442
1517
 
1443
1518
  // drag from one list and drop into another
1444
- _dispatchEvent(null, parentEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
1445
- _dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
1519
+ _dispatchEvent(null, parentEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, oldDraggableIndex, newDraggableIndex, evt);
1520
+ _dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, oldDraggableIndex, newDraggableIndex, evt);
1446
1521
  }
1447
1522
 
1448
1523
  putSortable && putSortable.save();
@@ -1450,12 +1525,13 @@
1450
1525
  else {
1451
1526
  if (dragEl.nextSibling !== nextEl) {
1452
1527
  // Get the index of the dragged element within its parent
1453
- newIndex = _index(dragEl, options.draggable);
1528
+ newIndex = _index(dragEl);
1529
+ newDraggableIndex = _index(dragEl, options.draggable);
1454
1530
 
1455
1531
  if (newIndex >= 0) {
1456
1532
  // drag & drop within the same list
1457
- _dispatchEvent(this, rootEl, 'update', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
1458
- _dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
1533
+ _dispatchEvent(this, rootEl, 'update', dragEl, parentEl, rootEl, oldIndex, newIndex, oldDraggableIndex, newDraggableIndex, evt);
1534
+ _dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, oldDraggableIndex, newDraggableIndex, evt);
1459
1535
  }
1460
1536
  }
1461
1537
  }
@@ -1464,9 +1540,9 @@
1464
1540
  /* jshint eqnull:true */
1465
1541
  if (newIndex == null || newIndex === -1) {
1466
1542
  newIndex = oldIndex;
1543
+ newDraggableIndex = oldDraggableIndex;
1467
1544
  }
1468
-
1469
- _dispatchEvent(this, rootEl, 'end', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
1545
+ _dispatchEvent(this, rootEl, 'end', dragEl, parentEl, rootEl, oldIndex, newIndex, oldDraggableIndex, newDraggableIndex, evt);
1470
1546
 
1471
1547
  // Save sorting
1472
1548
  this.save();
@@ -1504,7 +1580,6 @@
1504
1580
  lastTarget =
1505
1581
  lastDirection =
1506
1582
 
1507
- forRepaintDummy =
1508
1583
  realDragElRect =
1509
1584
 
1510
1585
  putSortable =
@@ -1696,7 +1771,15 @@
1696
1771
  ctx = ctx || document;
1697
1772
 
1698
1773
  do {
1699
- if ((selector === '>*' && el.parentNode === ctx) || _matches(el, selector) || (includeCTX && el === ctx)) {
1774
+ if (
1775
+ selector != null &&
1776
+ (
1777
+ selector[0] === '>' ?
1778
+ el.parentNode === ctx && _matches(el, selector) :
1779
+ _matches(el, selector)
1780
+ ) ||
1781
+ includeCTX && el === ctx
1782
+ ) {
1700
1783
  return el;
1701
1784
  }
1702
1785
 
@@ -1725,12 +1808,12 @@
1725
1808
 
1726
1809
 
1727
1810
  function _on(el, event, fn) {
1728
- el.addEventListener(event, fn, captureMode);
1811
+ el.addEventListener(event, fn, IE11OrLess ? false : captureMode);
1729
1812
  }
1730
1813
 
1731
1814
 
1732
1815
  function _off(el, event, fn) {
1733
- el.removeEventListener(event, fn, captureMode);
1816
+ el.removeEventListener(event, fn, IE11OrLess ? false : captureMode);
1734
1817
  }
1735
1818
 
1736
1819
 
@@ -1810,7 +1893,13 @@
1810
1893
 
1811
1894
 
1812
1895
 
1813
- function _dispatchEvent(sortable, rootEl, name, targetEl, toEl, fromEl, startIndex, newIndex, originalEvt) {
1896
+ function _dispatchEvent(
1897
+ sortable, rootEl, name,
1898
+ targetEl, toEl, fromEl,
1899
+ startIndex, newIndex,
1900
+ startDraggableIndex, newDraggableIndex,
1901
+ originalEvt
1902
+ ) {
1814
1903
  sortable = (sortable || rootEl[expando]);
1815
1904
  var evt,
1816
1905
  options = sortable.options,
@@ -1834,11 +1923,15 @@
1834
1923
  evt.oldIndex = startIndex;
1835
1924
  evt.newIndex = newIndex;
1836
1925
 
1926
+ evt.oldDraggableIndex = startDraggableIndex;
1927
+ evt.newDraggableIndex = newDraggableIndex;
1928
+
1837
1929
  evt.originalEvent = originalEvt;
1930
+ evt.pullMode = putSortable ? putSortable.lastPutMode : undefined;
1838
1931
 
1839
1932
  if (rootEl) {
1840
1933
  rootEl.dispatchEvent(evt);
1841
- }
1934
+ }
1842
1935
 
1843
1936
  if (options[onName]) {
1844
1937
  options[onName].call(sortable, evt);
@@ -1921,15 +2014,15 @@
1921
2014
  }
1922
2015
 
1923
2016
  /**
1924
- * Gets the last child in the el, ignoring ghostEl
2017
+ * Gets the last child in the el, ignoring ghostEl or invisible elements (clones)
1925
2018
  * @param {HTMLElement} el Parent element
1926
2019
  * @return {HTMLElement} The last child, ignoring ghostEl
1927
2020
  */
1928
2021
  function _lastChild(el) {
1929
2022
  var last = el.lastElementChild;
1930
2023
 
1931
- if (last === ghostEl) {
1932
- last = el.children[el.childElementCount - 2];
2024
+ while (last && (last === ghostEl || _css(last, 'display') === 'none')) {
2025
+ last = last.previousElementSibling;
1933
2026
  }
1934
2027
 
1935
2028
  return last || null;
@@ -1941,12 +2034,13 @@
1941
2034
  mouseOnOppAxis = axis === 'vertical' ? evt.clientX : evt.clientY,
1942
2035
  targetS2 = axis === 'vertical' ? elRect.bottom : elRect.right,
1943
2036
  targetS1Opp = axis === 'vertical' ? elRect.left : elRect.top,
1944
- targetS2Opp = axis === 'vertical' ? elRect.right : elRect.bottom;
2037
+ targetS2Opp = axis === 'vertical' ? elRect.right : elRect.bottom,
2038
+ spacer = 10;
1945
2039
 
1946
2040
  return (
1947
- mouseOnOppAxis > targetS1Opp &&
1948
- mouseOnOppAxis < targetS2Opp &&
1949
- mouseOnAxis > targetS2
2041
+ axis === 'vertical' ?
2042
+ (mouseOnOppAxis > targetS2Opp + spacer || mouseOnOppAxis <= targetS2Opp && mouseOnAxis > targetS2 && mouseOnOppAxis >= targetS1Opp) :
2043
+ (mouseOnAxis > targetS2 && mouseOnOppAxis > targetS1Opp || mouseOnAxis <= targetS2 && mouseOnOppAxis > targetS2Opp + spacer)
1950
2044
  );
1951
2045
  }
1952
2046
 
@@ -2004,7 +2098,7 @@
2004
2098
  mouseOnAxis > targetS1 + (targetLength * (1 - swapThreshold) / 2) &&
2005
2099
  mouseOnAxis < targetS2 - (targetLength * (1 - swapThreshold) / 2)
2006
2100
  ) {
2007
- return ((mouseOnAxis > targetS1 + targetLength / 2) ? -1 : 1);
2101
+ return _getInsertDirection(target);
2008
2102
  }
2009
2103
  }
2010
2104
  }
@@ -2029,12 +2123,11 @@
2029
2123
  * Gets the direction dragEl must be swapped relative to target in order to make it
2030
2124
  * seem that dragEl has been "inserted" into that element's position
2031
2125
  * @param {HTMLElement} target The target whose position dragEl is being inserted at
2032
- * @param {Object} options options of the parent sortable
2033
2126
  * @return {Number} Direction dragEl must be swapped
2034
2127
  */
2035
- function _getInsertDirection(target, options) {
2036
- var dragElIndex = _index(dragEl, options.draggable),
2037
- targetIndex = _index(target, options.draggable);
2128
+ function _getInsertDirection(target) {
2129
+ var dragElIndex = _index(dragEl),
2130
+ targetIndex = _index(target);
2038
2131
 
2039
2132
  if (dragElIndex < targetIndex) {
2040
2133
  return 1;
@@ -2077,7 +2170,7 @@
2077
2170
  }
2078
2171
 
2079
2172
  while (el && (el = el.previousElementSibling)) {
2080
- if ((el.nodeName.toUpperCase() !== 'TEMPLATE') && el !== cloneEl) {
2173
+ if ((el.nodeName.toUpperCase() !== 'TEMPLATE') && el !== cloneEl && (!selector || _matches(el, selector))) {
2081
2174
  index++;
2082
2175
  }
2083
2176
  }
@@ -2086,6 +2179,10 @@
2086
2179
  }
2087
2180
 
2088
2181
  function _matches(/**HTMLElement*/el, /**String*/selector) {
2182
+ if (!selector) return;
2183
+
2184
+ selector[0] === '>' && (selector = selector.substring(1));
2185
+
2089
2186
  if (el) {
2090
2187
  try {
2091
2188
  if (el.matches) {
@@ -2178,10 +2275,9 @@
2178
2275
  * @param {HTMLElement} el The element whose boundingClientRect is wanted
2179
2276
  * @param {[HTMLElement]} container the parent the element will be placed in
2180
2277
  * @param {[Boolean]} adjustForTransform Whether the rect should compensate for parent's transform
2181
- * (used for fixed positioning on el)
2182
2278
  * @return {Object} The boundingClientRect of el
2183
2279
  */
2184
- function _getRect(el, container, adjustForTransform) {
2280
+ function _getRect(el, adjustForTransform, container, adjustForFixed) {
2185
2281
  if (!el.getBoundingClientRect && el !== win) return;
2186
2282
 
2187
2283
  var elRect,
@@ -2192,7 +2288,7 @@
2192
2288
  height,
2193
2289
  width;
2194
2290
 
2195
- if (el !== win) {
2291
+ if (el !== win && el !== _getWindowScrollingElement()) {
2196
2292
  elRect = el.getBoundingClientRect();
2197
2293
  top = elRect.top;
2198
2294
  left = elRect.left;
@@ -2209,7 +2305,7 @@
2209
2305
  width = window.innerWidth;
2210
2306
  }
2211
2307
 
2212
- if (adjustForTransform && el !== win) {
2308
+ if (adjustForFixed && el !== win) {
2213
2309
  // Adjust for translate()
2214
2310
  container = container || el.parentNode;
2215
2311
 
@@ -2231,9 +2327,11 @@
2231
2327
  /* jshint boss:true */
2232
2328
  } while (container = container.parentNode);
2233
2329
  }
2330
+ }
2234
2331
 
2332
+ if (adjustForTransform && el !== win) {
2235
2333
  // Adjust for scale()
2236
- var matrix = _matrix(el),
2334
+ var matrix = _matrix(container || el),
2237
2335
  scaleX = matrix && matrix.a,
2238
2336
  scaleY = matrix && matrix.d;
2239
2337
 
@@ -2264,10 +2362,10 @@
2264
2362
  * Checks if a side of an element is scrolled past a side of it's parents
2265
2363
  * @param {HTMLElement} el The element who's side being scrolled out of view is in question
2266
2364
  * @param {String} side Side of the element in question ('top', 'left', 'right', 'bottom')
2267
- * @return {Boolean} Whether the element is overflowing the viewport on the given side of it's parent
2365
+ * @return {HTMLElement} The parent scroll element that the el's side is scrolled past, or null if there is no such element
2268
2366
  */
2269
2367
  function _isScrolledPast(el, side) {
2270
- var parent = _getParentAutoScrollElement(parent, true),
2368
+ var parent = _getParentAutoScrollElement(el, true),
2271
2369
  elSide = _getRect(el)[side];
2272
2370
 
2273
2371
  /* jshint boss:true */
@@ -2281,9 +2379,9 @@
2281
2379
  visible = elSide <= parentSide;
2282
2380
  }
2283
2381
 
2284
- if (!visible) return true;
2382
+ if (!visible) return parent;
2285
2383
 
2286
- if (parent === win) break;
2384
+ if (parent === _getWindowScrollingElement()) break;
2287
2385
 
2288
2386
  parent = _getParentAutoScrollElement(parent, false);
2289
2387
  }
@@ -2291,6 +2389,31 @@
2291
2389
  return false;
2292
2390
  }
2293
2391
 
2392
+ /**
2393
+ * Returns the scroll offset of the given element, added with all the scroll offsets of parent elements.
2394
+ * The value is returned in real pixels.
2395
+ * @param {HTMLElement} el
2396
+ * @return {Array} Offsets in the format of [left, top]
2397
+ */
2398
+ function _getRelativeScrollOffset(el) {
2399
+ var offsetLeft = 0,
2400
+ offsetTop = 0,
2401
+ winScroller = _getWindowScrollingElement();
2402
+
2403
+ if (el) {
2404
+ do {
2405
+ var matrix = _matrix(el),
2406
+ scaleX = matrix.a,
2407
+ scaleY = matrix.d;
2408
+
2409
+ offsetLeft += el.scrollLeft * scaleX;
2410
+ offsetTop += el.scrollTop * scaleY;
2411
+ } while (el !== winScroller && (el = el.parentNode));
2412
+ }
2413
+
2414
+ return [offsetLeft, offsetTop];
2415
+ }
2416
+
2294
2417
  // Fixed #973:
2295
2418
  _on(document, 'touchmove', function(evt) {
2296
2419
  if ((Sortable.active || awaitingDragStarted) && evt.cancelable) {
@@ -2332,6 +2455,6 @@
2332
2455
 
2333
2456
 
2334
2457
  // Export
2335
- Sortable.version = '1.8.1';
2458
+ Sortable.version = '1.9.0';
2336
2459
  return Sortable;
2337
2460
  });