sortablejs 1.15.2 → 1.15.4

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