sortablejs 1.15.2 → 1.15.6

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.
@@ -0,0 +1,2028 @@
1
+ /**!
2
+ * Sortable
3
+ * @author RubaXa <trash@rubaxa.org>
4
+ * @author owenm <owen23355@gmail.com>
5
+ * @license MIT
6
+ */
7
+
8
+ import { version } from '../package.json';
9
+
10
+ import { IE11OrLess, Edge, FireFox, Safari, IOS, ChromeForAndroid } from './BrowserInfo.js';
11
+
12
+ import AnimationStateManager from './Animation.js';
13
+
14
+ import PluginManager from './PluginManager.js';
15
+
16
+ import dispatchEvent from './EventDispatcher.js';
17
+
18
+ import {
19
+ on,
20
+ off,
21
+ closest,
22
+ toggleClass,
23
+ css,
24
+ matrix,
25
+ find,
26
+ getWindowScrollingElement,
27
+ getRect,
28
+ isScrolledPast,
29
+ getChild,
30
+ lastChild,
31
+ index,
32
+ getRelativeScrollOffset,
33
+ extend,
34
+ throttle,
35
+ scrollBy,
36
+ clone,
37
+ expando,
38
+ getChildContainingRectFromElement,
39
+ getParentOrHost
40
+ } from './utils.js';
41
+
42
+
43
+ let pluginEvent = function(eventName, sortable, { evt: originalEvent, ...data } = {}) {
44
+ PluginManager.pluginEvent.bind(Sortable)(eventName, sortable, {
45
+ dragEl,
46
+ parentEl,
47
+ ghostEl,
48
+ rootEl,
49
+ nextEl,
50
+ lastDownEl,
51
+ cloneEl,
52
+ cloneHidden,
53
+ dragStarted: moved,
54
+ putSortable,
55
+ activeSortable: Sortable.active,
56
+ originalEvent,
57
+
58
+ oldIndex,
59
+ oldDraggableIndex,
60
+ newIndex,
61
+ newDraggableIndex,
62
+
63
+ hideGhostForTarget: _hideGhostForTarget,
64
+ unhideGhostForTarget: _unhideGhostForTarget,
65
+
66
+
67
+ cloneNowHidden() {
68
+ cloneHidden = true;
69
+ },
70
+ cloneNowShown() {
71
+ cloneHidden = false;
72
+ },
73
+
74
+ dispatchSortableEvent(name) {
75
+ _dispatchEvent({ sortable, name, originalEvent });
76
+ },
77
+
78
+ ...data
79
+ });
80
+ };
81
+
82
+ function _dispatchEvent(info) {
83
+ dispatchEvent({
84
+ putSortable,
85
+ cloneEl,
86
+ targetEl: dragEl,
87
+ rootEl,
88
+ oldIndex,
89
+ oldDraggableIndex,
90
+ newIndex,
91
+ newDraggableIndex,
92
+ ...info
93
+ });
94
+ }
95
+
96
+
97
+ let dragEl,
98
+ parentEl,
99
+ ghostEl,
100
+ rootEl,
101
+ nextEl,
102
+ lastDownEl,
103
+
104
+ cloneEl,
105
+ cloneHidden,
106
+
107
+ oldIndex,
108
+ newIndex,
109
+ oldDraggableIndex,
110
+ newDraggableIndex,
111
+
112
+ activeGroup,
113
+ putSortable,
114
+
115
+ awaitingDragStarted = false,
116
+ ignoreNextClick = false,
117
+ sortables = [],
118
+
119
+ tapEvt,
120
+ touchEvt,
121
+ lastDx,
122
+ lastDy,
123
+ tapDistanceLeft,
124
+ tapDistanceTop,
125
+
126
+ moved,
127
+
128
+ lastTarget,
129
+ lastDirection,
130
+ pastFirstInvertThresh = false,
131
+ isCircumstantialInvert = false,
132
+
133
+ targetMoveDistance,
134
+
135
+ // For positioning ghost absolutely
136
+ ghostRelativeParent,
137
+ ghostRelativeParentInitialScroll = [], // (left, top)
138
+
139
+ _silent = false,
140
+ savedInputChecked = [];
141
+
142
+ /** @const */
143
+ const documentExists = typeof document !== 'undefined',
144
+
145
+ PositionGhostAbsolutely = IOS,
146
+
147
+ CSSFloatProperty = Edge || IE11OrLess ? 'cssFloat' : 'float',
148
+
149
+ // This will not pass for IE9, because IE9 DnD only works on anchors
150
+ supportDraggable = documentExists && !ChromeForAndroid && !IOS && ('draggable' in document.createElement('div')),
151
+
152
+ supportCssPointerEvents = (function() {
153
+ if (!documentExists) return;
154
+ // false when <= IE11
155
+ if (IE11OrLess) {
156
+ return false;
157
+ }
158
+ let el = document.createElement('x');
159
+ el.style.cssText = 'pointer-events:auto';
160
+ return el.style.pointerEvents === 'auto';
161
+ })(),
162
+
163
+ _detectDirection = function(el, options) {
164
+ let elCSS = css(el),
165
+ elWidth = parseInt(elCSS.width)
166
+ - parseInt(elCSS.paddingLeft)
167
+ - parseInt(elCSS.paddingRight)
168
+ - parseInt(elCSS.borderLeftWidth)
169
+ - parseInt(elCSS.borderRightWidth),
170
+ child1 = getChild(el, 0, options),
171
+ child2 = getChild(el, 1, options),
172
+ firstChildCSS = child1 && css(child1),
173
+ secondChildCSS = child2 && css(child2),
174
+ firstChildWidth = firstChildCSS && parseInt(firstChildCSS.marginLeft) + parseInt(firstChildCSS.marginRight) + getRect(child1).width,
175
+ secondChildWidth = secondChildCSS && parseInt(secondChildCSS.marginLeft) + parseInt(secondChildCSS.marginRight) + getRect(child2).width;
176
+
177
+ if (elCSS.display === 'flex') {
178
+ return elCSS.flexDirection === 'column' || elCSS.flexDirection === 'column-reverse'
179
+ ? 'vertical' : 'horizontal';
180
+ }
181
+
182
+ if (elCSS.display === 'grid') {
183
+ return elCSS.gridTemplateColumns.split(' ').length <= 1 ? 'vertical' : 'horizontal';
184
+ }
185
+
186
+ if (child1 && firstChildCSS.float && firstChildCSS.float !== 'none') {
187
+ let touchingSideChild2 = firstChildCSS.float === 'left' ? 'left' : 'right';
188
+
189
+ return child2 && (secondChildCSS.clear === 'both' || secondChildCSS.clear === touchingSideChild2) ?
190
+ 'vertical' : 'horizontal';
191
+ }
192
+
193
+ return (child1 &&
194
+ (
195
+ firstChildCSS.display === 'block' ||
196
+ firstChildCSS.display === 'flex' ||
197
+ firstChildCSS.display === 'table' ||
198
+ firstChildCSS.display === 'grid' ||
199
+ firstChildWidth >= elWidth &&
200
+ elCSS[CSSFloatProperty] === 'none' ||
201
+ child2 &&
202
+ elCSS[CSSFloatProperty] === 'none' &&
203
+ firstChildWidth + secondChildWidth > elWidth
204
+ ) ?
205
+ 'vertical' : 'horizontal'
206
+ );
207
+ },
208
+
209
+ _dragElInRowColumn = function(dragRect, targetRect, vertical) {
210
+ let dragElS1Opp = vertical ? dragRect.left : dragRect.top,
211
+ dragElS2Opp = vertical ? dragRect.right : dragRect.bottom,
212
+ dragElOppLength = vertical ? dragRect.width : dragRect.height,
213
+ targetS1Opp = vertical ? targetRect.left : targetRect.top,
214
+ targetS2Opp = vertical ? targetRect.right : targetRect.bottom,
215
+ targetOppLength = vertical ? targetRect.width : targetRect.height;
216
+
217
+ return (
218
+ dragElS1Opp === targetS1Opp ||
219
+ dragElS2Opp === targetS2Opp ||
220
+ (dragElS1Opp + dragElOppLength / 2) === (targetS1Opp + targetOppLength / 2)
221
+ );
222
+ },
223
+
224
+ /**
225
+ * Detects first nearest empty sortable to X and Y position using emptyInsertThreshold.
226
+ * @param {Number} x X position
227
+ * @param {Number} y Y position
228
+ * @return {HTMLElement} Element of the first found nearest Sortable
229
+ */
230
+ _detectNearestEmptySortable = function(x, y) {
231
+ let ret;
232
+ sortables.some((sortable) => {
233
+ const threshold = sortable[expando].options.emptyInsertThreshold;
234
+ if (!threshold || lastChild(sortable)) return;
235
+
236
+ const rect = getRect(sortable),
237
+ insideHorizontally = x >= (rect.left - threshold) && x <= (rect.right + threshold),
238
+ insideVertically = y >= (rect.top - threshold) && y <= (rect.bottom + threshold);
239
+
240
+ if (insideHorizontally && insideVertically) {
241
+ return (ret = sortable);
242
+ }
243
+ });
244
+ return ret;
245
+ },
246
+
247
+ _prepareGroup = function (options) {
248
+ function toFn(value, pull) {
249
+ return function(to, from, dragEl, evt) {
250
+ let sameGroup = to.options.group.name &&
251
+ from.options.group.name &&
252
+ to.options.group.name === from.options.group.name;
253
+
254
+ if (value == null && (pull || sameGroup)) {
255
+ // Default pull value
256
+ // Default pull and put value if same group
257
+ return true;
258
+ } else if (value == null || value === false) {
259
+ return false;
260
+ } else if (pull && value === 'clone') {
261
+ return value;
262
+ } else if (typeof value === 'function') {
263
+ return toFn(value(to, from, dragEl, evt), pull)(to, from, dragEl, evt);
264
+ } else {
265
+ let otherGroup = (pull ? to : from).options.group.name;
266
+
267
+ return (value === true ||
268
+ (typeof value === 'string' && value === otherGroup) ||
269
+ (value.join && value.indexOf(otherGroup) > -1));
270
+ }
271
+ };
272
+ }
273
+
274
+ let group = {};
275
+ let originalGroup = options.group;
276
+
277
+ if (!originalGroup || typeof originalGroup != 'object') {
278
+ originalGroup = {name: originalGroup};
279
+ }
280
+
281
+ group.name = originalGroup.name;
282
+ group.checkPull = toFn(originalGroup.pull, true);
283
+ group.checkPut = toFn(originalGroup.put);
284
+ group.revertClone = originalGroup.revertClone;
285
+
286
+ options.group = group;
287
+ },
288
+
289
+ _hideGhostForTarget = function() {
290
+ if (!supportCssPointerEvents && ghostEl) {
291
+ css(ghostEl, 'display', 'none');
292
+ }
293
+ },
294
+
295
+ _unhideGhostForTarget = function() {
296
+ if (!supportCssPointerEvents && ghostEl) {
297
+ css(ghostEl, 'display', '');
298
+ }
299
+ };
300
+
301
+
302
+ // #1184 fix - Prevent click event on fallback if dragged but item not changed position
303
+ if (documentExists && !ChromeForAndroid) {
304
+ document.addEventListener('click', function(evt) {
305
+ if (ignoreNextClick) {
306
+ evt.preventDefault();
307
+ evt.stopPropagation && evt.stopPropagation();
308
+ evt.stopImmediatePropagation && evt.stopImmediatePropagation();
309
+ ignoreNextClick = false;
310
+ return false;
311
+ }
312
+ }, true);
313
+ }
314
+
315
+ let nearestEmptyInsertDetectEvent = function(evt) {
316
+ if (dragEl) {
317
+ evt = evt.touches ? evt.touches[0] : evt;
318
+ let nearest = _detectNearestEmptySortable(evt.clientX, evt.clientY);
319
+
320
+ if (nearest) {
321
+ // Create imitation event
322
+ let event = {};
323
+ for (let i in evt) {
324
+ if (evt.hasOwnProperty(i)) {
325
+ event[i] = evt[i];
326
+ }
327
+ }
328
+ event.target = event.rootEl = nearest;
329
+ event.preventDefault = void 0;
330
+ event.stopPropagation = void 0;
331
+ nearest[expando]._onDragOver(event);
332
+ }
333
+ }
334
+ };
335
+
336
+
337
+ let _checkOutsideTargetEl = function(evt) {
338
+ if (dragEl) {
339
+ dragEl.parentNode[expando]._isOutsideThisEl(evt.target);
340
+ }
341
+ };
342
+
343
+
344
+ /**
345
+ * @class Sortable
346
+ * @param {HTMLElement} el
347
+ * @param {Object} [options]
348
+ */
349
+ function Sortable(el, options) {
350
+ if (!(el && el.nodeType && el.nodeType === 1)) {
351
+ throw `Sortable: \`el\` must be an HTMLElement, not ${ {}.toString.call(el) }`;
352
+ }
353
+
354
+ this.el = el; // root element
355
+ this.options = options = Object.assign({}, options);
356
+
357
+
358
+ // Export instance
359
+ el[expando] = this;
360
+
361
+ let defaults = {
362
+ group: null,
363
+ sort: true,
364
+ disabled: false,
365
+ store: null,
366
+ handle: null,
367
+ draggable: /^[uo]l$/i.test(el.nodeName) ? '>li' : '>*',
368
+ swapThreshold: 1, // percentage; 0 <= x <= 1
369
+ invertSwap: false, // invert always
370
+ invertedSwapThreshold: null, // will be set to same as swapThreshold if default
371
+ removeCloneOnHide: true,
372
+ direction: function() {
373
+ return _detectDirection(el, this.options);
374
+ },
375
+ ghostClass: 'sortable-ghost',
376
+ chosenClass: 'sortable-chosen',
377
+ dragClass: 'sortable-drag',
378
+ ignore: 'a, img',
379
+ filter: null,
380
+ preventOnFilter: true,
381
+ animation: 0,
382
+ easing: null,
383
+ setData: function (dataTransfer, dragEl) {
384
+ dataTransfer.setData('Text', dragEl.textContent);
385
+ },
386
+ dropBubble: false,
387
+ dragoverBubble: false,
388
+ dataIdAttr: 'data-id',
389
+ delay: 0,
390
+ delayOnTouchOnly: false,
391
+ touchStartThreshold: (Number.parseInt ? Number : window).parseInt(window.devicePixelRatio, 10) || 1,
392
+ forceFallback: false,
393
+ fallbackClass: 'sortable-fallback',
394
+ fallbackOnBody: false,
395
+ fallbackTolerance: 0,
396
+ fallbackOffset: {x: 0, y: 0},
397
+ // Disabled on Safari: #1571; Enabled on Safari IOS: #2244
398
+ supportPointer: Sortable.supportPointer !== false && ('PointerEvent' in window) && (!Safari || IOS),
399
+ emptyInsertThreshold: 5
400
+ };
401
+
402
+ PluginManager.initializePlugins(this, el, defaults);
403
+
404
+ // Set default options
405
+ for (let name in defaults) {
406
+ !(name in options) && (options[name] = defaults[name]);
407
+ }
408
+
409
+ _prepareGroup(options);
410
+
411
+ // Bind all private methods
412
+ for (let fn in this) {
413
+ if (fn.charAt(0) === '_' && typeof this[fn] === 'function') {
414
+ this[fn] = this[fn].bind(this);
415
+ }
416
+ }
417
+
418
+ // Setup drag mode
419
+ this.nativeDraggable = options.forceFallback ? false : supportDraggable;
420
+
421
+ if (this.nativeDraggable) {
422
+ // Touch start threshold cannot be greater than the native dragstart threshold
423
+ this.options.touchStartThreshold = 1;
424
+ }
425
+
426
+ // Bind events
427
+ if (options.supportPointer) {
428
+ on(el, 'pointerdown', this._onTapStart);
429
+ } else {
430
+ on(el, 'mousedown', this._onTapStart);
431
+ on(el, 'touchstart', this._onTapStart);
432
+ }
433
+
434
+ if (this.nativeDraggable) {
435
+ on(el, 'dragover', this);
436
+ on(el, 'dragenter', this);
437
+ }
438
+
439
+ sortables.push(this.el);
440
+
441
+ // Restore sorting
442
+ options.store && options.store.get && this.sort(options.store.get(this) || []);
443
+
444
+ // Add animation state manager
445
+ Object.assign(this, AnimationStateManager());
446
+ }
447
+
448
+ Sortable.prototype = /** @lends Sortable.prototype */ {
449
+ constructor: Sortable,
450
+
451
+ _isOutsideThisEl: function(target) {
452
+ if (!this.el.contains(target) && target !== this.el) {
453
+ lastTarget = null;
454
+ }
455
+ },
456
+
457
+ _getDirection: function(evt, target) {
458
+ return (typeof this.options.direction === 'function') ? this.options.direction.call(this, evt, target, dragEl) : this.options.direction;
459
+ },
460
+
461
+ _onTapStart: function (/** Event|TouchEvent */evt) {
462
+ if (!evt.cancelable) return;
463
+ let _this = this,
464
+ el = this.el,
465
+ options = this.options,
466
+ preventOnFilter = options.preventOnFilter,
467
+ type = evt.type,
468
+ touch = (evt.touches && evt.touches[0]) || (evt.pointerType && evt.pointerType === 'touch' && evt),
469
+ target = (touch || evt).target,
470
+ originalTarget = evt.target.shadowRoot && ((evt.path && evt.path[0]) || (evt.composedPath && evt.composedPath()[0])) || target,
471
+ filter = options.filter;
472
+
473
+ _saveInputCheckedState(el);
474
+
475
+
476
+ // Don't trigger start event when an element is been dragged, otherwise the evt.oldindex always wrong when set option.group.
477
+ if (dragEl) {
478
+ return;
479
+ }
480
+
481
+ if (/mousedown|pointerdown/.test(type) && evt.button !== 0 || options.disabled) {
482
+ return; // only left button and enabled
483
+ }
484
+
485
+ // cancel dnd if original target is content editable
486
+ if (originalTarget.isContentEditable) {
487
+ return;
488
+ }
489
+
490
+ // Safari ignores further event handling after mousedown
491
+ if (!this.nativeDraggable && Safari && target && target.tagName.toUpperCase() === 'SELECT') {
492
+ return;
493
+ }
494
+
495
+ target = closest(target, options.draggable, el, false);
496
+
497
+
498
+ if (target && target.animated) {
499
+ return;
500
+ }
501
+
502
+ if (lastDownEl === target) {
503
+ // Ignoring duplicate `down`
504
+ return;
505
+ }
506
+
507
+ // Get the index of the dragged element within its parent
508
+ oldIndex = index(target);
509
+ oldDraggableIndex = index(target, options.draggable);
510
+
511
+ // Check filter
512
+ if (typeof filter === 'function') {
513
+ if (filter.call(this, evt, target, this)) {
514
+ _dispatchEvent({
515
+ sortable: _this,
516
+ rootEl: originalTarget,
517
+ name: 'filter',
518
+ targetEl: target,
519
+ toEl: el,
520
+ fromEl: el
521
+ });
522
+ pluginEvent('filter', _this, { evt });
523
+ preventOnFilter && evt.preventDefault();
524
+ return; // cancel dnd
525
+ }
526
+ }
527
+ else if (filter) {
528
+ filter = filter.split(',').some(function (criteria) {
529
+ criteria = closest(originalTarget, criteria.trim(), el, false);
530
+
531
+ if (criteria) {
532
+ _dispatchEvent({
533
+ sortable: _this,
534
+ rootEl: criteria,
535
+ name: 'filter',
536
+ targetEl: target,
537
+ fromEl: el,
538
+ toEl: el
539
+ });
540
+ pluginEvent('filter', _this, { evt });
541
+ return true;
542
+ }
543
+ });
544
+
545
+ if (filter) {
546
+ preventOnFilter && evt.preventDefault();
547
+ return; // cancel dnd
548
+ }
549
+ }
550
+
551
+ if (options.handle && !closest(originalTarget, options.handle, el, false)) {
552
+ return;
553
+ }
554
+
555
+ // Prepare `dragstart`
556
+ this._prepareDragStart(evt, touch, target);
557
+ },
558
+
559
+ _prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target) {
560
+ let _this = this,
561
+ el = _this.el,
562
+ options = _this.options,
563
+ ownerDocument = el.ownerDocument,
564
+ dragStartFn;
565
+
566
+ if (target && !dragEl && (target.parentNode === el)) {
567
+ let dragRect = getRect(target);
568
+ rootEl = el;
569
+ dragEl = target;
570
+ parentEl = dragEl.parentNode;
571
+ nextEl = dragEl.nextSibling;
572
+ lastDownEl = target;
573
+ activeGroup = options.group;
574
+
575
+ Sortable.dragged = dragEl;
576
+
577
+ tapEvt = {
578
+ target: dragEl,
579
+ clientX: (touch || evt).clientX,
580
+ clientY: (touch || evt).clientY
581
+ };
582
+
583
+ tapDistanceLeft = tapEvt.clientX - dragRect.left;
584
+ tapDistanceTop = tapEvt.clientY - dragRect.top;
585
+
586
+ this._lastX = (touch || evt).clientX;
587
+ this._lastY = (touch || evt).clientY;
588
+
589
+ dragEl.style['will-change'] = 'all';
590
+
591
+ dragStartFn = function () {
592
+ pluginEvent('delayEnded', _this, { evt });
593
+ if (Sortable.eventCanceled) {
594
+ _this._onDrop();
595
+ return;
596
+ }
597
+ // Delayed drag has been triggered
598
+ // we can re-enable the events: touchmove/mousemove
599
+ _this._disableDelayedDragEvents();
600
+
601
+ if (!FireFox && _this.nativeDraggable) {
602
+ dragEl.draggable = true;
603
+ }
604
+
605
+ // Bind the events: dragstart/dragend
606
+ _this._triggerDragStart(evt, touch);
607
+
608
+ // Drag start event
609
+ _dispatchEvent({
610
+ sortable: _this,
611
+ name: 'choose',
612
+ originalEvent: evt
613
+ });
614
+
615
+ // Chosen item
616
+ toggleClass(dragEl, options.chosenClass, true);
617
+ };
618
+
619
+ // Disable "draggable"
620
+ options.ignore.split(',').forEach(function (criteria) {
621
+ find(dragEl, criteria.trim(), _disableDraggable);
622
+ });
623
+
624
+ on(ownerDocument, 'dragover', nearestEmptyInsertDetectEvent);
625
+ on(ownerDocument, 'mousemove', nearestEmptyInsertDetectEvent);
626
+ on(ownerDocument, 'touchmove', nearestEmptyInsertDetectEvent);
627
+
628
+ if (options.supportPointer) {
629
+ on(ownerDocument, 'pointerup', _this._onDrop);
630
+ // Native D&D triggers pointercancel
631
+ !this.nativeDraggable && on(ownerDocument, 'pointercancel', _this._onDrop);
632
+ } else {
633
+ on(ownerDocument, 'mouseup', _this._onDrop);
634
+ on(ownerDocument, 'touchend', _this._onDrop);
635
+ on(ownerDocument, 'touchcancel', _this._onDrop);
636
+ }
637
+
638
+ // Make dragEl draggable (must be before delay for FireFox)
639
+ if (FireFox && this.nativeDraggable) {
640
+ this.options.touchStartThreshold = 4;
641
+ dragEl.draggable = true;
642
+ }
643
+
644
+ pluginEvent('delayStart', this, { evt });
645
+
646
+ // Delay is impossible for native DnD in Edge or IE
647
+ if (options.delay && (!options.delayOnTouchOnly || touch) && (!this.nativeDraggable || !(Edge || IE11OrLess))) {
648
+ if (Sortable.eventCanceled) {
649
+ this._onDrop();
650
+ return;
651
+ }
652
+ // If the user moves the pointer or let go the click or touch
653
+ // before the delay has been reached:
654
+ // disable the delayed drag
655
+ if (options.supportPointer) {
656
+ on(ownerDocument, 'pointerup', _this._disableDelayedDrag);
657
+ on(ownerDocument, 'pointercancel', _this._disableDelayedDrag);
658
+ } else {
659
+ on(ownerDocument, 'mouseup', _this._disableDelayedDrag);
660
+ on(ownerDocument, 'touchend', _this._disableDelayedDrag);
661
+ on(ownerDocument, 'touchcancel', _this._disableDelayedDrag);
662
+ }
663
+ on(ownerDocument, 'mousemove', _this._delayedDragTouchMoveHandler);
664
+ on(ownerDocument, 'touchmove', _this._delayedDragTouchMoveHandler);
665
+ options.supportPointer && on(ownerDocument, 'pointermove', _this._delayedDragTouchMoveHandler);
666
+
667
+ _this._dragStartTimer = setTimeout(dragStartFn, options.delay);
668
+ } else {
669
+ dragStartFn();
670
+ }
671
+ }
672
+ },
673
+
674
+ _delayedDragTouchMoveHandler: function (/** TouchEvent|PointerEvent **/e) {
675
+ let touch = e.touches ? e.touches[0] : e;
676
+ if (Math.max(Math.abs(touch.clientX - this._lastX), Math.abs(touch.clientY - this._lastY))
677
+ >= Math.floor(this.options.touchStartThreshold / (this.nativeDraggable && window.devicePixelRatio || 1))
678
+ ) {
679
+ this._disableDelayedDrag();
680
+ }
681
+ },
682
+
683
+ _disableDelayedDrag: function () {
684
+ dragEl && _disableDraggable(dragEl);
685
+ clearTimeout(this._dragStartTimer);
686
+
687
+ this._disableDelayedDragEvents();
688
+ },
689
+
690
+ _disableDelayedDragEvents: function () {
691
+ let ownerDocument = this.el.ownerDocument;
692
+ off(ownerDocument, 'mouseup', this._disableDelayedDrag);
693
+ off(ownerDocument, 'touchend', this._disableDelayedDrag);
694
+ off(ownerDocument, 'touchcancel', this._disableDelayedDrag);
695
+ off(ownerDocument, 'pointerup', this._disableDelayedDrag);
696
+ off(ownerDocument, 'pointercancel', this._disableDelayedDrag);
697
+ off(ownerDocument, 'mousemove', this._delayedDragTouchMoveHandler);
698
+ off(ownerDocument, 'touchmove', this._delayedDragTouchMoveHandler);
699
+ off(ownerDocument, 'pointermove', this._delayedDragTouchMoveHandler);
700
+ },
701
+
702
+ _triggerDragStart: function (/** Event */evt, /** Touch */touch) {
703
+ touch = touch || (evt.pointerType == 'touch' && evt);
704
+
705
+ if (!this.nativeDraggable || touch) {
706
+ if (this.options.supportPointer) {
707
+ on(document, 'pointermove', this._onTouchMove);
708
+ } else if (touch) {
709
+ on(document, 'touchmove', this._onTouchMove);
710
+ } else {
711
+ on(document, 'mousemove', this._onTouchMove);
712
+ }
713
+ } else {
714
+ on(dragEl, 'dragend', this);
715
+ on(rootEl, 'dragstart', this._onDragStart);
716
+ }
717
+
718
+ try {
719
+
720
+ if (document.selection) {
721
+ _nextTick(() => {
722
+ document.selection.empty();
723
+ });
724
+ } else {
725
+ window.getSelection().removeAllRanges();
726
+ }
727
+ } catch (err) {
728
+ }
729
+ },
730
+
731
+ _dragStarted: function (fallback, evt) {
732
+ let _this = this;
733
+ awaitingDragStarted = false;
734
+ if (rootEl && dragEl) {
735
+ pluginEvent('dragStarted', this, { evt });
736
+
737
+ if (this.nativeDraggable) {
738
+ on(document, 'dragover', _checkOutsideTargetEl);
739
+ }
740
+ let options = this.options;
741
+
742
+ // Apply effect
743
+ !fallback && toggleClass(dragEl, options.dragClass, false);
744
+ toggleClass(dragEl, options.ghostClass, true);
745
+
746
+ Sortable.active = this;
747
+
748
+ fallback && this._appendGhost();
749
+
750
+ // Drag start event
751
+ _dispatchEvent({
752
+ sortable: this,
753
+ name: 'start',
754
+ originalEvent: evt
755
+ });
756
+ } else {
757
+ this._nulling();
758
+ }
759
+ },
760
+
761
+ _emulateDragOver: function () {
762
+ if (touchEvt) {
763
+ this._lastX = touchEvt.clientX;
764
+ this._lastY = touchEvt.clientY;
765
+
766
+ _hideGhostForTarget();
767
+
768
+ let target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
769
+ let parent = target;
770
+
771
+ while (target && target.shadowRoot) {
772
+ target = target.shadowRoot.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
773
+ if (target === parent) break;
774
+ parent = target;
775
+ }
776
+
777
+ dragEl.parentNode[expando]._isOutsideThisEl(target);
778
+
779
+ if (parent) {
780
+ do {
781
+ if (parent[expando]) {
782
+ let inserted;
783
+
784
+ inserted = parent[expando]._onDragOver({
785
+ clientX: touchEvt.clientX,
786
+ clientY: touchEvt.clientY,
787
+ target: target,
788
+ rootEl: parent
789
+ });
790
+
791
+ if (inserted && !this.options.dragoverBubble) {
792
+ break;
793
+ }
794
+ }
795
+
796
+ target = parent; // store last element
797
+ }
798
+ /* jshint boss:true */
799
+ while (parent = getParentOrHost(parent));
800
+ }
801
+
802
+ _unhideGhostForTarget();
803
+ }
804
+ },
805
+
806
+
807
+ _onTouchMove: function (/**TouchEvent*/evt) {
808
+ if (tapEvt) {
809
+ let options = this.options,
810
+ fallbackTolerance = options.fallbackTolerance,
811
+ fallbackOffset = options.fallbackOffset,
812
+ touch = evt.touches ? evt.touches[0] : evt,
813
+ ghostMatrix = ghostEl && matrix(ghostEl, true),
814
+ scaleX = ghostEl && ghostMatrix && ghostMatrix.a,
815
+ scaleY = ghostEl && ghostMatrix && ghostMatrix.d,
816
+ relativeScrollOffset = PositionGhostAbsolutely && ghostRelativeParent && getRelativeScrollOffset(ghostRelativeParent),
817
+ dx = ((touch.clientX - tapEvt.clientX)
818
+ + fallbackOffset.x) / (scaleX || 1)
819
+ + (relativeScrollOffset ? (relativeScrollOffset[0] - ghostRelativeParentInitialScroll[0]) : 0) / (scaleX || 1),
820
+ dy = ((touch.clientY - tapEvt.clientY)
821
+ + fallbackOffset.y) / (scaleY || 1)
822
+ + (relativeScrollOffset ? (relativeScrollOffset[1] - ghostRelativeParentInitialScroll[1]) : 0) / (scaleY || 1);
823
+
824
+ // only set the status to dragging, when we are actually dragging
825
+ if (!Sortable.active && !awaitingDragStarted) {
826
+ if (fallbackTolerance &&
827
+ Math.max(Math.abs(touch.clientX - this._lastX), Math.abs(touch.clientY - this._lastY)) < fallbackTolerance
828
+ ) {
829
+ return;
830
+ }
831
+ this._onDragStart(evt, true);
832
+ }
833
+
834
+ if (ghostEl) {
835
+ if (ghostMatrix) {
836
+ ghostMatrix.e += dx - (lastDx || 0);
837
+ ghostMatrix.f += dy - (lastDy || 0);
838
+ } else {
839
+ ghostMatrix = {
840
+ a: 1,
841
+ b: 0,
842
+ c: 0,
843
+ d: 1,
844
+ e: dx,
845
+ f: dy
846
+ };
847
+ }
848
+
849
+ let cssMatrix = `matrix(${ghostMatrix.a},${ghostMatrix.b},${ghostMatrix.c},${ghostMatrix.d},${ghostMatrix.e},${ghostMatrix.f})`;
850
+
851
+ css(ghostEl, 'webkitTransform', cssMatrix);
852
+ css(ghostEl, 'mozTransform', cssMatrix);
853
+ css(ghostEl, 'msTransform', cssMatrix);
854
+ css(ghostEl, 'transform', cssMatrix);
855
+
856
+ lastDx = dx;
857
+ lastDy = dy;
858
+
859
+ touchEvt = touch;
860
+ }
861
+
862
+ evt.cancelable && evt.preventDefault();
863
+ }
864
+ },
865
+
866
+ _appendGhost: function () {
867
+ // Bug if using scale(): https://stackoverflow.com/questions/2637058
868
+ // Not being adjusted for
869
+ if (!ghostEl) {
870
+ let container = this.options.fallbackOnBody ? document.body : rootEl,
871
+ rect = getRect(dragEl, true, PositionGhostAbsolutely, true, container),
872
+ options = this.options;
873
+
874
+ // Position absolutely
875
+ if (PositionGhostAbsolutely) {
876
+ // Get relatively positioned parent
877
+ ghostRelativeParent = container;
878
+
879
+ while (
880
+ css(ghostRelativeParent, 'position') === 'static' &&
881
+ css(ghostRelativeParent, 'transform') === 'none' &&
882
+ ghostRelativeParent !== document
883
+ ) {
884
+ ghostRelativeParent = ghostRelativeParent.parentNode;
885
+ }
886
+
887
+ if (ghostRelativeParent !== document.body && ghostRelativeParent !== document.documentElement) {
888
+ if (ghostRelativeParent === document) ghostRelativeParent = getWindowScrollingElement();
889
+
890
+ rect.top += ghostRelativeParent.scrollTop;
891
+ rect.left += ghostRelativeParent.scrollLeft;
892
+ } else {
893
+ ghostRelativeParent = getWindowScrollingElement();
894
+ }
895
+ ghostRelativeParentInitialScroll = getRelativeScrollOffset(ghostRelativeParent);
896
+ }
897
+
898
+
899
+ ghostEl = dragEl.cloneNode(true);
900
+
901
+ toggleClass(ghostEl, options.ghostClass, false);
902
+ toggleClass(ghostEl, options.fallbackClass, true);
903
+ toggleClass(ghostEl, options.dragClass, true);
904
+
905
+ css(ghostEl, 'transition', '');
906
+ css(ghostEl, 'transform', '');
907
+
908
+ css(ghostEl, 'box-sizing', 'border-box');
909
+ css(ghostEl, 'margin', 0);
910
+ css(ghostEl, 'top', rect.top);
911
+ css(ghostEl, 'left', rect.left);
912
+ css(ghostEl, 'width', rect.width);
913
+ css(ghostEl, 'height', rect.height);
914
+ css(ghostEl, 'opacity', '0.8');
915
+ css(ghostEl, 'position', (PositionGhostAbsolutely ? 'absolute' : 'fixed'));
916
+ css(ghostEl, 'zIndex', '100000');
917
+ css(ghostEl, 'pointerEvents', 'none');
918
+
919
+
920
+ Sortable.ghost = ghostEl;
921
+
922
+ container.appendChild(ghostEl);
923
+
924
+ // Set transform-origin
925
+ css(ghostEl, 'transform-origin', (tapDistanceLeft / parseInt(ghostEl.style.width) * 100) + '% ' + (tapDistanceTop / parseInt(ghostEl.style.height) * 100) + '%');
926
+ }
927
+ },
928
+
929
+ _onDragStart: function (/**Event*/evt, /**boolean*/fallback) {
930
+ let _this = this;
931
+ let dataTransfer = evt.dataTransfer;
932
+ let options = _this.options;
933
+
934
+ pluginEvent('dragStart', this, { evt });
935
+ if (Sortable.eventCanceled) {
936
+ this._onDrop();
937
+ return;
938
+ }
939
+
940
+ pluginEvent('setupClone', this);
941
+ if (!Sortable.eventCanceled) {
942
+ cloneEl = clone(dragEl);
943
+ cloneEl.removeAttribute("id");
944
+ cloneEl.draggable = false;
945
+ cloneEl.style['will-change'] = '';
946
+
947
+ this._hideClone();
948
+
949
+ toggleClass(cloneEl, this.options.chosenClass, false);
950
+ Sortable.clone = cloneEl;
951
+ }
952
+
953
+
954
+ // #1143: IFrame support workaround
955
+ _this.cloneId = _nextTick(function() {
956
+ pluginEvent('clone', _this);
957
+ if (Sortable.eventCanceled) return;
958
+
959
+ if (!_this.options.removeCloneOnHide) {
960
+ rootEl.insertBefore(cloneEl, dragEl);
961
+ }
962
+ _this._hideClone();
963
+
964
+ _dispatchEvent({
965
+ sortable: _this,
966
+ name: 'clone'
967
+ });
968
+ });
969
+
970
+
971
+ !fallback && toggleClass(dragEl, options.dragClass, true);
972
+
973
+ // Set proper drop events
974
+ if (fallback) {
975
+ ignoreNextClick = true;
976
+ _this._loopId = setInterval(_this._emulateDragOver, 50);
977
+ } else {
978
+ // Undo what was set in _prepareDragStart before drag started
979
+ off(document, 'mouseup', _this._onDrop);
980
+ off(document, 'touchend', _this._onDrop);
981
+ off(document, 'touchcancel', _this._onDrop);
982
+
983
+ if (dataTransfer) {
984
+ dataTransfer.effectAllowed = 'move';
985
+ options.setData && options.setData.call(_this, dataTransfer, dragEl);
986
+ }
987
+
988
+ on(document, 'drop', _this);
989
+
990
+ // #1276 fix:
991
+ css(dragEl, 'transform', 'translateZ(0)');
992
+ }
993
+
994
+ awaitingDragStarted = true;
995
+
996
+ _this._dragStartId = _nextTick(_this._dragStarted.bind(_this, fallback, evt));
997
+ on(document, 'selectstart', _this);
998
+
999
+ moved = true;
1000
+
1001
+ window.getSelection().removeAllRanges();
1002
+
1003
+ if (Safari) {
1004
+ css(document.body, 'user-select', 'none');
1005
+ }
1006
+ },
1007
+
1008
+
1009
+ // Returns true - if no further action is needed (either inserted or another condition)
1010
+ _onDragOver: function (/**Event*/evt) {
1011
+ let el = this.el,
1012
+ target = evt.target,
1013
+ dragRect,
1014
+ targetRect,
1015
+ revert,
1016
+ options = this.options,
1017
+ group = options.group,
1018
+ activeSortable = Sortable.active,
1019
+ isOwner = (activeGroup === group),
1020
+ canSort = options.sort,
1021
+ fromSortable = (putSortable || activeSortable),
1022
+ vertical,
1023
+ _this = this,
1024
+ completedFired = false;
1025
+
1026
+ if (_silent) return;
1027
+
1028
+ function dragOverEvent(name, extra) {
1029
+ pluginEvent(name, _this, {
1030
+ evt,
1031
+ isOwner,
1032
+ axis: vertical ? 'vertical' : 'horizontal',
1033
+ revert,
1034
+ dragRect,
1035
+ targetRect,
1036
+ canSort,
1037
+ fromSortable,
1038
+ target,
1039
+ completed,
1040
+ onMove(target, after) {
1041
+ return onMove(rootEl, el, dragEl, dragRect, target, getRect(target), evt, after);
1042
+ },
1043
+ changed,
1044
+ ...extra
1045
+ });
1046
+ }
1047
+
1048
+ // Capture animation state
1049
+ function capture() {
1050
+ dragOverEvent('dragOverAnimationCapture');
1051
+
1052
+ _this.captureAnimationState();
1053
+ if (_this !== fromSortable) {
1054
+ fromSortable.captureAnimationState();
1055
+ }
1056
+ }
1057
+
1058
+ // Return invocation when dragEl is inserted (or completed)
1059
+ function completed(insertion) {
1060
+ dragOverEvent('dragOverCompleted', { insertion });
1061
+
1062
+ if (insertion) {
1063
+ // Clones must be hidden before folding animation to capture dragRectAbsolute properly
1064
+ if (isOwner) {
1065
+ activeSortable._hideClone();
1066
+ } else {
1067
+ activeSortable._showClone(_this);
1068
+ }
1069
+
1070
+ if (_this !== fromSortable) {
1071
+ // Set ghost class to new sortable's ghost class
1072
+ toggleClass(dragEl, putSortable ? putSortable.options.ghostClass : activeSortable.options.ghostClass, false);
1073
+ toggleClass(dragEl, options.ghostClass, true);
1074
+ }
1075
+
1076
+ if (putSortable !== _this && _this !== Sortable.active) {
1077
+ putSortable = _this;
1078
+ } else if (_this === Sortable.active && putSortable) {
1079
+ putSortable = null;
1080
+ }
1081
+
1082
+ // Animation
1083
+ if (fromSortable === _this) {
1084
+ _this._ignoreWhileAnimating = target;
1085
+ }
1086
+ _this.animateAll(function() {
1087
+ dragOverEvent('dragOverAnimationComplete');
1088
+ _this._ignoreWhileAnimating = null;
1089
+ });
1090
+ if (_this !== fromSortable) {
1091
+ fromSortable.animateAll();
1092
+ fromSortable._ignoreWhileAnimating = null;
1093
+ }
1094
+ }
1095
+
1096
+
1097
+ // Null lastTarget if it is not inside a previously swapped element
1098
+ if ((target === dragEl && !dragEl.animated) || (target === el && !target.animated)) {
1099
+ lastTarget = null;
1100
+ }
1101
+
1102
+ // no bubbling and not fallback
1103
+ if (!options.dragoverBubble && !evt.rootEl && target !== document) {
1104
+ dragEl.parentNode[expando]._isOutsideThisEl(evt.target);
1105
+
1106
+ // Do not detect for empty insert if already inserted
1107
+ !insertion && nearestEmptyInsertDetectEvent(evt);
1108
+ }
1109
+
1110
+ !options.dragoverBubble && evt.stopPropagation && evt.stopPropagation();
1111
+
1112
+ return (completedFired = true);
1113
+ }
1114
+
1115
+ // Call when dragEl has been inserted
1116
+ function changed() {
1117
+ newIndex = index(dragEl);
1118
+ newDraggableIndex = index(dragEl, options.draggable);
1119
+ _dispatchEvent({
1120
+ sortable: _this,
1121
+ name: 'change',
1122
+ toEl: el,
1123
+ newIndex,
1124
+ newDraggableIndex,
1125
+ originalEvent: evt
1126
+ });
1127
+ }
1128
+
1129
+
1130
+ if (evt.preventDefault !== void 0) {
1131
+ evt.cancelable && evt.preventDefault();
1132
+ }
1133
+
1134
+
1135
+ target = closest(target, options.draggable, el, true);
1136
+
1137
+ dragOverEvent('dragOver');
1138
+ if (Sortable.eventCanceled) return completedFired;
1139
+
1140
+ if (
1141
+ dragEl.contains(evt.target) ||
1142
+ target.animated && target.animatingX && target.animatingY ||
1143
+ _this._ignoreWhileAnimating === target
1144
+ ) {
1145
+ return completed(false);
1146
+ }
1147
+
1148
+ ignoreNextClick = false;
1149
+
1150
+ if (activeSortable && !options.disabled &&
1151
+ (isOwner
1152
+ ? canSort || (revert = parentEl !== rootEl) // Reverting item into the original list
1153
+ : (
1154
+ putSortable === this ||
1155
+ (
1156
+ (this.lastPutMode = activeGroup.checkPull(this, activeSortable, dragEl, evt)) &&
1157
+ group.checkPut(this, activeSortable, dragEl, evt)
1158
+ )
1159
+ )
1160
+ )
1161
+ ) {
1162
+ vertical = this._getDirection(evt, target) === 'vertical';
1163
+
1164
+ dragRect = getRect(dragEl);
1165
+
1166
+ dragOverEvent('dragOverValid');
1167
+ if (Sortable.eventCanceled) return completedFired;
1168
+
1169
+ if (revert) {
1170
+ parentEl = rootEl; // actualization
1171
+ capture();
1172
+
1173
+ this._hideClone();
1174
+
1175
+ dragOverEvent('revert');
1176
+
1177
+ if (!Sortable.eventCanceled) {
1178
+ if (nextEl) {
1179
+ rootEl.insertBefore(dragEl, nextEl);
1180
+ } else {
1181
+ rootEl.appendChild(dragEl);
1182
+ }
1183
+ }
1184
+
1185
+ return completed(true);
1186
+ }
1187
+
1188
+ let elLastChild = lastChild(el, options.draggable);
1189
+
1190
+ if (!elLastChild || _ghostIsLast(evt, vertical, this) && !elLastChild.animated) {
1191
+ // Insert to end of list
1192
+
1193
+ // If already at end of list: Do not insert
1194
+ if (elLastChild === dragEl) {
1195
+ return completed(false);
1196
+ }
1197
+
1198
+ // if there is a last element, it is the target
1199
+ if (elLastChild && el === evt.target) {
1200
+ target = elLastChild;
1201
+ }
1202
+
1203
+ if (target) {
1204
+ targetRect = getRect(target);
1205
+ }
1206
+
1207
+ if (onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, !!target) !== false) {
1208
+ capture();
1209
+ if (elLastChild && elLastChild.nextSibling) { // the last draggable element is not the last node
1210
+ el.insertBefore(dragEl, elLastChild.nextSibling);
1211
+ }
1212
+ else {
1213
+ el.appendChild(dragEl);
1214
+ }
1215
+ parentEl = el; // actualization
1216
+
1217
+ changed();
1218
+ return completed(true);
1219
+ }
1220
+ }
1221
+ else if (elLastChild && _ghostIsFirst(evt, vertical, this)) {
1222
+ // Insert to start of list
1223
+ let firstChild = getChild(el, 0, options, true);
1224
+ if (firstChild === dragEl) {
1225
+ return completed(false);
1226
+ }
1227
+ target = firstChild;
1228
+ targetRect = getRect(target);
1229
+
1230
+ if (onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, false) !== false) {
1231
+ capture();
1232
+ el.insertBefore(dragEl, firstChild);
1233
+ parentEl = el; // actualization
1234
+
1235
+ changed();
1236
+ return completed(true);
1237
+ }
1238
+ }
1239
+ else if (target.parentNode === el) {
1240
+ targetRect = getRect(target);
1241
+ let direction = 0,
1242
+ targetBeforeFirstSwap,
1243
+ differentLevel = dragEl.parentNode !== el,
1244
+ differentRowCol = !_dragElInRowColumn(dragEl.animated && dragEl.toRect || dragRect, target.animated && target.toRect || targetRect, vertical),
1245
+ side1 = vertical ? 'top' : 'left',
1246
+ scrolledPastTop = isScrolledPast(target, 'top', 'top') || isScrolledPast(dragEl, 'top', 'top'),
1247
+ scrollBefore = scrolledPastTop ? scrolledPastTop.scrollTop : void 0;
1248
+
1249
+
1250
+ if (lastTarget !== target) {
1251
+ targetBeforeFirstSwap = targetRect[side1];
1252
+ pastFirstInvertThresh = false;
1253
+ isCircumstantialInvert = (!differentRowCol && options.invertSwap) || differentLevel;
1254
+ }
1255
+
1256
+ direction = _getSwapDirection(
1257
+ evt, target, targetRect, vertical,
1258
+ differentRowCol ? 1 : options.swapThreshold,
1259
+ options.invertedSwapThreshold == null ? options.swapThreshold : options.invertedSwapThreshold,
1260
+ isCircumstantialInvert,
1261
+ lastTarget === target
1262
+ );
1263
+
1264
+ let sibling;
1265
+
1266
+ if (direction !== 0) {
1267
+ // Check if target is beside dragEl in respective direction (ignoring hidden elements)
1268
+ let dragIndex = index(dragEl);
1269
+
1270
+ do {
1271
+ dragIndex -= direction;
1272
+ sibling = parentEl.children[dragIndex];
1273
+ } while (sibling && (css(sibling, 'display') === 'none' || sibling === ghostEl));
1274
+ }
1275
+ // If dragEl is already beside target: Do not insert
1276
+ if (
1277
+ direction === 0 ||
1278
+ sibling === target
1279
+ ) {
1280
+ return completed(false);
1281
+ }
1282
+
1283
+ lastTarget = target;
1284
+
1285
+ lastDirection = direction;
1286
+
1287
+ let nextSibling = target.nextElementSibling,
1288
+ after = false;
1289
+
1290
+ after = direction === 1;
1291
+
1292
+ let moveVector = onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, after);
1293
+
1294
+ if (moveVector !== false) {
1295
+ if (moveVector === 1 || moveVector === -1) {
1296
+ after = (moveVector === 1);
1297
+ }
1298
+
1299
+ _silent = true;
1300
+ setTimeout(_unsilent, 30);
1301
+
1302
+ capture();
1303
+
1304
+ if (after && !nextSibling) {
1305
+ el.appendChild(dragEl);
1306
+ } else {
1307
+ target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
1308
+ }
1309
+
1310
+ // Undo chrome's scroll adjustment (has no effect on other browsers)
1311
+ if (scrolledPastTop) {
1312
+ scrollBy(scrolledPastTop, 0, scrollBefore - scrolledPastTop.scrollTop);
1313
+ }
1314
+
1315
+ parentEl = dragEl.parentNode; // actualization
1316
+
1317
+ // must be done before animation
1318
+ if (targetBeforeFirstSwap !== undefined && !isCircumstantialInvert) {
1319
+ targetMoveDistance = Math.abs(targetBeforeFirstSwap - getRect(target)[side1]);
1320
+ }
1321
+ changed();
1322
+
1323
+ return completed(true);
1324
+ }
1325
+ }
1326
+
1327
+ if (el.contains(dragEl)) {
1328
+ return completed(false);
1329
+ }
1330
+ }
1331
+
1332
+ return false;
1333
+ },
1334
+
1335
+ _ignoreWhileAnimating: null,
1336
+
1337
+ _offMoveEvents: function() {
1338
+ off(document, 'mousemove', this._onTouchMove);
1339
+ off(document, 'touchmove', this._onTouchMove);
1340
+ off(document, 'pointermove', this._onTouchMove);
1341
+ off(document, 'dragover', nearestEmptyInsertDetectEvent);
1342
+ off(document, 'mousemove', nearestEmptyInsertDetectEvent);
1343
+ off(document, 'touchmove', nearestEmptyInsertDetectEvent);
1344
+ },
1345
+
1346
+ _offUpEvents: function () {
1347
+ let ownerDocument = this.el.ownerDocument;
1348
+
1349
+ off(ownerDocument, 'mouseup', this._onDrop);
1350
+ off(ownerDocument, 'touchend', this._onDrop);
1351
+ off(ownerDocument, 'pointerup', this._onDrop);
1352
+ off(ownerDocument, 'pointercancel', this._onDrop);
1353
+ off(ownerDocument, 'touchcancel', this._onDrop);
1354
+ off(document, 'selectstart', this);
1355
+ },
1356
+
1357
+ _onDrop: function (/**Event*/evt) {
1358
+ let el = this.el,
1359
+ options = this.options;
1360
+
1361
+ // Get the index of the dragged element within its parent
1362
+ newIndex = index(dragEl);
1363
+ newDraggableIndex = index(dragEl, options.draggable);
1364
+
1365
+ pluginEvent('drop', this, {
1366
+ evt
1367
+ });
1368
+
1369
+ parentEl = dragEl && dragEl.parentNode;
1370
+
1371
+ // Get again after plugin event
1372
+ newIndex = index(dragEl);
1373
+ newDraggableIndex = index(dragEl, options.draggable);
1374
+
1375
+ if (Sortable.eventCanceled) {
1376
+ this._nulling();
1377
+ return;
1378
+ }
1379
+
1380
+ awaitingDragStarted = false;
1381
+ isCircumstantialInvert = false;
1382
+ pastFirstInvertThresh = false;
1383
+
1384
+ clearInterval(this._loopId);
1385
+
1386
+ clearTimeout(this._dragStartTimer);
1387
+
1388
+ _cancelNextTick(this.cloneId);
1389
+ _cancelNextTick(this._dragStartId);
1390
+
1391
+ // Unbind events
1392
+ if (this.nativeDraggable) {
1393
+ off(document, 'drop', this);
1394
+ off(el, 'dragstart', this._onDragStart);
1395
+ }
1396
+ this._offMoveEvents();
1397
+ this._offUpEvents();
1398
+
1399
+
1400
+ if (Safari) {
1401
+ css(document.body, 'user-select', '');
1402
+ }
1403
+
1404
+ css(dragEl, 'transform', '');
1405
+
1406
+ if (evt) {
1407
+ if (moved) {
1408
+ evt.cancelable && evt.preventDefault();
1409
+ !options.dropBubble && evt.stopPropagation();
1410
+ }
1411
+
1412
+ ghostEl && ghostEl.parentNode && ghostEl.parentNode.removeChild(ghostEl);
1413
+
1414
+ if (rootEl === parentEl || (putSortable && putSortable.lastPutMode !== 'clone')) {
1415
+ // Remove clone(s)
1416
+ cloneEl && cloneEl.parentNode && cloneEl.parentNode.removeChild(cloneEl);
1417
+ }
1418
+
1419
+ if (dragEl) {
1420
+ if (this.nativeDraggable) {
1421
+ off(dragEl, 'dragend', this);
1422
+ }
1423
+
1424
+ _disableDraggable(dragEl);
1425
+ dragEl.style['will-change'] = '';
1426
+
1427
+ // Remove classes
1428
+ // ghostClass is added in dragStarted
1429
+ if (moved && !awaitingDragStarted) {
1430
+ toggleClass(dragEl, putSortable ? putSortable.options.ghostClass : this.options.ghostClass, false);
1431
+ }
1432
+ toggleClass(dragEl, this.options.chosenClass, false);
1433
+
1434
+ // Drag stop event
1435
+ _dispatchEvent({
1436
+ sortable: this,
1437
+ name: 'unchoose',
1438
+ toEl: parentEl,
1439
+ newIndex: null,
1440
+ newDraggableIndex: null,
1441
+ originalEvent: evt
1442
+ });
1443
+
1444
+
1445
+ if (rootEl !== parentEl) {
1446
+
1447
+ if (newIndex >= 0) {
1448
+ // Add event
1449
+ _dispatchEvent({
1450
+ rootEl: parentEl,
1451
+ name: 'add',
1452
+ toEl: parentEl,
1453
+ fromEl: rootEl,
1454
+ originalEvent: evt
1455
+ });
1456
+
1457
+ // Remove event
1458
+ _dispatchEvent({
1459
+ sortable: this,
1460
+ name: 'remove',
1461
+ toEl: parentEl,
1462
+ originalEvent: evt
1463
+ });
1464
+
1465
+ // drag from one list and drop into another
1466
+ _dispatchEvent({
1467
+ rootEl: parentEl,
1468
+ name: 'sort',
1469
+ toEl: parentEl,
1470
+ fromEl: rootEl,
1471
+ originalEvent: evt
1472
+ });
1473
+
1474
+ _dispatchEvent({
1475
+ sortable: this,
1476
+ name: 'sort',
1477
+ toEl: parentEl,
1478
+ originalEvent: evt
1479
+ });
1480
+ }
1481
+
1482
+ putSortable && putSortable.save();
1483
+ } else {
1484
+ if (newIndex !== oldIndex) {
1485
+ if (newIndex >= 0) {
1486
+ // drag & drop within the same list
1487
+ _dispatchEvent({
1488
+ sortable: this,
1489
+ name: 'update',
1490
+ toEl: parentEl,
1491
+ originalEvent: evt
1492
+ });
1493
+
1494
+ _dispatchEvent({
1495
+ sortable: this,
1496
+ name: 'sort',
1497
+ toEl: parentEl,
1498
+ originalEvent: evt
1499
+ });
1500
+ }
1501
+ }
1502
+ }
1503
+
1504
+ if (Sortable.active) {
1505
+ /* jshint eqnull:true */
1506
+ if (newIndex == null || newIndex === -1) {
1507
+ newIndex = oldIndex;
1508
+ newDraggableIndex = oldDraggableIndex;
1509
+ }
1510
+
1511
+ _dispatchEvent({
1512
+ sortable: this,
1513
+ name: 'end',
1514
+ toEl: parentEl,
1515
+ originalEvent: evt
1516
+ });
1517
+
1518
+ // Save sorting
1519
+ this.save();
1520
+ }
1521
+ }
1522
+
1523
+ }
1524
+ this._nulling();
1525
+ },
1526
+
1527
+ _nulling: function() {
1528
+ pluginEvent('nulling', this);
1529
+
1530
+ rootEl =
1531
+ dragEl =
1532
+ parentEl =
1533
+ ghostEl =
1534
+ nextEl =
1535
+ cloneEl =
1536
+ lastDownEl =
1537
+ cloneHidden =
1538
+
1539
+ tapEvt =
1540
+ touchEvt =
1541
+
1542
+ moved =
1543
+ newIndex =
1544
+ newDraggableIndex =
1545
+ oldIndex =
1546
+ oldDraggableIndex =
1547
+
1548
+ lastTarget =
1549
+ lastDirection =
1550
+
1551
+ putSortable =
1552
+ activeGroup =
1553
+ Sortable.dragged =
1554
+ Sortable.ghost =
1555
+ Sortable.clone =
1556
+ Sortable.active = null;
1557
+
1558
+ savedInputChecked.forEach(function (el) {
1559
+ el.checked = true;
1560
+ });
1561
+
1562
+ savedInputChecked.length =
1563
+ lastDx =
1564
+ lastDy = 0;
1565
+ },
1566
+
1567
+ handleEvent: function (/**Event*/evt) {
1568
+ switch (evt.type) {
1569
+ case 'drop':
1570
+ case 'dragend':
1571
+ this._onDrop(evt);
1572
+ break;
1573
+
1574
+ case 'dragenter':
1575
+ case 'dragover':
1576
+ if (dragEl) {
1577
+ this._onDragOver(evt);
1578
+ _globalDragOver(evt);
1579
+ }
1580
+ break;
1581
+
1582
+ case 'selectstart':
1583
+ evt.preventDefault();
1584
+ break;
1585
+ }
1586
+ },
1587
+
1588
+
1589
+ /**
1590
+ * Serializes the item into an array of string.
1591
+ * @returns {String[]}
1592
+ */
1593
+ toArray: function () {
1594
+ let order = [],
1595
+ el,
1596
+ children = this.el.children,
1597
+ i = 0,
1598
+ n = children.length,
1599
+ options = this.options;
1600
+
1601
+ for (; i < n; i++) {
1602
+ el = children[i];
1603
+ if (closest(el, options.draggable, this.el, false)) {
1604
+ order.push(el.getAttribute(options.dataIdAttr) || _generateId(el));
1605
+ }
1606
+ }
1607
+
1608
+ return order;
1609
+ },
1610
+
1611
+
1612
+ /**
1613
+ * Sorts the elements according to the array.
1614
+ * @param {String[]} order order of the items
1615
+ */
1616
+ sort: function (order, useAnimation) {
1617
+ let items = {}, rootEl = this.el;
1618
+
1619
+ this.toArray().forEach(function (id, i) {
1620
+ let el = rootEl.children[i];
1621
+
1622
+ if (closest(el, this.options.draggable, rootEl, false)) {
1623
+ items[id] = el;
1624
+ }
1625
+ }, this);
1626
+
1627
+ useAnimation && this.captureAnimationState();
1628
+ order.forEach(function (id) {
1629
+ if (items[id]) {
1630
+ rootEl.removeChild(items[id]);
1631
+ rootEl.appendChild(items[id]);
1632
+ }
1633
+ });
1634
+ useAnimation && this.animateAll();
1635
+ },
1636
+
1637
+
1638
+ /**
1639
+ * Save the current sorting
1640
+ */
1641
+ save: function () {
1642
+ let store = this.options.store;
1643
+ store && store.set && store.set(this);
1644
+ },
1645
+
1646
+
1647
+ /**
1648
+ * 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.
1649
+ * @param {HTMLElement} el
1650
+ * @param {String} [selector] default: `options.draggable`
1651
+ * @returns {HTMLElement|null}
1652
+ */
1653
+ closest: function (el, selector) {
1654
+ return closest(el, selector || this.options.draggable, this.el, false);
1655
+ },
1656
+
1657
+
1658
+ /**
1659
+ * Set/get option
1660
+ * @param {string} name
1661
+ * @param {*} [value]
1662
+ * @returns {*}
1663
+ */
1664
+ option: function (name, value) {
1665
+ let options = this.options;
1666
+
1667
+ if (value === void 0) {
1668
+ return options[name];
1669
+ } else {
1670
+ let modifiedValue = PluginManager.modifyOption(this, name, value);
1671
+ if (typeof modifiedValue !== 'undefined') {
1672
+ options[name] = modifiedValue;
1673
+ } else {
1674
+ options[name] = value;
1675
+ }
1676
+
1677
+ if (name === 'group') {
1678
+ _prepareGroup(options);
1679
+ }
1680
+ }
1681
+ },
1682
+
1683
+
1684
+ /**
1685
+ * Destroy
1686
+ */
1687
+ destroy: function () {
1688
+ pluginEvent('destroy', this);
1689
+ let el = this.el;
1690
+
1691
+ el[expando] = null;
1692
+
1693
+ off(el, 'mousedown', this._onTapStart);
1694
+ off(el, 'touchstart', this._onTapStart);
1695
+ off(el, 'pointerdown', this._onTapStart);
1696
+
1697
+ if (this.nativeDraggable) {
1698
+ off(el, 'dragover', this);
1699
+ off(el, 'dragenter', this);
1700
+ }
1701
+ // Remove draggable attributes
1702
+ Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) {
1703
+ el.removeAttribute('draggable');
1704
+ });
1705
+
1706
+ this._onDrop();
1707
+
1708
+ this._disableDelayedDragEvents();
1709
+
1710
+ sortables.splice(sortables.indexOf(this.el), 1);
1711
+
1712
+ this.el = el = null;
1713
+ },
1714
+
1715
+ _hideClone: function() {
1716
+ if (!cloneHidden) {
1717
+ pluginEvent('hideClone', this);
1718
+ if (Sortable.eventCanceled) return;
1719
+
1720
+
1721
+ css(cloneEl, 'display', 'none');
1722
+ if (this.options.removeCloneOnHide && cloneEl.parentNode) {
1723
+ cloneEl.parentNode.removeChild(cloneEl);
1724
+ }
1725
+ cloneHidden = true;
1726
+ }
1727
+ },
1728
+
1729
+ _showClone: function(putSortable) {
1730
+ if (putSortable.lastPutMode !== 'clone') {
1731
+ this._hideClone();
1732
+ return;
1733
+ }
1734
+
1735
+
1736
+ if (cloneHidden) {
1737
+ pluginEvent('showClone', this);
1738
+ if (Sortable.eventCanceled) return;
1739
+
1740
+ // show clone at dragEl or original position
1741
+ if (dragEl.parentNode == rootEl && !this.options.group.revertClone) {
1742
+ rootEl.insertBefore(cloneEl, dragEl);
1743
+ } else if (nextEl) {
1744
+ rootEl.insertBefore(cloneEl, nextEl);
1745
+ } else {
1746
+ rootEl.appendChild(cloneEl);
1747
+ }
1748
+
1749
+ if (this.options.group.revertClone) {
1750
+ this.animate(dragEl, cloneEl);
1751
+ }
1752
+
1753
+ css(cloneEl, 'display', '');
1754
+ cloneHidden = false;
1755
+ }
1756
+ }
1757
+ };
1758
+
1759
+ function _globalDragOver(/**Event*/evt) {
1760
+ if (evt.dataTransfer) {
1761
+ evt.dataTransfer.dropEffect = 'move';
1762
+ }
1763
+ evt.cancelable && evt.preventDefault();
1764
+ }
1765
+
1766
+ function onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvent, willInsertAfter) {
1767
+ let evt,
1768
+ sortable = fromEl[expando],
1769
+ onMoveFn = sortable.options.onMove,
1770
+ retVal;
1771
+ // Support for new CustomEvent feature
1772
+ if (window.CustomEvent && !IE11OrLess && !Edge) {
1773
+ evt = new CustomEvent('move', {
1774
+ bubbles: true,
1775
+ cancelable: true
1776
+ });
1777
+ } else {
1778
+ evt = document.createEvent('Event');
1779
+ evt.initEvent('move', true, true);
1780
+ }
1781
+
1782
+ evt.to = toEl;
1783
+ evt.from = fromEl;
1784
+ evt.dragged = dragEl;
1785
+ evt.draggedRect = dragRect;
1786
+ evt.related = targetEl || toEl;
1787
+ evt.relatedRect = targetRect || getRect(toEl);
1788
+ evt.willInsertAfter = willInsertAfter;
1789
+
1790
+ evt.originalEvent = originalEvent;
1791
+
1792
+ fromEl.dispatchEvent(evt);
1793
+
1794
+ if (onMoveFn) {
1795
+ retVal = onMoveFn.call(sortable, evt, originalEvent);
1796
+ }
1797
+
1798
+ return retVal;
1799
+ }
1800
+
1801
+ function _disableDraggable(el) {
1802
+ el.draggable = false;
1803
+ }
1804
+
1805
+ function _unsilent() {
1806
+ _silent = false;
1807
+ }
1808
+
1809
+ function _ghostIsFirst(evt, vertical, sortable) {
1810
+ let firstElRect = getRect(getChild(sortable.el, 0, sortable.options, true));
1811
+ const childContainingRect = getChildContainingRectFromElement(sortable.el, sortable.options, ghostEl);
1812
+ const spacer = 10;
1813
+
1814
+ return vertical ?
1815
+ (evt.clientX < childContainingRect.left - spacer || evt.clientY < firstElRect.top && evt.clientX < firstElRect.right) :
1816
+ (evt.clientY < childContainingRect.top - spacer || evt.clientY < firstElRect.bottom && evt.clientX < firstElRect.left)
1817
+ }
1818
+
1819
+ function _ghostIsLast(evt, vertical, sortable) {
1820
+ const lastElRect = getRect(lastChild(sortable.el, sortable.options.draggable));
1821
+ const childContainingRect = getChildContainingRectFromElement(sortable.el, sortable.options, ghostEl);
1822
+ const spacer = 10;
1823
+
1824
+ return vertical ?
1825
+ (evt.clientX > childContainingRect.right + spacer || evt.clientY > lastElRect.bottom && evt.clientX > lastElRect.left) :
1826
+ (evt.clientY > childContainingRect.bottom + spacer || evt.clientX > lastElRect.right && evt.clientY > lastElRect.top);
1827
+ }
1828
+
1829
+ function _getSwapDirection(evt, target, targetRect, vertical, swapThreshold, invertedSwapThreshold, invertSwap, isLastTarget) {
1830
+ let mouseOnAxis = vertical ? evt.clientY : evt.clientX,
1831
+ targetLength = vertical ? targetRect.height : targetRect.width,
1832
+ targetS1 = vertical ? targetRect.top : targetRect.left,
1833
+ targetS2 = vertical ? targetRect.bottom : targetRect.right,
1834
+ invert = false;
1835
+
1836
+
1837
+ if (!invertSwap) {
1838
+ // Never invert or create dragEl shadow when target movemenet causes mouse to move past the end of regular swapThreshold
1839
+ if (isLastTarget && targetMoveDistance < targetLength * swapThreshold) { // multiplied only by swapThreshold because mouse will already be inside target by (1 - threshold) * targetLength / 2
1840
+ // check if past first invert threshold on side opposite of lastDirection
1841
+ if (!pastFirstInvertThresh &&
1842
+ (lastDirection === 1 ?
1843
+ (
1844
+ mouseOnAxis > targetS1 + targetLength * invertedSwapThreshold / 2
1845
+ ) :
1846
+ (
1847
+ mouseOnAxis < targetS2 - targetLength * invertedSwapThreshold / 2
1848
+ )
1849
+ )
1850
+ )
1851
+ {
1852
+ // past first invert threshold, do not restrict inverted threshold to dragEl shadow
1853
+ pastFirstInvertThresh = true;
1854
+ }
1855
+
1856
+ if (!pastFirstInvertThresh) {
1857
+ // dragEl shadow (target move distance shadow)
1858
+ if (
1859
+ lastDirection === 1 ?
1860
+ (
1861
+ mouseOnAxis < targetS1 + targetMoveDistance // over dragEl shadow
1862
+ ) :
1863
+ (
1864
+ mouseOnAxis > targetS2 - targetMoveDistance
1865
+ )
1866
+ )
1867
+ {
1868
+ return -lastDirection;
1869
+ }
1870
+ } else {
1871
+ invert = true;
1872
+ }
1873
+ } else {
1874
+ // Regular
1875
+ if (
1876
+ mouseOnAxis > targetS1 + (targetLength * (1 - swapThreshold) / 2) &&
1877
+ mouseOnAxis < targetS2 - (targetLength * (1 - swapThreshold) / 2)
1878
+ ) {
1879
+ return _getInsertDirection(target);
1880
+ }
1881
+ }
1882
+ }
1883
+
1884
+ invert = invert || invertSwap;
1885
+
1886
+ if (invert) {
1887
+ // Invert of regular
1888
+ if (
1889
+ mouseOnAxis < targetS1 + (targetLength * invertedSwapThreshold / 2) ||
1890
+ mouseOnAxis > targetS2 - (targetLength * invertedSwapThreshold / 2)
1891
+ )
1892
+ {
1893
+ return ((mouseOnAxis > targetS1 + targetLength / 2) ? 1 : -1);
1894
+ }
1895
+ }
1896
+
1897
+ return 0;
1898
+ }
1899
+
1900
+ /**
1901
+ * Gets the direction dragEl must be swapped relative to target in order to make it
1902
+ * seem that dragEl has been "inserted" into that element's position
1903
+ * @param {HTMLElement} target The target whose position dragEl is being inserted at
1904
+ * @return {Number} Direction dragEl must be swapped
1905
+ */
1906
+ function _getInsertDirection(target) {
1907
+ if (index(dragEl) < index(target)) {
1908
+ return 1;
1909
+ } else {
1910
+ return -1;
1911
+ }
1912
+ }
1913
+
1914
+
1915
+ /**
1916
+ * Generate id
1917
+ * @param {HTMLElement} el
1918
+ * @returns {String}
1919
+ * @private
1920
+ */
1921
+ function _generateId(el) {
1922
+ let str = el.tagName + el.className + el.src + el.href + el.textContent,
1923
+ i = str.length,
1924
+ sum = 0;
1925
+
1926
+ while (i--) {
1927
+ sum += str.charCodeAt(i);
1928
+ }
1929
+
1930
+ return sum.toString(36);
1931
+ }
1932
+
1933
+ function _saveInputCheckedState(root) {
1934
+ savedInputChecked.length = 0;
1935
+
1936
+ let inputs = root.getElementsByTagName('input');
1937
+ let idx = inputs.length;
1938
+
1939
+ while (idx--) {
1940
+ let el = inputs[idx];
1941
+ el.checked && savedInputChecked.push(el);
1942
+ }
1943
+ }
1944
+
1945
+ function _nextTick(fn) {
1946
+ return setTimeout(fn, 0);
1947
+ }
1948
+
1949
+ function _cancelNextTick(id) {
1950
+ return clearTimeout(id);
1951
+ }
1952
+
1953
+ // Fixed #973:
1954
+ if (documentExists) {
1955
+ on(document, 'touchmove', function(evt) {
1956
+ if ((Sortable.active || awaitingDragStarted) && evt.cancelable) {
1957
+ evt.preventDefault();
1958
+ }
1959
+ });
1960
+ }
1961
+
1962
+
1963
+ // Export utils
1964
+ Sortable.utils = {
1965
+ on,
1966
+ off,
1967
+ css,
1968
+ find,
1969
+ is: function (el, selector) {
1970
+ return !!closest(el, selector, el, false);
1971
+ },
1972
+ extend,
1973
+ throttle,
1974
+ closest,
1975
+ toggleClass,
1976
+ clone,
1977
+ index,
1978
+ nextTick: _nextTick,
1979
+ cancelNextTick: _cancelNextTick,
1980
+ detectDirection: _detectDirection,
1981
+ getChild,
1982
+ expando
1983
+ };
1984
+
1985
+
1986
+ /**
1987
+ * Get the Sortable instance of an element
1988
+ * @param {HTMLElement} element The element
1989
+ * @return {Sortable|undefined} The instance of Sortable
1990
+ */
1991
+ Sortable.get = function(element) {
1992
+ return element[expando];
1993
+ };
1994
+
1995
+ /**
1996
+ * Mount a plugin to Sortable
1997
+ * @param {...SortablePlugin|SortablePlugin[]} plugins Plugins being mounted
1998
+ */
1999
+ Sortable.mount = function(...plugins) {
2000
+ if (plugins[0].constructor === Array) plugins = plugins[0];
2001
+
2002
+ plugins.forEach((plugin) => {
2003
+ if (!plugin.prototype || !plugin.prototype.constructor) {
2004
+ throw `Sortable: Mounted plugin must be a constructor function, not ${ {}.toString.call(plugin) }`;
2005
+ }
2006
+ if (plugin.utils) Sortable.utils = { ...Sortable.utils, ...plugin.utils };
2007
+
2008
+ PluginManager.mount(plugin);
2009
+ });
2010
+ };
2011
+
2012
+
2013
+
2014
+ /**
2015
+ * Create sortable instance
2016
+ * @param {HTMLElement} el
2017
+ * @param {Object} [options]
2018
+ */
2019
+ Sortable.create = function (el, options) {
2020
+ return new Sortable(el, options);
2021
+ };
2022
+
2023
+
2024
+ // Export
2025
+ Sortable.version = version;
2026
+
2027
+
2028
+ export default Sortable;