Sortable.js 62.3 KB
Newer Older
R
RubaXa 已提交
1 2 3
/**!
 * Sortable
 * @author	RubaXa   <trash@rubaxa.org>
O
owen-m1 已提交
4
 * @author	owenm    <owen23355@gmail.com>
R
RubaXa 已提交
5 6 7
 * @license MIT
 */

L
Lebedev Konstantin 已提交
8
(function sortableModule(factory) {
R
RubaXa 已提交
9 10
	"use strict";

R
RubaXa 已提交
11
	if (typeof define === "function" && define.amd) {
R
RubaXa 已提交
12
		define(factory);
R
RubaXa 已提交
13
	}
R
RubaXa 已提交
14
	else if (typeof module != "undefined" && typeof module.exports != "undefined") {
S
Scott Nelson 已提交
15 16
		module.exports = factory();
	}
R
RubaXa 已提交
17
	else {
R
RubaXa 已提交
18
		/* jshint sub:true */
R
RubaXa 已提交
19 20
		window["Sortable"] = factory();
	}
L
Lebedev Konstantin 已提交
21
})(function sortableFactory() {
R
RubaXa 已提交
22 23
	"use strict";

R
RubaXa 已提交
24
	if (typeof window === "undefined" || !window.document) {
L
Lebedev Konstantin 已提交
25
		return function sortableError() {
R
RubaXa 已提交
26 27
			throw new Error("Sortable.js requires a window with a document");
		};
O
Onoshko Dan 已提交
28
	}
R
RubaXa 已提交
29

R
RubaXa 已提交
30
	var dragEl,
31
		parentEl,
R
RubaXa 已提交
32 33 34 35
		ghostEl,
		cloneEl,
		rootEl,
		nextEl,
L
Lebedev Konstantin 已提交
36
		lastDownEl,
R
RubaXa 已提交
37

38 39
		scrollEl,
		scrollParentEl,
40
		scrollCustomFn,
41

R
RubaXa 已提交
42 43 44
		oldIndex,
		newIndex,

R
RubaXa 已提交
45
		activeGroup,
R
RubaXa 已提交
46 47
		putSortable,

F
Flame2057 已提交
48
		autoScrolls = [],
O
owen-m1 已提交
49
		scrolling = false,
F
Flame2057 已提交
50

O
1.8.0  
owen-m1 已提交
51 52 53 54
		awaitingDragStarted = false,
		ignoreNextClick = false,
		sortables = [],

F
Flame2057 已提交
55 56 57
		pointerElemChangedInterval,
		lastPointerElemX,
		lastPointerElemY,
R
RubaXa 已提交
58

R
RubaXa 已提交
59 60
		tapEvt,
		touchEvt,
R
RubaXa 已提交
61

C
ChiefORZ 已提交
62 63
		moved,

O
1.8.0  
owen-m1 已提交
64

O
owen-m1 已提交
65 66 67 68
		lastTarget,
		lastDirection,
		pastFirstInvertThresh = false,
		isCircumstantialInvert = false,
O
1.8.0  
owen-m1 已提交
69 70 71 72
		lastMode, // 'swap' or 'insert'

		targetMoveDistance,

O
owen-m1 已提交
73

74
		forRepaintDummy,
O
owen-m1 已提交
75
		realDragElRect, // dragEl rect after current animation
76

R
RubaXa 已提交
77
		/** @const */
78
		R_SPACE = /\s+/g,
R
RubaXa 已提交
79

R
RubaXa 已提交
80
		expando = 'Sortable' + (new Date).getTime(),
R
RubaXa 已提交
81

R
RubaXa 已提交
82 83 84
		win = window,
		document = win.document,
		parseInt = win.parseInt,
R
RubaXa 已提交
85
		setTimeout = win.setTimeout,
R
RubaXa 已提交
86

R
* UPD  
RubaXa 已提交
87
		$ = win.jQuery || win.Zepto,
88 89
		Polymer = win.Polymer,

O
owen-m1 已提交
90 91 92 93
		captureMode = {
			capture: false,
			passive: false
		},
94

O
1.8.0  
owen-m1 已提交
95 96 97 98 99 100 101
		IE11OrLess = !!navigator.userAgent.match(/(?:Trident.*rv[ :]?11\.|msie|iemobile)/i),
		Edge = !!navigator.userAgent.match(/Edge/i),
		// FireFox = !!navigator.userAgent.match(/firefox/i),

		CSSFloatProperty = Edge || IE11OrLess ? 'cssFloat' : 'float',

		// This will not pass for IE9, because IE9 DnD only works on anchors
R
RubaXa 已提交
102
		supportDraggable = ('draggable' in document.createElement('div')),
O
1.8.0  
owen-m1 已提交
103 104 105 106

		supportCssPointerEvents = (function() {
			// false when <= IE11
			if (IE11OrLess) {
107 108
				return false;
			}
O
1.8.0  
owen-m1 已提交
109
			var el = document.createElement('x');
110 111 112
			el.style.cssText = 'pointer-events:auto';
			return el.style.pointerEvents === 'auto';
		})(),
R
RubaXa 已提交
113

R
RubaXa 已提交
114
		_silent = false,
O
owen-m1 已提交
115
		_alignedSilent = false,
R
RubaXa 已提交
116

R
RubaXa 已提交
117
		abs = Math.abs,
R
RubaXa 已提交
118
		min = Math.min,
R
RubaXa 已提交
119

L
Lebedev Konstantin 已提交
120
		savedInputChecked = [],
R
RubaXa 已提交
121

O
owen-m1 已提交
122 123 124 125 126 127 128
		_detectDirection = function(el, options) {
			var elCSS = _css(el),
				elWidth = parseInt(elCSS.width),
				child1 = _getChild(el, 0, options),
				child2 = _getChild(el, 1, options),
				firstChildCSS = child1 && _css(child1),
				secondChildCSS = child2 && _css(child2),
O
1.8.0  
owen-m1 已提交
129 130
				firstChildWidth = firstChildCSS && parseInt(firstChildCSS.marginLeft) + parseInt(firstChildCSS.marginRight) + _getRect(child1).width,
				secondChildWidth = secondChildCSS && parseInt(secondChildCSS.marginLeft) + parseInt(secondChildCSS.marginRight) + _getRect(child2).width;
O
owen-m1 已提交
131 132 133 134
			if (elCSS.display === 'flex') {
				return elCSS.flexDirection === 'column' || elCSS.flexDirection === 'column-reverse'
				? 'vertical' : 'horizontal';
			}
135 136 137 138 139 140
			if (child1 && firstChildCSS.float !== 'none') {
				var touchingSideChild2 = firstChildCSS.float === 'left' ? 'left' : 'right';

				return child2 && (secondChildCSS.clear === 'both' || secondChildCSS.clear === touchingSideChild2) ?
					'vertical' : 'horizontal';
			}
O
owen-m1 已提交
141 142
			return (child1 &&
				(
143
					firstChildCSS.display === 'block' ||
O
1.8.0  
owen-m1 已提交
144 145
					firstChildCSS.display === 'flex' ||
					firstChildCSS.display === 'table' ||
O
owen-m1 已提交
146 147
					firstChildCSS.display === 'grid' ||
					firstChildWidth >= elWidth &&
O
1.8.0  
owen-m1 已提交
148
					elCSS[CSSFloatProperty] === 'none' ||
O
owen-m1 已提交
149
					child2 &&
O
1.8.0  
owen-m1 已提交
150
					elCSS[CSSFloatProperty] === 'none' &&
O
owen-m1 已提交
151 152 153 154 155 156
					firstChildWidth + secondChildWidth > elWidth
				) ?
				'vertical' : 'horizontal'
			);
		},

O
1.8.0  
owen-m1 已提交
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
		/**
		 * Detects first nearest empty sortable to X and Y position using emptyInsertThreshold.
		 * @param  {Number} x      X position
		 * @param  {Number} y      Y position
		 * @return {HTMLElement}   Element of the first found nearest Sortable
		 */
		_detectNearestEmptySortable = function(x, y) {
			for (var i = 0; i < sortables.length; i++) {
				if (sortables[i].children.length) continue;

				var rect = _getRect(sortables[i]),
					threshold = sortables[i][expando].options.emptyInsertThreshold,
					insideHorizontally = x >= (rect.left - threshold) && x <= (rect.right + threshold),
					insideVertically = y >= (rect.top - threshold) && y <= (rect.bottom + threshold);

				if (insideHorizontally && insideVertically) {
					return sortables[i];
				}
			}
		},

		_isClientInRowColumn = function(x, y, el, axis, options) {
			var targetRect = _getRect(el),
O
owen-m1 已提交
180 181
				targetS1Opp = axis === 'vertical' ? targetRect.left : targetRect.top,
				targetS2Opp = axis === 'vertical' ? targetRect.right : targetRect.bottom,
O
1.8.0  
owen-m1 已提交
182
				mouseOnOppAxis = axis === 'vertical' ? x : y;
O
owen-m1 已提交
183 184 185

			return targetS1Opp < mouseOnOppAxis && mouseOnOppAxis < targetS2Opp;
		},
F
Flame2057 已提交
186

O
1.8.0  
owen-m1 已提交
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
		_isElInRowColumn = function(el1, el2, axis) {
			var el1Rect = el1 === dragEl && realDragElRect || _getRect(el1),
				el2Rect = el2 === dragEl && realDragElRect || _getRect(el2),
				el1S1Opp = axis === 'vertical' ? el1Rect.left : el1Rect.top,
				el1S2Opp = axis === 'vertical' ? el1Rect.right : el1Rect.bottom,
				el1OppLength = axis === 'vertical' ? el1Rect.width : el1Rect.height,
				el2S1Opp = axis === 'vertical' ? el2Rect.left : el2Rect.top,
				el2S2Opp = axis === 'vertical' ? el2Rect.right : el2Rect.bottom,
				el2OppLength = axis === 'vertical' ? el2Rect.width : el2Rect.height;

			return (
				el1S1Opp === el2S1Opp ||
				el1S2Opp === el2S2Opp ||
				(el1S1Opp + el1OppLength / 2) === (el2S1Opp + el2OppLength / 2)
			);
		},

O
owen-m1 已提交
204 205 206 207 208
		_getParentAutoScrollElement = function(el, includeSelf) {
			// skip to window
			if (!el || !el.getBoundingClientRect) return win;

			var elem = el;
F
Flame2057 已提交
209 210
			var gotSelf = false;
			do {
O
owen-m1 已提交
211 212 213 214 215 216 217 218
				// we don't need to get elem css if it isn't even overflowing in the first place (performance)
				if (elem.clientWidth < elem.scrollWidth || elem.clientHeight < elem.scrollHeight) {
					var elemCSS = _css(elem);
					if (
						elem.clientWidth < elem.scrollWidth && (elemCSS.overflowX == 'auto' || elemCSS.overflowX == 'scroll') ||
						elem.clientHeight < elem.scrollHeight && (elemCSS.overflowY == 'auto' || elemCSS.overflowY == 'scroll')
					) {
						if (!elem || !elem.getBoundingClientRect || elem === document.body) return win;
F
Flame2057 已提交
219

O
owen-m1 已提交
220 221 222
						if (gotSelf || includeSelf) return elem;
						gotSelf = true;
					}
F
Flame2057 已提交
223
				}
O
owen-m1 已提交
224
			/* jshint boss:true */
F
Flame2057 已提交
225
			} while (elem = elem.parentNode);
O
owen-m1 已提交
226 227

			return win;
F
Flame2057 已提交
228 229
		},

O
owen-m1 已提交
230
		_autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl, /**Boolean*/isFallback) {
231
			// Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521
F
Flame2057 已提交
232 233
			if (options.scroll) {
				var _this = rootEl ? rootEl[expando] : window,
234 235 236 237 238 239 240 241 242
					sens = options.scrollSensitivity,
					speed = options.scrollSpeed,

					x = evt.clientX,
					y = evt.clientY,

					winWidth = window.innerWidth,
					winHeight = window.innerHeight,

O
1.8.0  
owen-m1 已提交
243
					scrollThisInstance = false;
244

F
Flame2057 已提交
245
				// Detect scrollEl
246
				if (scrollParentEl !== rootEl) {
F
Flame2057 已提交
247 248
					_clearAutoScrolls();

249
					scrollEl = options.scroll;
250
					scrollCustomFn = options.scrollFn;
251 252

					if (scrollEl === true) {
F
Flame2057 已提交
253 254
						scrollEl = _getParentAutoScrollElement(rootEl, true);
						scrollParentEl = scrollEl;
255 256 257 258
					}
				}


F
Flame2057 已提交
259 260 261
				var layersOut = 0;
				var currentParent = scrollEl;
				do {
O
1.8.0  
owen-m1 已提交
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
					var	el = currentParent,
						rect = _getRect(el),

						top = rect.top,
						bottom = rect.bottom,
						left = rect.left,
						right = rect.right,

						width = rect.width,
						height = rect.height,

						scrollWidth,
						scrollHeight,

						css,

						vx,
						vy,

						canScrollX,
						canScrollY,

						scrollPosX,
						scrollPosY;


					if (el !== win) {
						scrollWidth = el.scrollWidth;
						scrollHeight = el.scrollHeight;
291

O
owen-m1 已提交
292
						css = _css(el);
O
1.8.0  
owen-m1 已提交
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309

						canScrollX = width < scrollWidth && (css.overflowX === 'auto' || css.overflowX === 'scroll');
						canScrollY = height < scrollHeight && (css.overflowY === 'auto' || css.overflowY === 'scroll');

						scrollPosX = el.scrollLeft;
						scrollPosY = el.scrollTop;
					} else {
						scrollWidth = document.documentElement.scrollWidth;
						scrollHeight = document.documentElement.scrollHeight;

						css = _css(document.documentElement);

						canScrollX = width < scrollWidth && (css.overflowX === 'auto' || css.overflowX === 'scroll' || css.overflowX === 'visible');
						canScrollY = height < scrollHeight && (css.overflowY === 'auto' || css.overflowY === 'scroll' || css.overflowY === 'visible');

						scrollPosX = document.documentElement.scrollLeft;
						scrollPosY = document.documentElement.scrollTop;
F
Flame2057 已提交
310
					}
311

O
1.8.0  
owen-m1 已提交
312 313 314 315 316
					vx = canScrollX && (abs(right - x) <= sens && (scrollPosX + width) < scrollWidth) - (abs(left - x) <= sens && !!scrollPosX);

					vy = canScrollY && (abs(bottom - y) <= sens && (scrollPosY + height) < scrollHeight) - (abs(top - y) <= sens && !!scrollPosY);


F
Flame2057 已提交
317 318 319 320 321 322 323
					if (!autoScrolls[layersOut]) {
						for (var i = 0; i <= layersOut; i++) {
							if (!autoScrolls[i]) {
								autoScrolls[i] = {};
							}
						}
					}
324

O
owen-m1 已提交
325
					if (autoScrolls[layersOut].vx != vx || autoScrolls[layersOut].vy != vy || autoScrolls[layersOut].el !== el) {
F
Flame2057 已提交
326 327 328
						autoScrolls[layersOut].el = el;
						autoScrolls[layersOut].vx = vx;
						autoScrolls[layersOut].vy = vy;
329

F
Flame2057 已提交
330
						clearInterval(autoScrolls[layersOut].pid);
331

O
owen-m1 已提交
332 333 334
						if (el && (vx != 0 || vy != 0)) {
							scrollThisInstance = true;
							/* jshint loopfunc:true */
F
Flame2057 已提交
335
							autoScrolls[layersOut].pid = setInterval((function () {
O
owen-m1 已提交
336 337 338 339 340 341
								// emulate drag over during autoscroll (fallback), emulating native DnD behaviour
								if (isFallback && this.layer === 0) {
									Sortable.active._emulateDragOver(true);
								}
								var scrollOffsetY = autoScrolls[this.layer].vy ? autoScrolls[this.layer].vy * speed : 0;
								var scrollOffsetX = autoScrolls[this.layer].vx ? autoScrolls[this.layer].vx * speed : 0;
342

F
Flame2057 已提交
343
								if ('function' === typeof(scrollCustomFn)) {
O
owen-m1 已提交
344
									if (scrollCustomFn.call(_this, scrollOffsetX, scrollOffsetY, evt, touchEvt, autoScrolls[this.layer].el) !== 'continue') {
F
Flame2057 已提交
345 346
										return;
									}
N
Noel Heesen 已提交
347
								}
O
owen-m1 已提交
348
								if (autoScrolls[this.layer].el === win) {
F
Flame2057 已提交
349 350
									win.scrollTo(win.pageXOffset + scrollOffsetX, win.pageYOffset + scrollOffsetY);
								} else {
O
owen-m1 已提交
351 352
									autoScrolls[this.layer].el.scrollTop += scrollOffsetY;
									autoScrolls[this.layer].el.scrollLeft += scrollOffsetX;
F
Flame2057 已提交
353
								}
O
owen-m1 已提交
354
							}).bind({layer: layersOut}), 24);
F
Flame2057 已提交
355
						}
356
					}
F
Flame2057 已提交
357
					layersOut++;
O
owen-m1 已提交
358 359
				} while (options.bubbleScroll && currentParent !== win && (currentParent = _getParentAutoScrollElement(currentParent, false)));
				scrolling = scrollThisInstance; // in case another function catches scrolling as false in between when it is not
360
			}
R
RubaXa 已提交
361 362
		}, 30),

F
Flame2057 已提交
363 364 365 366 367 368 369
		_clearAutoScrolls = function () {
			autoScrolls.forEach(function(autoScroll) {
				clearInterval(autoScroll.pid);
			});
			autoScrolls = [];
		},

R
RubaXa 已提交
370
		_prepareGroup = function (options) {
R
RubaXa 已提交
371
			function toFn(value, pull) {
372
				return function(to, from, dragEl, evt) {
O
1.8.0  
owen-m1 已提交
373 374 375
					var sameGroup = to.options.group.name &&
									from.options.group.name &&
									to.options.group.name === from.options.group.name;
376

O
1.8.0  
owen-m1 已提交
377 378 379 380
					if (value == null && (pull || sameGroup)) {
						// Default pull value
						// Default pull and put value if same group
						return true;
O
owen-m1 已提交
381
					} else if (value == null || value === false) {
O
1.8.0  
owen-m1 已提交
382
						return false;
383
					} else if (pull && value === 'clone') {
O
1.8.0  
owen-m1 已提交
384
						return value;
385
					} else if (typeof value === 'function') {
O
1.8.1  
owen-m1 已提交
386
						return toFn(value(to, from, dragEl, evt), pull)(to, from, dragEl, evt);
387
					} else {
F
Flame2057 已提交
388 389
						var otherGroup = (pull ? to : from).options.group.name;

O
1.8.0  
owen-m1 已提交
390
						return (value === true ||
F
Flame2057 已提交
391
						(typeof value === 'string' && value === otherGroup) ||
392 393
						(value.join && value.indexOf(otherGroup) > -1));
					}
O
owen-m1 已提交
394
				};
R
RubaXa 已提交
395 396
			}

R
RubaXa 已提交
397 398
			var group = {};
			var originalGroup = options.group;
R
RubaXa 已提交
399

R
RubaXa 已提交
400
			if (!originalGroup || typeof originalGroup != 'object') {
D
desmaisons_david 已提交
401
				originalGroup = {name: originalGroup};
R
RubaXa 已提交
402 403
			}

R
RubaXa 已提交
404 405 406
			group.name = originalGroup.name;
			group.checkPull = toFn(originalGroup.pull, true);
			group.checkPut = toFn(originalGroup.put);
L
Lebedev Konstantin 已提交
407
			group.revertClone = originalGroup.revertClone;
R
RubaXa 已提交
408 409

			options.group = group;
O
owen-m1 已提交
410 411 412
		},

		_checkAlignment = function(evt) {
O
1.8.0  
owen-m1 已提交
413
			if (!dragEl || !dragEl.parentNode) return;
O
owen-m1 已提交
414
			dragEl.parentNode[expando] && dragEl.parentNode[expando]._computeIsAligned(evt);
O
1.8.0  
owen-m1 已提交
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
		},

		_isTrueParentSortable = function(el, target) {
			var trueParent = target;
			while (!trueParent[expando]) {
				trueParent = trueParent.parentNode;
			}

			return el === trueParent;
		},

		_artificalBubble = function(sortable, originalEvt, method) {
			// Artificial IE bubbling
			var nextParent = sortable.parentNode;
			while (nextParent && !nextParent[expando]) {
				nextParent = nextParent.parentNode;
			}

			if (nextParent) {
				nextParent[expando][method](_extend(originalEvt, {
					artificialBubble: true
				}));
			}
		},

		_hideGhostForTarget = function() {
			if (!supportCssPointerEvents && ghostEl) {
				_css(ghostEl, 'display', 'none');
			}
		},

		_unhideGhostForTarget = function() {
			if (!supportCssPointerEvents && ghostEl) {
				_css(ghostEl, 'display', '');
			}
		};
R
RubaXa 已提交
451 452


O
1.8.0  
owen-m1 已提交
453 454 455 456 457 458 459 460 461 462 463 464
	// #1184 fix - Prevent click event on fallback if dragged but item not changed position
	document.addEventListener('click', function(evt) {
		if (ignoreNextClick) {
			evt.preventDefault();
			evt.stopPropagation && evt.stopPropagation();
			evt.stopImmediatePropagation && evt.stopImmediatePropagation();
			ignoreNextClick = false;
			return false;
		}
	}, true);

	var nearestEmptyInsertDetectEvent = function(evt) {
O
owen-m1 已提交
465
		evt = evt.touches ? evt.touches[0] : evt;
O
1.8.0  
owen-m1 已提交
466 467 468 469 470 471 472 473 474 475 476 477 478 479
		if (dragEl) {
			var nearest = _detectNearestEmptySortable(evt.clientX, evt.clientY);

			if (nearest) {
				nearest[expando]._onDragOver({
					clientX: evt.clientX,
					clientY: evt.clientY,
					target: nearest,
					rootEl: nearest
				});
			}
		}
	};
	// We do not want this to be triggered if completed (bubbling canceled), so only define it here
O
owen-m1 已提交
480 481 482
	_on(document, 'dragover', nearestEmptyInsertDetectEvent);
	_on(document, 'mousemove', nearestEmptyInsertDetectEvent);
	_on(document, 'touchmove', nearestEmptyInsertDetectEvent);
O
1.8.0  
owen-m1 已提交
483

R
RubaXa 已提交
484 485 486
	/**
	 * @class  Sortable
	 * @param  {HTMLElement}  el
487
	 * @param  {Object}       [options]
R
RubaXa 已提交
488
	 */
R
RubaXa 已提交
489
	function Sortable(el, options) {
R
RubaXa 已提交
490
		if (!(el && el.nodeType && el.nodeType === 1)) {
O
1.8.0  
owen-m1 已提交
491
			throw 'Sortable: `el` must be HTMLElement, not ' + {}.toString.call(el);
R
RubaXa 已提交
492 493
		}

R
RubaXa 已提交
494
		this.el = el; // root element
495
		this.options = options = _extend({}, options);
R
RubaXa 已提交
496 497


498 499 500
		// Export instance
		el[expando] = this;

R
RubaXa 已提交
501
		// Default options
502
		var defaults = {
R
RubaXa 已提交
503
			group: null,
R
RubaXa 已提交
504
			sort: true,
R
RubaXa 已提交
505
			disabled: false,
506 507
			store: null,
			handle: null,
O
owen-m1 已提交
508
			scroll: true,
R
RubaXa 已提交
509 510
			scrollSensitivity: 30,
			scrollSpeed: 10,
511
			bubbleScroll: true,
512
			draggable: /[uo]l/i.test(el.nodeName) ? '>li' : '>*',
O
owen-m1 已提交
513 514 515
			swapThreshold: 1, // percentage; 0 <= x <= 1
			invertSwap: false, // invert always
			invertedSwapThreshold: null, // will be set to same as swapThreshold if default
O
1.8.0  
owen-m1 已提交
516 517 518 519
			removeCloneOnHide: true,
			direction: function() {
				return _detectDirection(el, this.options);
			},
520
			ghostClass: 'sortable-ghost',
R
RubaXa 已提交
521
			chosenClass: 'sortable-chosen',
R
RubaXa 已提交
522
			dragClass: 'sortable-drag',
523
			ignore: 'a, img',
524
			filter: null,
L
Lebedev Konstantin 已提交
525
			preventOnFilter: true,
R
RubaXa 已提交
526
			animation: 0,
O
1.8.0  
owen-m1 已提交
527
			easing: null,
R
RubaXa 已提交
528 529
			setData: function (dataTransfer, dragEl) {
				dataTransfer.setData('Text', dragEl.textContent);
530 531
			},
			dropBubble: false,
R
RubaXa 已提交
532
			dragoverBubble: false,
533
			dataIdAttr: 'data-id',
534
			delay: 0,
535
			touchStartThreshold: parseInt(window.devicePixelRatio, 10) || 1,
C
ChiefORZ 已提交
536 537
			forceFallback: false,
			fallbackClass: 'sortable-fallback',
538
			fallbackOnBody: false,
539
			fallbackTolerance: 0,
R
RubaXa 已提交
540
			fallbackOffset: {x: 0, y: 0},
O
owen-m1 已提交
541 542 543
			supportPointer: Sortable.supportPointer !== false && (
				('PointerEvent' in window) ||
				window.navigator && ('msPointerEnabled' in window.navigator) // microsoft
O
1.8.0  
owen-m1 已提交
544 545
			),
			emptyInsertThreshold: 5
R
RubaXa 已提交
546
		};
547

R
RubaXa 已提交
548

549 550
		// Set default options
		for (var name in defaults) {
R
RubaXa 已提交
551
			!(name in options) && (options[name] = defaults[name]);
552
		}
R
RubaXa 已提交
553

R
RubaXa 已提交
554
		_prepareGroup(options);
R
RubaXa 已提交
555

R
* JSDoc  
RubaXa 已提交
556
		// Bind all private methods
R
RubaXa 已提交
557
		for (var fn in this) {
D
dev101 已提交
558
			if (fn.charAt(0) === '_' && typeof this[fn] === 'function') {
559
				this[fn] = this[fn].bind(this);
R
RubaXa 已提交
560 561 562
			}
		}

R
RubaXa 已提交
563 564
		// Setup drag mode
		this.nativeDraggable = options.forceFallback ? false : supportDraggable;
R
RubaXa 已提交
565 566

		// Bind events
O
1.8.0  
owen-m1 已提交
567 568 569 570 571 572
		if (options.supportPointer) {
			_on(el, 'pointerdown', this._onTapStart);
		} else {
			_on(el, 'mousedown', this._onTapStart);
			_on(el, 'touchstart', this._onTapStart);
		}
R
RubaXa 已提交
573

R
RubaXa 已提交
574
		if (this.nativeDraggable) {
575 576 577
			_on(el, 'dragover', this);
			_on(el, 'dragenter', this);
		}
R
RubaXa 已提交
578

O
1.8.0  
owen-m1 已提交
579
		sortables.push(this.el);
580 581

		// Restore sorting
O
owen-m1 已提交
582
		options.store && options.store.get && this.sort(options.store.get(this) || []);
R
RubaXa 已提交
583 584
	}

585
	Sortable.prototype = /** @lends Sortable.prototype */ {
R
RubaXa 已提交
586 587
		constructor: Sortable,

O
1.8.0  
owen-m1 已提交
588 589 590 591 592 593 594 595 596 597
		_computeIsAligned: function(evt) {
			var target;

			if (ghostEl && !supportCssPointerEvents) {
				_hideGhostForTarget();
				target = document.elementFromPoint(evt.clientX, evt.clientY);
				_unhideGhostForTarget();
			} else {
				target = evt.target;
			}
O
owen-m1 已提交
598

O
1.8.0  
owen-m1 已提交
599
			target = _closest(target, this.options.draggable, this.el, false);
O
owen-m1 已提交
600 601
			if (_alignedSilent) return;
			if (!dragEl || dragEl.parentNode !== this.el) return;
O
1.8.0  
owen-m1 已提交
602 603 604 605 606 607 608

			var children = this.el.children;
			for (var i = 0; i < children.length; i++) {
				// Don't change for target in case it is changed to aligned before onDragOver is fired
				if (_closest(children[i], this.options.draggable, this.el, false) && children[i] !== target) {
					children[i].sortableMouseAligned = _isClientInRowColumn(evt.clientX, evt.clientY, children[i], this._getDirection(evt, null), this.options);
				}
O
owen-m1 已提交
609
			}
O
1.8.0  
owen-m1 已提交
610 611 612 613 614
			// Used for nulling last target when not in element, nothing to do with checking if aligned
			if (!_closest(target, this.options.draggable, this.el, true)) {
				lastTarget = null;
			}

O
owen-m1 已提交
615 616 617 618
			_alignedSilent = true;
			setTimeout(function() {
				_alignedSilent = false;
			}, 30);
O
1.8.0  
owen-m1 已提交
619

O
owen-m1 已提交
620 621 622 623 624 625
		},

		_getDirection: function(evt, target) {
			return (typeof this.options.direction === 'function') ? this.options.direction.call(this, evt, target, dragEl) : this.options.direction;
		},

626
		_onTapStart: function (/** Event|TouchEvent */evt) {
O
1.8.0  
owen-m1 已提交
627
			if (!evt.cancelable) return;
628 629
			var _this = this,
				el = this.el,
630
				options = this.options,
631
				preventOnFilter = options.preventOnFilter,
632 633 634
				type = evt.type,
				touch = evt.touches && evt.touches[0],
				target = (touch || evt).target,
G
Gaetan D 已提交
635
				originalTarget = evt.target.shadowRoot && ((evt.path && evt.path[0]) || (evt.composedPath && evt.composedPath()[0])) || target,
R
RubaXa 已提交
636 637
				filter = options.filter,
				startIndex;
R
RubaXa 已提交
638

L
Lebedev Konstantin 已提交
639 640 641
			_saveInputCheckedState(el);


O
1.8.0  
owen-m1 已提交
642 643 644 645 646 647
			// IE: Calls events in capture mode if event element is nested. This ensures only correct element's _onTapStart goes through.
			// This process is also done in _onDragOver
			if (IE11OrLess && !evt.artificialBubble && !_isTrueParentSortable(el, target)) {
				return;
			}

R
RubaXa 已提交
648
			// Don't trigger start event when an element is been dragged, otherwise the evt.oldindex always wrong when set option.group.
W
why520crazy 已提交
649
			if (dragEl) {
650 651
				return;
			}
R
RubaXa 已提交
652

653
			if (/mousedown|pointerdown/.test(type) && evt.button !== 0 || options.disabled) {
O
1.8.0  
owen-m1 已提交
654
				return; // only left button and enabled
R
RubaXa 已提交
655
			}
R
RubaXa 已提交
656

657 658 659 660
			// cancel dnd if original target is content editable
			if (originalTarget.isContentEditable) {
				return;
			}
661

O
1.8.0  
owen-m1 已提交
662
			target = _closest(target, options.draggable, el, false);
663

664
			if (!target) {
O
1.8.0  
owen-m1 已提交
665 666 667
				if (IE11OrLess) {
					_artificalBubble(el, evt, '_onTapStart');
				}
668 669
				return;
			}
670

L
Lebedev Konstantin 已提交
671 672 673 674 675
			if (lastDownEl === target) {
				// Ignoring duplicate `down`
				return;
			}

R
RubaXa 已提交
676
			// Get the index of the dragged element within its parent
R
RubaXa 已提交
677
			startIndex = _index(target, options.draggable);
678 679 680 681

			// Check filter
			if (typeof filter === 'function') {
				if (filter.call(this, evt, target, this)) {
J
Joey Becker 已提交
682
					_dispatchEvent(_this, originalTarget, 'filter', target, el, el, startIndex);
O
owen-m1 已提交
683
					preventOnFilter && evt.cancelable && evt.preventDefault();
684 685
					return; // cancel dnd
				}
686
			}
687 688
			else if (filter) {
				filter = filter.split(',').some(function (criteria) {
O
owen-m1 已提交
689
					criteria = _closest(originalTarget, criteria.trim(), el, false);
690

691
					if (criteria) {
J
Joey Becker 已提交
692
						_dispatchEvent(_this, criteria, 'filter', target, el, el, startIndex);
693 694 695 696 697
						return true;
					}
				});

				if (filter) {
O
owen-m1 已提交
698
					preventOnFilter && evt.cancelable && evt.preventDefault();
699
					return; // cancel dnd
700 701 702
				}
			}

O
owen-m1 已提交
703
			if (options.handle && !_closest(originalTarget, options.handle, el, false)) {
704 705 706
				return;
			}

707
			// Prepare `dragstart`
R
RubaXa 已提交
708
			this._prepareDragStart(evt, touch, target, startIndex);
709 710
		},

F
Flame2057 已提交
711

O
owen-m1 已提交
712 713 714 715
		_handleAutoScroll: function(evt, fallback) {
			if (!dragEl || !this.options.scroll) return;
			var x = evt.clientX,
				y = evt.clientY,
F
Flame2057 已提交
716

717
				elem = document.elementFromPoint(x, y),
O
1.8.0  
owen-m1 已提交
718
				_this = this;
F
Flame2057 已提交
719

O
1.8.0  
owen-m1 已提交
720 721 722 723 724
			// IE does not seem to have native autoscroll,
			// Edge's autoscroll seems too conditional,
			// Firefox and Chrome are good
			if (fallback || Edge || IE11OrLess) {
				_autoScroll(evt, _this.options, elem, fallback);
F
Flame2057 已提交
725 726 727

				// Listener for pointer element change
				var ogElemScroller = _getParentAutoScrollElement(elem, true);
O
owen-m1 已提交
728 729 730 731 732 733 734 735
				if (
					scrolling &&
					(
						!pointerElemChangedInterval ||
						x !== lastPointerElemX ||
						y !== lastPointerElemY
					)
				) {
F
Flame2057 已提交
736 737

					pointerElemChangedInterval && clearInterval(pointerElemChangedInterval);
738
					// Detect for pointer elem change, emulating native DnD behaviour
F
Flame2057 已提交
739
					pointerElemChangedInterval = setInterval(function() {
740
						if (!dragEl) return;
O
owen-m1 已提交
741
						// could also check if scroll direction on newElem changes due to parent autoscrolling
F
Flame2057 已提交
742
						var newElem = _getParentAutoScrollElement(document.elementFromPoint(x, y), true);
O
owen-m1 已提交
743
						if (newElem !== ogElemScroller) {
F
Flame2057 已提交
744 745
							ogElemScroller = newElem;
							_clearAutoScrolls();
O
1.8.0  
owen-m1 已提交
746
							_autoScroll(evt, _this.options, ogElemScroller, fallback);
F
Flame2057 已提交
747 748 749 750 751 752 753
						}
					}, 10);
					lastPointerElemX = x;
					lastPointerElemY = y;
				}

			} else {
O
1.8.0  
owen-m1 已提交
754
				// if DnD is enabled (and browser has good autoscrolling), first autoscroll will already scroll, so get parent autoscroll of first autoscroll
O
owen-m1 已提交
755 756 757 758
				if (!_this.options.bubbleScroll || _getParentAutoScrollElement(elem, true) === window) {
					_clearAutoScrolls();
					return;
				}
O
1.8.0  
owen-m1 已提交
759
				_autoScroll(evt, _this.options, _getParentAutoScrollElement(elem, false), false);
F
Flame2057 已提交
760 761 762
			}
		},

R
RubaXa 已提交
763
		_prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target, /** Number */startIndex) {
764 765 766 767 768
			var _this = this,
				el = _this.el,
				options = _this.options,
				ownerDocument = el.ownerDocument,
				dragStartFn;
769 770

			if (target && !dragEl && (target.parentNode === el)) {
771
				rootEl = el;
772
				dragEl = target;
R
RubaXa 已提交
773
				parentEl = dragEl.parentNode;
774
				nextEl = dragEl.nextSibling;
D
desmaisons_david 已提交
775
				lastDownEl = target;
776
				activeGroup = options.group;
R
RubaXa 已提交
777
				oldIndex = startIndex;
778

O
1.8.0  
owen-m1 已提交
779 780 781 782 783 784
				tapEvt = {
					target: dragEl,
					clientX: (touch || evt).clientX,
					clientY: (touch || evt).clientY
				};

785 786
				this._lastX = (touch || evt).clientX;
				this._lastY = (touch || evt).clientY;
S
sp-kilobug 已提交
787

R
RubaXa 已提交
788
				dragEl.style['will-change'] = 'all';
O
1.8.0  
owen-m1 已提交
789 790 791
				// undo animation if needed
				dragEl.style.transition = '';
				dragEl.style.transform = '';
L
Lebedev Konstantin 已提交
792

793 794 795 796
				dragStartFn = function () {
					// Delayed drag has been triggered
					// we can re-enable the events: touchmove/mousemove
					_this._disableDelayedDrag();
797

798
					// Make the element draggable
799
					dragEl.draggable = _this.nativeDraggable;
Y
yak 已提交
800

801 802 803
					// Bind the events: dragstart/dragend
					_this._triggerDragStart(evt, touch);

804 805
					// Drag start event
					_dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, rootEl, oldIndex);
F
Flame2057 已提交
806

R
RubaXa 已提交
807
					// Chosen item
R
RubaXa 已提交
808
					_toggleClass(dragEl, options.chosenClass, true);
809 810
				};

R
RubaXa 已提交
811 812 813 814 815
				// Disable "draggable"
				options.ignore.split(',').forEach(function (criteria) {
					_find(dragEl, criteria.trim(), _disableDraggable);
				});

O
1.8.0  
owen-m1 已提交
816 817 818 819 820 821 822
				if (options.supportPointer) {
					_on(ownerDocument, 'pointerup', _this._onDrop);
				} else {
					_on(ownerDocument, 'mouseup', _this._onDrop);
					_on(ownerDocument, 'touchend', _this._onDrop);
					_on(ownerDocument, 'touchcancel', _this._onDrop);
				}
823 824

				if (options.delay) {
825 826
					// If the user moves the pointer or let go the click or touch
					// before the delay has been reached:
827
					// disable the delayed drag
828 829 830
					_on(ownerDocument, 'mouseup', _this._disableDelayedDrag);
					_on(ownerDocument, 'touchend', _this._disableDelayedDrag);
					_on(ownerDocument, 'touchcancel', _this._disableDelayedDrag);
Y
yak 已提交
831
					_on(ownerDocument, 'mousemove', _this._delayedDragTouchMoveHandler);
832 833
					_on(ownerDocument, 'touchmove', _this._delayedDragTouchMoveHandler);
					options.supportPointer && _on(ownerDocument, 'pointermove', _this._delayedDragTouchMoveHandler);
834

O
1.8.0  
owen-m1 已提交
835
					_this._dragStartTimer = setTimeout(dragStartFn, options.delay);
836 837
				} else {
					dragStartFn();
838
				}
D
desmaisons_david 已提交
839
			}
840
		},
R
RubaXa 已提交
841

842
		_delayedDragTouchMoveHandler: function (/** TouchEvent|PointerEvent **/e) {
Y
yak 已提交
843 844 845 846
			var touch = e.touches ? e.touches[0] : e;
			if (min(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY))
					>= this.options.touchStartThreshold
			) {
847 848 849 850
				this._disableDelayedDrag();
			}
		},

851 852
		_disableDelayedDrag: function () {
			var ownerDocument = this.el.ownerDocument;
853

854
			clearTimeout(this._dragStartTimer);
855 856 857
			_off(ownerDocument, 'mouseup', this._disableDelayedDrag);
			_off(ownerDocument, 'touchend', this._disableDelayedDrag);
			_off(ownerDocument, 'touchcancel', this._disableDelayedDrag);
Y
yak 已提交
858 859 860
			_off(ownerDocument, 'mousemove', this._delayedDragTouchMoveHandler);
			_off(ownerDocument, 'touchmove', this._delayedDragTouchMoveHandler);
			_off(ownerDocument, 'pointermove', this._delayedDragTouchMoveHandler);
861
		},
R
RubaXa 已提交
862

863
		_triggerDragStart: function (/** Event */evt, /** Touch */touch) {
864 865
			touch = touch || (evt.pointerType == 'touch' ? evt : null);

O
1.8.0  
owen-m1 已提交
866 867 868 869 870 871 872 873 874
			if (!this.nativeDraggable || touch) {
				if (this.options.supportPointer) {
					_on(document, 'pointermove', this._onTouchMove);
				} else if (touch) {
					_on(document, 'touchmove', this._onTouchMove);
				} else {
					_on(document, 'mousemove', this._onTouchMove);
				}
			} else {
875 876
				_on(dragEl, 'dragend', this);
				_on(rootEl, 'dragstart', this._onDragStart);
877 878
			}

879
			try {
880 881
				if (document.selection) {
					// Timeout neccessary for IE9
R
RubaXa 已提交
882
					_nextTick(function () {
883
						document.selection.empty();
884
					});
885 886
				} else {
					window.getSelection().removeAllRanges();
887
				}
888
			} catch (err) {
889
			}
890
		},
891

O
1.8.0  
owen-m1 已提交
892 893
		_dragStarted: function (fallback) {
			awaitingDragStarted = false;
894
			if (rootEl && dragEl) {
O
owen-m1 已提交
895 896 897 898
				if (this.nativeDraggable) {
					_on(document, 'dragover', this._handleAutoScroll);
					_on(document, 'dragover', _checkAlignment);
				}
R
RubaXa 已提交
899 900
				var options = this.options;

901
				// Apply effect
O
1.8.0  
owen-m1 已提交
902
				!fallback && _toggleClass(dragEl, options.dragClass, false);
O
owen-m1 已提交
903 904
				_toggleClass(dragEl, options.ghostClass, true);

O
1.8.0  
owen-m1 已提交
905
				// In case dragging an animated element
O
owen-m1 已提交
906
				_css(dragEl, 'transform', '');
R
RubaXa 已提交
907

908
				Sortable.active = this;
909

O
1.8.0  
owen-m1 已提交
910
				fallback && this._appendGhost();
O
owen-m1 已提交
911

912
				// Drag start event
J
Joey Becker 已提交
913
				_dispatchEvent(this, rootEl, 'start', dragEl, rootEl, rootEl, oldIndex);
L
#1009  
Lebedev Konstantin 已提交
914 915
			} else {
				this._nulling();
916
			}
R
RubaXa 已提交
917 918
		},

O
owen-m1 已提交
919
		_emulateDragOver: function (bypassLastTouchCheck) {
R
RubaXa 已提交
920
			if (touchEvt) {
O
owen-m1 已提交
921
				if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY && !bypassLastTouchCheck) {
922 923
					return;
				}
R
RubaXa 已提交
924 925
				this._lastX = touchEvt.clientX;
				this._lastY = touchEvt.clientY;
926

O
1.8.0  
owen-m1 已提交
927
				_hideGhostForTarget();
R
RubaXa 已提交
928

L
Luiz Corte Real 已提交
929 930
				var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
				var parent = target;
R
RubaXa 已提交
931

R
RubaXa 已提交
932
				while (target && target.shadowRoot) {
933
					target = target.shadowRoot.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
L
Luiz Corte Real 已提交
934
					parent = target;
935 936
				}

R
RubaXa 已提交
937 938
				if (parent) {
					do {
R
RubaXa 已提交
939
						if (parent[expando]) {
O
1.8.0  
owen-m1 已提交
940
							var inserted;
R
RubaXa 已提交
941

O
1.8.0  
owen-m1 已提交
942 943 944 945 946 947 948 949
							inserted = parent[expando]._onDragOver({
								clientX: touchEvt.clientX,
								clientY: touchEvt.clientY,
								target: target,
								rootEl: parent
							});

							if (inserted && !this.options.dragoverBubble) {
950 951
								break;
							}
R
RubaXa 已提交
952 953 954
						}

						target = parent; // store last element
L
Larry Davis 已提交
955
					}
R
RubaXa 已提交
956 957
					/* jshint boss:true */
					while (parent = parent.parentNode);
R
RubaXa 已提交
958
				}
O
1.8.0  
owen-m1 已提交
959
				dragEl.parentNode[expando]._computeIsAligned(touchEvt);
R
RubaXa 已提交
960

O
1.8.0  
owen-m1 已提交
961
				_unhideGhostForTarget();
R
RubaXa 已提交
962 963 964 965
			}
		},


R
RubaXa 已提交
966 967
		_onTouchMove: function (/**TouchEvent*/evt) {
			if (tapEvt) {
D
desmaisons_david 已提交
968
				var	options = this.options,
R
RubaXa 已提交
969
					fallbackTolerance = options.fallbackTolerance,
970
					fallbackOffset = options.fallbackOffset,
R
RubaXa 已提交
971
					touch = evt.touches ? evt.touches[0] : evt,
O
1.8.0  
owen-m1 已提交
972 973 974 975 976
					matrix = ghostEl && _matrix(ghostEl),
					scaleX = ghostEl && matrix && matrix.a,
					scaleY = ghostEl && matrix && matrix.d,
					dx = ((touch.clientX - tapEvt.clientX) + fallbackOffset.x) / (scaleX ? scaleX : 1),
					dy = ((touch.clientY - tapEvt.clientY) + fallbackOffset.y) / (scaleY ? scaleY : 1),
R
RubaXa 已提交
977
					translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';
S
sp-kilobug 已提交
978

O
owen-m1 已提交
979

980
				// only set the status to dragging, when we are actually dragging
O
1.8.0  
owen-m1 已提交
981
				if (!Sortable.active && !awaitingDragStarted) {
R
RubaXa 已提交
982 983 984 985
					if (fallbackTolerance &&
						min(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY)) < fallbackTolerance
					) {
						return;
S
sp-kilobug 已提交
986
					}
O
1.8.0  
owen-m1 已提交
987
					this._onDragStart(evt, true);
988
				}
R
RubaXa 已提交
989

O
owen-m1 已提交
990 991 992
				this._handleAutoScroll(touch, true);


A
Alex Wild 已提交
993
				moved = true;
R
RubaXa 已提交
994
				touchEvt = touch;
R
RubaXa 已提交
995

O
1.8.0  
owen-m1 已提交
996

R
RubaXa 已提交
997 998 999 1000 1001
				_css(ghostEl, 'webkitTransform', translate3d);
				_css(ghostEl, 'mozTransform', translate3d);
				_css(ghostEl, 'msTransform', translate3d);
				_css(ghostEl, 'transform', translate3d);

O
owen-m1 已提交
1002
				evt.cancelable && evt.preventDefault();
R
RubaXa 已提交
1003 1004 1005
			}
		},

R
RubaXa 已提交
1006 1007
		_appendGhost: function () {
			if (!ghostEl) {
O
1.8.0  
owen-m1 已提交
1008
				var rect = _getRect(dragEl, this.options.fallbackOnBody ? document.body : rootEl, true),
R
RubaXa 已提交
1009
					css = _css(dragEl),
O
owen-m1 已提交
1010
					options = this.options;
R
RubaXa 已提交
1011

1012
				ghostEl = dragEl.cloneNode(true);
R
RubaXa 已提交
1013

R
RubaXa 已提交
1014 1015
				_toggleClass(ghostEl, options.ghostClass, false);
				_toggleClass(ghostEl, options.fallbackClass, true);
R
RubaXa 已提交
1016
				_toggleClass(ghostEl, options.dragClass, true);
C
ChiefORZ 已提交
1017

O
owen-m1 已提交
1018 1019 1020 1021
				_css(ghostEl, 'box-sizing', 'border-box');
				_css(ghostEl, 'margin', 0);
				_css(ghostEl, 'top', rect.top);
				_css(ghostEl, 'left', rect.left);
R
RubaXa 已提交
1022 1023
				_css(ghostEl, 'width', rect.width);
				_css(ghostEl, 'height', rect.height);
R
RubaXa 已提交
1024 1025 1026
				_css(ghostEl, 'opacity', '0.8');
				_css(ghostEl, 'position', 'fixed');
				_css(ghostEl, 'zIndex', '100000');
1027
				_css(ghostEl, 'pointerEvents', 'none');
R
RubaXa 已提交
1028

R
RubaXa 已提交
1029
				options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
1030 1031 1032
			}
		},

O
1.8.0  
owen-m1 已提交
1033
		_onDragStart: function (/**Event*/evt, /**boolean*/fallback) {
R
RubaXa 已提交
1034 1035
			var _this = this;
			var dataTransfer = evt.dataTransfer;
R
RubaXa 已提交
1036
			var options = _this.options;
1037

O
1.8.0  
owen-m1 已提交
1038 1039
			// Setup clone
			cloneEl = _clone(dragEl);
L
Lebedev Konstantin 已提交
1040

O
1.8.0  
owen-m1 已提交
1041 1042
			cloneEl.draggable = false;
			cloneEl.style['will-change'] = '';
1043

O
1.8.0  
owen-m1 已提交
1044
			this._hideClone();
L
Lebedev Konstantin 已提交
1045

O
1.8.0  
owen-m1 已提交
1046
			_toggleClass(cloneEl, _this.options.chosenClass, false);
1047

R
RubaXa 已提交
1048

O
1.8.0  
owen-m1 已提交
1049 1050 1051 1052 1053 1054 1055
			// #1143: IFrame support workaround
			_this._cloneId = _nextTick(function () {
				if (!_this.options.removeCloneOnHide) {
					rootEl.insertBefore(cloneEl, dragEl);
				}
				_dispatchEvent(_this, rootEl, 'clone', dragEl);
			});
O
owen-m1 已提交
1056

R
RubaXa 已提交
1057

O
1.8.0  
owen-m1 已提交
1058
			!fallback && _toggleClass(dragEl, options.dragClass, true);
R
RubaXa 已提交
1059

O
1.8.0  
owen-m1 已提交
1060 1061 1062
			// Set proper drop events
			if (fallback) {
				ignoreNextClick = true;
R
RubaXa 已提交
1063
				_this._loopId = setInterval(_this._emulateDragOver, 50);
O
1.8.0  
owen-m1 已提交
1064 1065 1066 1067 1068 1069
			} else {
				// Undo what was set in _prepareDragStart before drag started
				_off(document, 'mouseup', _this._onDrop);
				_off(document, 'touchend', _this._onDrop);
				_off(document, 'touchcancel', _this._onDrop);

R
RubaXa 已提交
1070 1071
				if (dataTransfer) {
					dataTransfer.effectAllowed = 'move';
R
RubaXa 已提交
1072
					options.setData && options.setData.call(_this, dataTransfer, dragEl);
R
RubaXa 已提交
1073
				}
R
RubaXa 已提交
1074

R
RubaXa 已提交
1075
				_on(document, 'drop', _this);
R
RubaXa 已提交
1076

O
owen-m1 已提交
1077 1078
				// #1276 fix:
				_css(dragEl, 'transform', 'translateZ(0)');
R
RubaXa 已提交
1079
			}
O
1.8.0  
owen-m1 已提交
1080 1081 1082 1083

			awaitingDragStarted = true;

			_this._dragStartId = _nextTick(_this._dragStarted.bind(_this, fallback));
O
owen-m1 已提交
1084
			_on(document, 'selectstart', _this);
R
RubaXa 已提交
1085 1086
		},

O
1.8.0  
owen-m1 已提交
1087
		// Returns true - if no further action is needed (either inserted or another condition)
R
RubaXa 已提交
1088
		_onDragOver: function (/**Event*/evt) {
R
RubaXa 已提交
1089
			var el = this.el,
O
1.8.0  
owen-m1 已提交
1090
				target = evt.target,
R
RubaXa 已提交
1091
				dragRect,
R
#930  
RubaXa 已提交
1092
				targetRect,
R
RubaXa 已提交
1093 1094 1095
				revert,
				options = this.options,
				group = options.group,
R
RubaXa 已提交
1096
				activeSortable = Sortable.active,
1097
				isOwner = (activeGroup === group),
O
1.8.0  
owen-m1 已提交
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
				canSort = options.sort,
				_this = this;

			if (_silent) return;

			// IE event order fix
			if (IE11OrLess && !evt.rootEl && !evt.artificialBubble && !_isTrueParentSortable(el, target)) {
				return;
			}

			// Return invocation when no further action is needed in another sortable
			function completed() {
				if (activeSortable) {
					// Set ghost class to new sortable's ghost class
					_toggleClass(dragEl, putSortable ? putSortable.options.ghostClass : activeSortable.options.ghostClass, false);
					_toggleClass(dragEl, options.ghostClass, true);
				}

				if (putSortable !== _this && _this !== Sortable.active) {
					putSortable = _this;
				} else if (_this === Sortable.active) {
					putSortable = null;
				}

O
owen-m1 已提交
1122

O
1.8.0  
owen-m1 已提交
1123 1124 1125 1126 1127 1128 1129 1130 1131
				// Null lastTarget if it is not inside a previously swapped element
				if ((target === dragEl && !dragEl.animated) || (target === el && !target.animated)) {
					lastTarget = null;
				}
				// no bubbling and not fallback
				if (!options.dragoverBubble && !evt.rootEl && target !== document) {
					_this._handleAutoScroll(evt);
					dragEl.parentNode[expando]._computeIsAligned(evt);
				}
O
owen-m1 已提交
1132

O
1.8.0  
owen-m1 已提交
1133 1134 1135 1136
				!options.dragoverBubble && evt.stopPropagation && evt.stopPropagation();

				return true;
			}
O
owen-m1 已提交
1137

O
1.8.0  
owen-m1 已提交
1138 1139 1140
			// Call when dragEl has been inserted
			function changed() {
				_dispatchEvent(_this, rootEl, 'change', target, el, rootEl, oldIndex, _index(dragEl, options.draggable), evt);
O
owen-m1 已提交
1141
			}
R
RubaXa 已提交
1142

O
1.8.0  
owen-m1 已提交
1143

R
RubaXa 已提交
1144
			if (evt.preventDefault !== void 0) {
O
owen-m1 已提交
1145
				evt.cancelable && evt.preventDefault();
R
RubaXa 已提交
1146
			}
R
RubaXa 已提交
1147

O
owen-m1 已提交
1148 1149 1150

			moved = true;

O
1.8.0  
owen-m1 已提交
1151
			target = _closest(target, options.draggable, el, true);
O
owen-m1 已提交
1152

O
1.8.0  
owen-m1 已提交
1153 1154 1155
			// target is dragEl or target is animated
			if (!!_closest(evt.target, null, dragEl, true) || target.animated) {
				return completed();
1156 1157
			}

O
1.8.0  
owen-m1 已提交
1158 1159
			if (target !== dragEl) {
				ignoreNextClick = false;
O
owen-m1 已提交
1160
			}
Y
yak 已提交
1161

L
#1009  
Lebedev Konstantin 已提交
1162
			if (activeSortable && !options.disabled &&
1163
				(isOwner
R
RubaXa 已提交
1164
					? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list
R
RubaXa 已提交
1165 1166
					: (
						putSortable === this ||
1167
						(
1168
							(this.lastPutMode = activeGroup.checkPull(this, activeSortable, dragEl, evt)) &&
1169 1170
							group.checkPut(this, activeSortable, dragEl, evt)
						)
R
RubaXa 已提交
1171
					)
O
owen-m1 已提交
1172
				)
R
RubaXa 已提交
1173
			) {
O
owen-m1 已提交
1174
				var axis = this._getDirection(evt, target);
1175

O
1.8.0  
owen-m1 已提交
1176
				dragRect = _getRect(dragEl);
R
RubaXa 已提交
1177

1178
				if (revert) {
1179
					this._hideClone();
R
RubaXa 已提交
1180
					parentEl = rootEl; // actualization
R
RubaXa 已提交
1181

O
1.8.0  
owen-m1 已提交
1182 1183 1184
					if (nextEl) {
						rootEl.insertBefore(dragEl, nextEl);
					} else {
1185 1186 1187
						rootEl.appendChild(dragEl);
					}

O
1.8.0  
owen-m1 已提交
1188
					return completed();
R
RubaXa 已提交
1189
				}
R
RubaXa 已提交
1190

R
RubaXa 已提交
1191
				if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
O
1.8.0  
owen-m1 已提交
1192
					_ghostIsLast(evt, axis, el) && !dragEl.animated
R
RubaXa 已提交
1193
				) {
1194 1195
					//assign target only if condition is true
					if (el.children.length !== 0 && el.children[0] !== ghostEl && el === evt.target) {
O
owen-m1 已提交
1196
						target = _lastChild(el);
1197 1198
					}

R
RubaXa 已提交
1199
					if (target) {
O
1.8.0  
owen-m1 已提交
1200
						targetRect = _getRect(target);
R
RubaXa 已提交
1201
					}
R
RubaXa 已提交
1202

1203 1204 1205 1206 1207
					if (isOwner) {
						activeSortable._hideClone();
					} else {
						activeSortable._showClone(this);
					}
R
RubaXa 已提交
1208

O
owen-m1 已提交
1209
					if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, !!target) !== false) {
O
1.8.0  
owen-m1 已提交
1210 1211 1212
						el.appendChild(dragEl);
						parentEl = el; // actualization
						realDragElRect = null;
R
RubaXa 已提交
1213

O
1.8.0  
owen-m1 已提交
1214
						changed();
R
RubaXa 已提交
1215 1216
						this._animate(dragRect, dragEl);
						target && this._animate(targetRect, target);
O
1.8.0  
owen-m1 已提交
1217
						return completed();
R
RubaXa 已提交
1218
					}
R
RubaXa 已提交
1219
				}
1220
				else if (target && target !== dragEl && target.parentNode === el) {
O
1.8.0  
owen-m1 已提交
1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231
					var direction = 0,
						targetBeforeFirstSwap,
						aligned = target.sortableMouseAligned,
						differentLevel = dragEl.parentNode !== el,
						scrolledPastTop = _isScrolledPast(target, axis === 'vertical' ? 'top' : 'left');

					if (lastTarget !== target) {
						lastMode = null;
						targetBeforeFirstSwap = _getRect(target)[axis === 'vertical' ? 'top' : 'left'];
						pastFirstInvertThresh = false;
					}
O
owen-m1 已提交
1232

O
1.8.0  
owen-m1 已提交
1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247
					// Reference: https://www.lucidchart.com/documents/view/10fa0e93-e362-4126-aca2-b709ee56bd8b/0
					if (
						_isElInRowColumn(dragEl, target, axis) && aligned ||
						differentLevel ||
						scrolledPastTop ||
						options.invertSwap ||
						lastMode === 'insert' ||
						// Needed, in the case that we are inside target and inserted because not aligned... aligned will stay false while inside
						// and lastMode will change to 'insert', but we must swap
						lastMode === 'swap'
					) {
						// New target that we will be inside
						if (lastMode !== 'swap') {
							isCircumstantialInvert = options.invertSwap || differentLevel || scrolling || scrolledPastTop;
						}
O
owen-m1 已提交
1248

O
1.8.0  
owen-m1 已提交
1249 1250 1251 1252 1253 1254 1255 1256 1257
						direction = _getSwapDirection(evt, target, axis,
							options.swapThreshold, options.invertedSwapThreshold == null ? options.swapThreshold : options.invertedSwapThreshold,
							isCircumstantialInvert,
							lastTarget === target);
						lastMode = 'swap';
					} else {
						// Insert at position
						direction = _getInsertDirection(target, options);
						lastMode = 'insert';
R
RubaXa 已提交
1258
					}
O
1.8.0  
owen-m1 已提交
1259
					if (direction === 0) return completed();
R
RubaXa 已提交
1260

O
1.8.0  
owen-m1 已提交
1261 1262
					realDragElRect = null;
					lastTarget = target;
O
owen-m1 已提交
1263 1264 1265

					lastDirection = direction;

O
1.8.0  
owen-m1 已提交
1266
					targetRect = _getRect(target);
R
RubaXa 已提交
1267

O
owen-m1 已提交
1268
					var nextSibling = target.nextElementSibling,
O
1.8.0  
owen-m1 已提交
1269
						after = false;
R
RubaXa 已提交
1270

O
owen-m1 已提交
1271
					after = direction === 1;
R
RubaXa 已提交
1272

1273 1274 1275
					var moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, after);

					if (moveVector !== false) {
R
RubaXa 已提交
1276 1277 1278
						if (moveVector === 1 || moveVector === -1) {
							after = (moveVector === 1);
						}
R
RubaXa 已提交
1279

1280 1281 1282
						_silent = true;
						setTimeout(_unsilent, 30);

1283 1284 1285 1286 1287
						if (isOwner) {
							activeSortable._hideClone();
						} else {
							activeSortable._showClone(this);
						}
R
RubaXa 已提交
1288

O
1.8.0  
owen-m1 已提交
1289 1290 1291 1292
						if (after && !nextSibling) {
							el.appendChild(dragEl);
						} else {
							target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
R
RubaXa 已提交
1293
						}
R
RubaXa 已提交
1294

R
RubaXa 已提交
1295 1296
						parentEl = dragEl.parentNode; // actualization

O
1.8.0  
owen-m1 已提交
1297 1298 1299 1300 1301 1302
						// must be done before animation
						if (targetBeforeFirstSwap !== undefined && !isCircumstantialInvert) {
							targetMoveDistance = abs(targetBeforeFirstSwap - _getRect(target)[axis === 'vertical' ? 'top' : 'left']);
						}
						changed();
						!differentLevel && this._animate(targetRect, target);
R
RubaXa 已提交
1303
						this._animate(dragRect, dragEl);
O
1.8.0  
owen-m1 已提交
1304 1305

						return completed();
R
RubaXa 已提交
1306
					}
R
RubaXa 已提交
1307
				}
O
1.8.0  
owen-m1 已提交
1308 1309 1310 1311 1312 1313 1314 1315

				if (el.contains(dragEl)) {
					return completed();
				}
			}

			if (IE11OrLess && !evt.rootEl) {
				_artificalBubble(el, evt, '_onDragOver');
R
RubaXa 已提交
1316
			}
O
1.8.0  
owen-m1 已提交
1317 1318

			return false;
R
RubaXa 已提交
1319 1320
		},

1321 1322 1323 1324
		_animate: function (prevRect, target) {
			var ms = this.options.animation;

			if (ms) {
O
1.8.0  
owen-m1 已提交
1325
				var currentRect = _getRect(target);
1326

O
owen-m1 已提交
1327 1328 1329 1330
				if (target === dragEl) {
					realDragElRect = currentRect;
				}

L
Lebedev Konstantin 已提交
1331
				if (prevRect.nodeType === 1) {
O
1.8.0  
owen-m1 已提交
1332
					prevRect = _getRect(prevRect);
L
Lebedev Konstantin 已提交
1333 1334
				}

O
owen-m1 已提交
1335
				// Check if actually moving position
O
1.8.0  
owen-m1 已提交
1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352
				if ((prevRect.left + prevRect.width / 2) !== (currentRect.left + currentRect.width / 2)
					|| (prevRect.top + prevRect.height / 2) !== (currentRect.top + currentRect.height / 2)
				) {
					var matrix = _matrix(this.el),
						scaleX = matrix && matrix.a,
						scaleY = matrix && matrix.d;

					_css(target, 'transition', 'none');
					_css(target, 'transform', 'translate3d('
						+ (prevRect.left - currentRect.left) / (scaleX ? scaleX : 1) + 'px,'
						+ (prevRect.top - currentRect.top) / (scaleY ? scaleY : 1) + 'px,0)'
					);

					forRepaintDummy = target.offsetWidth; // repaint
					_css(target, 'transition', 'transform ' + ms + 'ms' + (this.options.easing ? ' ' + this.options.easing : ''));
					_css(target, 'transform', 'translate3d(0,0,0)');
				}
1353

O
1.8.0  
owen-m1 已提交
1354
				(typeof target.animated === 'number') && clearTimeout(target.animated);
R
* anim  
RubaXa 已提交
1355
				target.animated = setTimeout(function () {
1356
					_css(target, 'transition', '');
1357
					_css(target, 'transform', '');
1358 1359 1360 1361 1362
					target.animated = false;
				}, ms);
			}
		},

1363
		_offUpEvents: function () {
1364 1365
			var ownerDocument = this.el.ownerDocument;

1366
			_off(document, 'touchmove', this._onTouchMove);
1367
			_off(document, 'pointermove', this._onTouchMove);
1368 1369
			_off(ownerDocument, 'mouseup', this._onDrop);
			_off(ownerDocument, 'touchend', this._onDrop);
1370
			_off(ownerDocument, 'pointerup', this._onDrop);
1371
			_off(ownerDocument, 'touchcancel', this._onDrop);
O
owen-m1 已提交
1372
			_off(document, 'selectstart', this);
1373
		},
R
RubaXa 已提交
1374

R
RubaXa 已提交
1375
		_onDrop: function (/**Event*/evt) {
1376 1377
			var el = this.el,
				options = this.options;
O
1.8.0  
owen-m1 已提交
1378
			awaitingDragStarted = false;
O
owen-m1 已提交
1379 1380 1381
			scrolling = false;
			isCircumstantialInvert = false;
			pastFirstInvertThresh = false;
R
RubaXa 已提交
1382

R
RubaXa 已提交
1383
			clearInterval(this._loopId);
F
Flame2057 已提交
1384

1385
			clearInterval(pointerElemChangedInterval);
F
Flame2057 已提交
1386
			_clearAutoScrolls();
1387
			_cancelThrottle();
F
Flame2057 已提交
1388

R
RubaXa 已提交
1389
			clearTimeout(this._dragStartTimer);
1390

R
RubaXa 已提交
1391 1392 1393
			_cancelNextTick(this._cloneId);
			_cancelNextTick(this._dragStartId);

R
RubaXa 已提交
1394
			// Unbind events
R
RubaXa 已提交
1395
			_off(document, 'mousemove', this._onTouchMove);
1396

F
Flame2057 已提交
1397

R
RubaXa 已提交
1398
			if (this.nativeDraggable) {
1399 1400
				_off(document, 'drop', this);
				_off(el, 'dragstart', this._onDragStart);
O
owen-m1 已提交
1401 1402
				_off(document, 'dragover', this._handleAutoScroll);
				_off(document, 'dragover', _checkAlignment);
1403
			}
R
RubaXa 已提交
1404

1405
			this._offUpEvents();
R
RubaXa 已提交
1406

R
RubaXa 已提交
1407
			if (evt) {
R
RubaXa 已提交
1408
				if (moved) {
O
owen-m1 已提交
1409
					evt.cancelable && evt.preventDefault();
C
ChiefORZ 已提交
1410 1411
					!options.dropBubble && evt.stopPropagation();
				}
R
RubaXa 已提交
1412

B
Billy Hardy 已提交
1413
				ghostEl && ghostEl.parentNode && ghostEl.parentNode.removeChild(ghostEl);
R
RubaXa 已提交
1414

O
owen-m1 已提交
1415
				if (rootEl === parentEl || (putSortable && putSortable.lastPutMode !== 'clone')) {
L
Lebedev Konstantin 已提交
1416
					// Remove clone
B
Billy Hardy 已提交
1417
					cloneEl && cloneEl.parentNode && cloneEl.parentNode.removeChild(cloneEl);
L
Lebedev Konstantin 已提交
1418 1419
				}

R
RubaXa 已提交
1420
				if (dragEl) {
R
RubaXa 已提交
1421
					if (this.nativeDraggable) {
1422 1423
						_off(dragEl, 'dragend', this);
					}
R
RubaXa 已提交
1424

1425
					_disableDraggable(dragEl);
L
Lebedev Konstantin 已提交
1426
					dragEl.style['will-change'] = '';
R
RubaXa 已提交
1427 1428

					// Remove class's
O
1.8.0  
owen-m1 已提交
1429
					_toggleClass(dragEl, putSortable ? putSortable.options.ghostClass : this.options.ghostClass, false);
R
RubaXa 已提交
1430
					_toggleClass(dragEl, this.options.chosenClass, false);
R
RubaXa 已提交
1431

1432
					// Drag stop event
S
Sam Wray 已提交
1433
					_dispatchEvent(this, rootEl, 'unchoose', dragEl, parentEl, rootEl, oldIndex, null, evt);
1434

1435
					if (rootEl !== parentEl) {
1436
						newIndex = _index(dragEl, options.draggable);
R
RubaXa 已提交
1437

1438
						if (newIndex >= 0) {
1439
							// Add event
S
Sam Wray 已提交
1440
							_dispatchEvent(null, parentEl, 'add', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
1441

1442
							// Remove event
S
Sam Wray 已提交
1443
							_dispatchEvent(this, rootEl, 'remove', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
N
n00dl3 已提交
1444

N
n00dl3 已提交
1445
							// drag from one list and drop into another
S
Sam Wray 已提交
1446 1447
							_dispatchEvent(null, parentEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
							_dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
1448
						}
O
owen-m1 已提交
1449 1450

						putSortable && putSortable.save();
R
RubaXa 已提交
1451
					}
R
RubaXa 已提交
1452 1453 1454
					else {
						if (dragEl.nextSibling !== nextEl) {
							// Get the index of the dragged element within its parent
1455
							newIndex = _index(dragEl, options.draggable);
1456 1457

							if (newIndex >= 0) {
1458
								// drag & drop within the same list
S
Sam Wray 已提交
1459 1460
								_dispatchEvent(this, rootEl, 'update', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
								_dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
1461
							}
R
RubaXa 已提交
1462
						}
R
RubaXa 已提交
1463
					}
1464

R
RubaXa 已提交
1465
					if (Sortable.active) {
1466
						/* jshint eqnull:true */
L
Lebedev Konstantin 已提交
1467
						if (newIndex == null || newIndex === -1) {
R
RubaXa 已提交
1468 1469 1470
							newIndex = oldIndex;
						}

S
Sam Wray 已提交
1471
						_dispatchEvent(this, rootEl, 'end', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
R
RubaXa 已提交
1472 1473 1474 1475

						// Save sorting
						this.save();
					}
R
RubaXa 已提交
1476
				}
R
RubaXa 已提交
1477

1478 1479 1480
			}
			this._nulling();
		},
R
RubaXa 已提交
1481

D
desmaisons_david 已提交
1482
		_nulling: function() {
L
Lebedev Konstantin 已提交
1483
			rootEl =
D
desmaisons_david 已提交
1484 1485 1486 1487 1488 1489
			dragEl =
			parentEl =
			ghostEl =
			nextEl =
			cloneEl =
			lastDownEl =
R
RubaXa 已提交
1490

D
desmaisons_david 已提交
1491 1492
			scrollEl =
			scrollParentEl =
O
owen-m1 已提交
1493 1494 1495 1496 1497
			autoScrolls.length =

			pointerElemChangedInterval =
			lastPointerElemX =
			lastPointerElemY =
C
ChiefORZ 已提交
1498

D
desmaisons_david 已提交
1499 1500
			tapEvt =
			touchEvt =
R
RubaXa 已提交
1501

D
desmaisons_david 已提交
1502 1503
			moved =
			newIndex =
O
owen-m1 已提交
1504 1505 1506 1507
			oldIndex =

			lastTarget =
			lastDirection =
R
RubaXa 已提交
1508

O
owen-m1 已提交
1509 1510
			forRepaintDummy =
			realDragElRect =
R
RubaXa 已提交
1511

D
desmaisons_david 已提交
1512 1513 1514
			putSortable =
			activeGroup =
			Sortable.active = null;
L
Lebedev Konstantin 已提交
1515 1516 1517 1518

			savedInputChecked.forEach(function (el) {
				el.checked = true;
			});
O
1.8.0  
owen-m1 已提交
1519

L
Lebedev Konstantin 已提交
1520
			savedInputChecked.length = 0;
1521
		},
R
RubaXa 已提交
1522

R
RubaXa 已提交
1523
		handleEvent: function (/**Event*/evt) {
1524 1525 1526 1527 1528 1529 1530
			switch (evt.type) {
				case 'drop':
				case 'dragend':
					this._onDrop(evt);
					break;

				case 'dragenter':
O
owen-m1 已提交
1531
				case 'dragover':
D
desmaisons_david 已提交
1532 1533 1534 1535
					if (dragEl) {
						this._onDragOver(evt);
						_globalDragOver(evt);
					}
1536
					break;
R
RubaXa 已提交
1537

1538 1539 1540
				case 'selectstart':
					evt.preventDefault();
					break;
R
RubaXa 已提交
1541
			}
R
RubaXa 已提交
1542 1543 1544
		},


1545 1546 1547 1548 1549 1550 1551 1552 1553
		/**
		 * Serializes the item into an array of string.
		 * @returns {String[]}
		 */
		toArray: function () {
			var order = [],
				el,
				children = this.el.children,
				i = 0,
R
RubaXa 已提交
1554 1555
				n = children.length,
				options = this.options;
1556 1557 1558

			for (; i < n; i++) {
				el = children[i];
O
owen-m1 已提交
1559
				if (_closest(el, options.draggable, this.el, false)) {
R
RubaXa 已提交
1560
					order.push(el.getAttribute(options.dataIdAttr) || _generateId(el));
R
RubaXa 已提交
1561
				}
1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572
			}

			return order;
		},


		/**
		 * Sorts the elements according to the array.
		 * @param  {String[]}  order  order of the items
		 */
		sort: function (order) {
R
RubaXa 已提交
1573
			var items = {}, rootEl = this.el;
1574 1575

			this.toArray().forEach(function (id, i) {
R
RubaXa 已提交
1576 1577
				var el = rootEl.children[i];

O
owen-m1 已提交
1578
				if (_closest(el, this.options.draggable, rootEl, false)) {
R
RubaXa 已提交
1579 1580 1581
					items[id] = el;
				}
			}, this);
1582 1583 1584

			order.forEach(function (id) {
				if (items[id]) {
R
RubaXa 已提交
1585 1586
					rootEl.removeChild(items[id]);
					rootEl.appendChild(items[id]);
1587 1588 1589 1590 1591
				}
			});
		},


R
RubaXa 已提交
1592 1593 1594 1595 1596
		/**
		 * Save the current sorting
		 */
		save: function () {
			var store = this.options.store;
O
owen-m1 已提交
1597
			store && store.set && store.set(this);
R
RubaXa 已提交
1598 1599 1600
		},


1601 1602 1603 1604 1605 1606 1607
		/**
		 * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
		 * @param   {HTMLElement}  el
		 * @param   {String}       [selector]  default: `options.draggable`
		 * @returns {HTMLElement|null}
		 */
		closest: function (el, selector) {
O
owen-m1 已提交
1608
			return _closest(el, selector || this.options.draggable, this.el, false);
1609 1610 1611
		},


1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624
		/**
		 * Set/get option
		 * @param   {string} name
		 * @param   {*}      [value]
		 * @returns {*}
		 */
		option: function (name, value) {
			var options = this.options;

			if (value === void 0) {
				return options[name];
			} else {
				options[name] = value;
R
RubaXa 已提交
1625 1626 1627 1628

				if (name === 'group') {
					_prepareGroup(options);
				}
1629 1630 1631 1632
			}
		},


1633 1634 1635 1636
		/**
		 * Destroy
		 */
		destroy: function () {
1637
			var el = this.el;
R
RubaXa 已提交
1638

1639
			el[expando] = null;
1640

R
RubaXa 已提交
1641 1642
			_off(el, 'mousedown', this._onTapStart);
			_off(el, 'touchstart', this._onTapStart);
1643
			_off(el, 'pointerdown', this._onTapStart);
R
RubaXa 已提交
1644

R
RubaXa 已提交
1645
			if (this.nativeDraggable) {
1646 1647 1648
				_off(el, 'dragover', this);
				_off(el, 'dragenter', this);
			}
1649
			// Remove draggable attributes
R
RubaXa 已提交
1650
			Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) {
1651 1652 1653
				el.removeAttribute('draggable');
			});

R
RubaXa 已提交
1654 1655
			this._onDrop();

O
1.8.0  
owen-m1 已提交
1656 1657
			sortables.splice(sortables.indexOf(this.el), 1);

1658
			this.el = el = null;
1659
		},
R
RubaXa 已提交
1660

1661 1662 1663 1664
		_hideClone: function() {
			if (!cloneEl.cloneHidden) {
				_css(cloneEl, 'display', 'none');
				cloneEl.cloneHidden = true;
O
1.8.0  
owen-m1 已提交
1665 1666 1667
				if (cloneEl.parentNode && this.options.removeCloneOnHide) {
					cloneEl.parentNode.removeChild(cloneEl);
				}
1668 1669
			}
		},
1670

1671
		_showClone: function(putSortable) {
O
owen-m1 已提交
1672 1673 1674 1675
			if (putSortable.lastPutMode !== 'clone') {
				this._hideClone();
				return;
			}
1676

1677 1678
			if (cloneEl.cloneHidden) {
				// show clone at dragEl or original position
O
owen-m1 已提交
1679 1680 1681 1682 1683 1684 1685
				if (rootEl.contains(dragEl) && !this.options.group.revertClone) {
					rootEl.insertBefore(cloneEl, dragEl);
				} else if (nextEl) {
					rootEl.insertBefore(cloneEl, nextEl);
				} else {
					rootEl.appendChild(cloneEl);
				}
L
Lebedev Konstantin 已提交
1686

1687 1688
				if (this.options.group.revertClone) {
					this._animate(dragEl, cloneEl);
L
Lebedev Konstantin 已提交
1689
				}
1690 1691
				_css(cloneEl, 'display', '');
				cloneEl.cloneHidden = false;
L
Lebedev Konstantin 已提交
1692
			}
R
RubaXa 已提交
1693
		}
1694
	};
R
RubaXa 已提交
1695

O
owen-m1 已提交
1696
	function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx, includeCTX) {
R
RubaXa 已提交
1697
		if (el) {
R
RubaXa 已提交
1698 1699 1700
			ctx = ctx || document;

			do {
1701 1702 1703 1704
				if (
					selector != null &&
					(
						selector[0] === '>' && el.parentNode === ctx && _matches(el, selector.substring(1)) ||
O
owen-m1 已提交
1705 1706 1707
						_matches(el, selector)
					) ||
					includeCTX && el === ctx
1708
				) {
R
RubaXa 已提交
1709
					return el;
R
RubaXa 已提交
1710
				}
O
owen-m1 已提交
1711 1712

				if (el === ctx) break;
R
#930  
RubaXa 已提交
1713 1714
				/* jshint boss:true */
			} while (el = _getParentOrHost(el));
R
RubaXa 已提交
1715 1716
		}

R
RubaXa 已提交
1717
		return null;
R
RubaXa 已提交
1718 1719 1720
	}


N
Nikita SVIRIDENKO 已提交
1721
	function _getParentOrHost(el) {
1722 1723 1724
		return (el.host && el !== document && el.host.nodeType)
			? el.host
			: el.parentNode;
N
Nikita SVIRIDENKO 已提交
1725
	}
1726 1727


1728
	function _globalDragOver(/**Event*/evt) {
1729
		if (evt.dataTransfer) {
O
owen-m1 已提交
1730
			evt.dataTransfer.dropEffect = 'move';
1731
		}
O
owen-m1 已提交
1732
		evt.cancelable && evt.preventDefault();
R
RubaXa 已提交
1733 1734 1735
	}


R
RubaXa 已提交
1736
	function _on(el, event, fn) {
1737
		el.addEventListener(event, fn, captureMode);
R
RubaXa 已提交
1738 1739 1740
	}


R
RubaXa 已提交
1741
	function _off(el, event, fn) {
1742
		el.removeEventListener(event, fn, captureMode);
R
RubaXa 已提交
1743 1744 1745
	}


R
RubaXa 已提交
1746
	function _toggleClass(el, name, state) {
O
owen-m1 已提交
1747
		if (el && name) {
R
RubaXa 已提交
1748
			if (el.classList) {
R
RubaXa 已提交
1749 1750 1751
				el.classList[state ? 'add' : 'remove'](name);
			}
			else {
1752 1753
				var className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' ');
				el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' ');
R
RubaXa 已提交
1754 1755 1756 1757 1758
			}
		}
	}


R
RubaXa 已提交
1759
	function _css(el, prop, val) {
R
RubaXa 已提交
1760 1761
		var style = el && el.style;

R
RubaXa 已提交
1762 1763 1764
		if (style) {
			if (val === void 0) {
				if (document.defaultView && document.defaultView.getComputedStyle) {
R
RubaXa 已提交
1765 1766
					val = document.defaultView.getComputedStyle(el, '');
				}
R
RubaXa 已提交
1767 1768
				else if (el.currentStyle) {
					val = el.currentStyle;
R
RubaXa 已提交
1769
				}
R
RubaXa 已提交
1770 1771 1772 1773

				return prop === void 0 ? val : val[prop];
			}
			else {
O
1.8.0  
owen-m1 已提交
1774
				if (!(prop in style) && prop.indexOf('webkit') === -1) {
R
RubaXa 已提交
1775 1776 1777 1778
					prop = '-webkit-' + prop;
				}

				style[prop] = val + (typeof val === 'string' ? '' : 'px');
R
RubaXa 已提交
1779 1780 1781 1782
			}
		}
	}

O
1.8.0  
owen-m1 已提交
1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802
	function _matrix(el) {
		var appliedTransforms = '';
		do {
			var transform = _css(el, 'transform');

			if (transform && transform !== 'none') {
				appliedTransforms = transform + ' ' + appliedTransforms;
			}
			/* jshint boss:true */
		} while (el = el.parentNode);

		if (window.DOMMatrix) {
			return new DOMMatrix(appliedTransforms);
		} else if (window.WebKitCSSMatrix) {
			return new WebKitCSSMatrix(appliedTransforms);
		} else if (window.CSSMatrix) {
			return new CSSMatrix(appliedTransforms);
		}
	}

R
RubaXa 已提交
1803

R
RubaXa 已提交
1804 1805
	function _find(ctx, tagName, iterator) {
		if (ctx) {
R
RubaXa 已提交
1806
			var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length;
R
RubaXa 已提交
1807

R
RubaXa 已提交
1808 1809
			if (iterator) {
				for (; i < n; i++) {
R
RubaXa 已提交
1810 1811 1812
					iterator(list[i], i);
				}
			}
R
RubaXa 已提交
1813

R
RubaXa 已提交
1814
			return list;
R
RubaXa 已提交
1815
		}
R
RubaXa 已提交
1816 1817

		return [];
R
RubaXa 已提交
1818 1819 1820
	}


R
RubaXa 已提交
1821

S
Sam Wray 已提交
1822
	function _dispatchEvent(sortable, rootEl, name, targetEl, toEl, fromEl, startIndex, newIndex, originalEvt) {
1823
		sortable = (sortable || rootEl[expando]);
O
owen-m1 已提交
1824
		var evt,
1825
			options = sortable.options,
R
RubaXa 已提交
1826
			onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1);
O
owen-m1 已提交
1827
		// Support for new CustomEvent feature
O
1.8.0  
owen-m1 已提交
1828
		if (window.CustomEvent && !IE11OrLess && !Edge) {
O
owen-m1 已提交
1829 1830 1831 1832 1833 1834 1835 1836
			evt = new CustomEvent(name, {
				bubbles: true,
				cancelable: true
			});
		} else {
			evt = document.createEvent('Event');
			evt.initEvent(name, true, true);
		}
R
RubaXa 已提交
1837

J
Joey Becker 已提交
1838
		evt.to = toEl || rootEl;
R
RubaXa 已提交
1839 1840 1841 1842 1843 1844 1845
		evt.from = fromEl || rootEl;
		evt.item = targetEl || rootEl;
		evt.clone = cloneEl;

		evt.oldIndex = startIndex;
		evt.newIndex = newIndex;

1846 1847
		evt.originalEvent = originalEvt;

O
1.8.0  
owen-m1 已提交
1848 1849 1850
		if (rootEl) {
			rootEl.dispatchEvent(evt);
	        }
R
RubaXa 已提交
1851

R
RubaXa 已提交
1852 1853 1854 1855 1856 1857
		if (options[onName]) {
			options[onName].call(sortable, evt);
		}
	}


1858
	function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvt, willInsertAfter) {
R
RubaXa 已提交
1859 1860 1861 1862
		var evt,
			sortable = fromEl[expando],
			onMoveFn = sortable.options.onMove,
			retVal;
O
owen-m1 已提交
1863
		// Support for new CustomEvent feature
O
1.8.0  
owen-m1 已提交
1864
		if (window.CustomEvent && !IE11OrLess && !Edge) {
O
owen-m1 已提交
1865 1866 1867 1868 1869 1870 1871 1872
			evt = new CustomEvent('move', {
				bubbles: true,
				cancelable: true
			});
		} else {
			evt = document.createEvent('Event');
			evt.initEvent('move', true, true);
		}
R
RubaXa 已提交
1873

C
ChiefORZ 已提交
1874 1875 1876 1877 1878
		evt.to = toEl;
		evt.from = fromEl;
		evt.dragged = dragEl;
		evt.draggedRect = dragRect;
		evt.related = targetEl || toEl;
O
1.8.0  
owen-m1 已提交
1879
		evt.relatedRect = targetRect || _getRect(toEl);
1880
		evt.willInsertAfter = willInsertAfter;
R
RubaXa 已提交
1881

1882 1883
		evt.originalEvent = originalEvt;

C
ChiefORZ 已提交
1884
		fromEl.dispatchEvent(evt);
R
RubaXa 已提交
1885

C
ChiefORZ 已提交
1886
		if (onMoveFn) {
1887
			retVal = onMoveFn.call(sortable, evt, originalEvt);
R
RubaXa 已提交
1888 1889
		}

R
RubaXa 已提交
1890
		return retVal;
R
RubaXa 已提交
1891 1892
	}

R
RubaXa 已提交
1893
	function _disableDraggable(el) {
R
RubaXa 已提交
1894
		el.draggable = false;
R
RubaXa 已提交
1895 1896
	}

R
RubaXa 已提交
1897
	function _unsilent() {
R
RubaXa 已提交
1898 1899 1900
		_silent = false;
	}

O
1.8.0  
owen-m1 已提交
1901 1902 1903 1904 1905 1906 1907 1908
	/**
	 * Gets nth child of el, ignoring hidden children, sortable's elements (does not ignore clone if it's visible)
	 * and non-draggable elements
	 * @param  {HTMLElement} el       The parent element
	 * @param  {Number} childNum      The index of the child
	 * @param  {Object} options       Parent Sortable's options
	 * @return {HTMLElement}          The child at index childNum, or null if not found
	 */
O
owen-m1 已提交
1909 1910 1911
	function _getChild(el, childNum, options) {
		var currentChild = 0,
			i = 0,
O
1.8.0  
owen-m1 已提交
1912 1913
			children = el.children;

O
owen-m1 已提交
1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931
		while (i < children.length) {
			if (
				children[i].style.display !== 'none' &&
				children[i] !== ghostEl &&
				children[i] !== dragEl &&
				_closest(children[i], options.draggable, el, false)
			) {
				if (currentChild === childNum) {
					return children[i];
				}
				currentChild++;
			}

			i++;
		}
		return null;
	}

O
1.8.0  
owen-m1 已提交
1932
	/**
1933
	 * Gets the last child in the el, ignoring ghostEl or invisible elements (clones)
O
1.8.0  
owen-m1 已提交
1934 1935 1936
	 * @param  {HTMLElement} el       Parent element
	 * @return {HTMLElement}          The last child, ignoring ghostEl
	 */
O
owen-m1 已提交
1937 1938 1939
	function _lastChild(el) {
		var last = el.lastElementChild;

1940 1941 1942 1943
		while (last === ghostEl || last.style.display === 'none') {
			last = last.previousElementSibling;

			if (!last) break;
O
owen-m1 已提交
1944 1945 1946 1947 1948 1949
		}

		return last || null;
	}

	function _ghostIsLast(evt, axis, el) {
O
1.8.0  
owen-m1 已提交
1950
		var elRect = _getRect(_lastChild(el)),
O
owen-m1 已提交
1951 1952 1953 1954
			mouseOnAxis = axis === 'vertical' ? evt.clientY : evt.clientX,
			mouseOnOppAxis = axis === 'vertical' ? evt.clientX : evt.clientY,
			targetS2 = axis === 'vertical' ? elRect.bottom : elRect.right,
			targetS1Opp = axis === 'vertical' ? elRect.left : elRect.top,
1955 1956
			targetS2Opp = axis === 'vertical' ? elRect.right : elRect.bottom,
			spacer = 10;
O
1.8.0  
owen-m1 已提交
1957

O
owen-m1 已提交
1958
		return (
1959 1960 1961
			axis === 'vertical' ?
				(mouseOnOppAxis > targetS2Opp + spacer || mouseOnOppAxis <= targetS2Opp && mouseOnAxis > targetS2 && mouseOnOppAxis >= targetS1Opp) :
				(mouseOnAxis > targetS2 && mouseOnOppAxis > targetS1Opp || mouseOnAxis <= targetS2 && mouseOnOppAxis > targetS2Opp + spacer)
O
owen-m1 已提交
1962 1963 1964
		);
	}

O
1.8.0  
owen-m1 已提交
1965 1966
	function _getSwapDirection(evt, target, axis, swapThreshold, invertedSwapThreshold, invertSwap, isLastTarget) {
		var targetRect = _getRect(target),
O
owen-m1 已提交
1967 1968 1969 1970
			mouseOnAxis = axis === 'vertical' ? evt.clientY : evt.clientX,
			targetLength = axis === 'vertical' ? targetRect.height : targetRect.width,
			targetS1 = axis === 'vertical' ? targetRect.top : targetRect.left,
			targetS2 = axis === 'vertical' ? targetRect.bottom : targetRect.right,
O
1.8.0  
owen-m1 已提交
1971 1972 1973
			dragRect = _getRect(dragEl),
			invert = false;

O
owen-m1 已提交
1974 1975

		if (!invertSwap) {
O
1.8.0  
owen-m1 已提交
1976 1977
			// Never invert or create dragEl shadow when target movemenet causes mouse to move past the end of regular swapThreshold
			if (isLastTarget && targetMoveDistance < targetLength * swapThreshold) { // multiplied only by swapThreshold because mouse will already be inside target by (1 - threshold) * targetLength / 2
O
owen-m1 已提交
1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995
				// check if past first invert threshold on side opposite of lastDirection
				if (!pastFirstInvertThresh &&
					(lastDirection === 1 ?
						(
							mouseOnAxis > targetS1 + targetLength * invertedSwapThreshold / 2
						) :
						(
							mouseOnAxis < targetS2 - targetLength * invertedSwapThreshold / 2
						)
					)
				)
				{
					// past first invert threshold, do not restrict inverted threshold to dragEl shadow
					pastFirstInvertThresh = true;
				}

				if (!pastFirstInvertThresh) {
					var dragS1 = axis === 'vertical' ? dragRect.top : dragRect.left,
O
1.8.0  
owen-m1 已提交
1996 1997
						dragS2 = axis === 'vertical' ? dragRect.bottom : dragRect.right;
					// dragEl shadow (target move distance shadow)
O
owen-m1 已提交
1998 1999 2000
					if (
						lastDirection === 1 ?
						(
O
1.8.0  
owen-m1 已提交
2001
							mouseOnAxis < targetS1 + targetMoveDistance // over dragEl shadow
O
owen-m1 已提交
2002 2003
						) :
						(
O
1.8.0  
owen-m1 已提交
2004
							mouseOnAxis > targetS2 - targetMoveDistance
O
owen-m1 已提交
2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022
						)
					)
					{
						return lastDirection * -1;
					}
				} else {
					invert = true;
				}
			} else {
				// Regular
				if (
					mouseOnAxis > targetS1 + (targetLength * (1 - swapThreshold) / 2) &&
					mouseOnAxis < targetS2 - (targetLength * (1 - swapThreshold) / 2)
				) {
					return ((mouseOnAxis > targetS1 + targetLength / 2) ? -1 : 1);
				}
			}
		}
R
RubaXa 已提交
2023

O
owen-m1 已提交
2024
		invert = invert || invertSwap;
R
RubaXa 已提交
2025

O
owen-m1 已提交
2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037
		if (invert) {
			// Invert of regular
			if (
				mouseOnAxis < targetS1 + (targetLength * invertedSwapThreshold / 2) ||
				mouseOnAxis > targetS2 - (targetLength * invertedSwapThreshold / 2)
			)
			{
				return ((mouseOnAxis > targetS1 + targetLength / 2) ? 1 : -1);
			}
		}

		return 0;
R
RubaXa 已提交
2038 2039
	}

O
1.8.0  
owen-m1 已提交
2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057
	/**
	 * Gets the direction dragEl must be swapped relative to target in order to make it
	 * seem that dragEl has been "inserted" into that element's position
	 * @param  {HTMLElement} target       The target whose position dragEl is being inserted at
	 * @param  {Object} options           options of the parent sortable
	 * @return {Number}                   Direction dragEl must be swapped
	 */
	function _getInsertDirection(target, options) {
		var dragElIndex = _index(dragEl, options.draggable),
			targetIndex = _index(target, options.draggable);

		if (dragElIndex < targetIndex) {
			return 1;
		} else {
			return -1;
		}
	}

R
RubaXa 已提交
2058

2059 2060 2061 2062 2063 2064 2065
	/**
	 * Generate id
	 * @param   {HTMLElement} el
	 * @returns {String}
	 * @private
	 */
	function _generateId(el) {
R
RubaXa 已提交
2066
		var str = el.tagName + el.className + el.src + el.href + el.textContent,
2067
			i = str.length,
R
RubaXa 已提交
2068
			sum = 0;
2069

2070 2071 2072
		while (i--) {
			sum += str.charCodeAt(i);
		}
2073

2074 2075 2076
		return sum.toString(36);
	}

2077
	/**
2078 2079
	 * Returns the index of an element within its parent for a selected set of
	 * elements
2080
	 * @param  {HTMLElement} el
2081
	 * @param  {selector} selector
2082
	 * @return {number}
2083
	 */
2084
	function _index(el, selector) {
2085 2086
		var index = 0;

2087 2088 2089
		if (!el || !el.parentNode) {
			return -1;
		}
2090

2091
		while (el && (el = el.previousElementSibling)) {
O
1.8.0  
owen-m1 已提交
2092
			if ((el.nodeName.toUpperCase() !== 'TEMPLATE') && el !== cloneEl) {
2093 2094
				index++;
			}
2095
		}
2096

2097 2098
		return index;
	}
R
RubaXa 已提交
2099

2100
	function _matches(/**HTMLElement*/el, /**String*/selector) {
2101
		if (el) {
S
Sam Wray 已提交
2102 2103
			try {
				if (el.matches) {
2104
					return el.matches(selector);
S
Sam Wray 已提交
2105
				} else if (el.msMatchesSelector) {
2106
					return el.msMatchesSelector(selector);
O
1.8.0  
owen-m1 已提交
2107 2108
				} else if (el.webkitMatchesSelector) {
					return el.webkitMatchesSelector(selector);
2109
				}
S
Sam Wray 已提交
2110 2111
			} catch(_) {
				return false;
2112
			}
2113
		}
2114 2115

		return false;
2116 2117
	}

2118
	var _throttleTimeout;
R
RubaXa 已提交
2119 2120
	function _throttle(callback, ms) {
		return function () {
2121 2122
			if (!_throttleTimeout) {
				var args = arguments,
O
1.8.0  
owen-m1 已提交
2123
					_this = this;
R
RubaXa 已提交
2124

2125
				_throttleTimeout = setTimeout(function () {
R
RubaXa 已提交
2126 2127 2128 2129 2130 2131
					if (args.length === 1) {
						callback.call(_this, args[0]);
					} else {
						callback.apply(_this, args);
					}

2132
					_throttleTimeout = void 0;
R
RubaXa 已提交
2133 2134 2135 2136 2137
				}, ms);
			}
		};
	}

2138 2139 2140 2141 2142
	function _cancelThrottle() {
		clearTimeout(_throttleTimeout);
		_throttleTimeout = void 0;
	}

2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154
	function _extend(dst, src) {
		if (dst && src) {
			for (var key in src) {
				if (src.hasOwnProperty(key)) {
					dst[key] = src[key];
				}
			}
		}

		return dst;
	}

2155
	function _clone(el) {
C
camargo 已提交
2156
		if (Polymer && Polymer.dom) {
C
camargo 已提交
2157
			return Polymer.dom(el).cloneNode(true);
C
camargo 已提交
2158 2159
		}
		else if ($) {
C
camargo 已提交
2160
			return $(el).clone(true)[0];
C
camargo 已提交
2161 2162
		}
		else {
C
camargo 已提交
2163
			return el.cloneNode(true);
C
camargo 已提交
2164
		}
2165 2166
	}

L
Lebedev Konstantin 已提交
2167
	function _saveInputCheckedState(root) {
2168
		savedInputChecked.length = 0;
2169

L
Lebedev Konstantin 已提交
2170 2171 2172 2173 2174 2175 2176 2177 2178
		var inputs = root.getElementsByTagName('input');
		var idx = inputs.length;

		while (idx--) {
			var el = inputs[idx];
			el.checked && savedInputChecked.push(el);
		}
	}

R
RubaXa 已提交
2179
	function _nextTick(fn) {
R
RubaXa 已提交
2180 2181 2182 2183 2184
		return setTimeout(fn, 0);
	}

	function _cancelNextTick(id) {
		return clearTimeout(id);
R
RubaXa 已提交
2185 2186
	}

O
1.8.0  
owen-m1 已提交
2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305

	/**
	 * Returns the "bounding client rect" of given element
	 * @param  {HTMLElement} el                The element whose boundingClientRect is wanted
	 * @param  {[HTMLElement]} container       the parent the element will be placed in
	 * @param  {[Boolean]} adjustForTransform  Whether the rect should compensate for parent's transform
	 * (used for fixed positioning on el)
	 * @return {Object}                        The boundingClientRect of el
	 */
	function _getRect(el, container, adjustForTransform) {
		if (!el.getBoundingClientRect && el !== win) return;

		var elRect,
			top,
			left,
			bottom,
			right,
			height,
			width;

		if (el !== win) {
			elRect = el.getBoundingClientRect();
			top = elRect.top;
			left = elRect.left;
			bottom = elRect.bottom;
			right = elRect.right;
			height = elRect.height;
			width = elRect.width;
		} else {
			top = 0;
			left = 0;
			bottom = window.innerHeight;
			right = window.innerWidth;
			height = window.innerHeight;
			width = window.innerWidth;
		}

		if (adjustForTransform && el !== win) {
			// Adjust for translate()
			container = container || el.parentNode;

			// solves #1123 (see: https://stackoverflow.com/a/37953806/6088312)
			// Not needed on <= IE11
			if (!IE11OrLess) {
				do {
					if (container && container.getBoundingClientRect && _css(container, 'transform') !== 'none') {
						var containerRect = container.getBoundingClientRect();

						// Set relative to edges of padding box of container
						top -= containerRect.top + parseInt(_css(container, 'border-top-width'));
						left -= containerRect.left + parseInt(_css(container, 'border-left-width'));
						bottom = top + elRect.height;
						right = left + elRect.width;

						break;
					}
					/* jshint boss:true */
				} while (container = container.parentNode);
			}

			// Adjust for scale()
			var matrix = _matrix(el),
				scaleX = matrix && matrix.a,
				scaleY = matrix && matrix.d;

			if (matrix) {
				top /= scaleY;
				left /= scaleX;

				width /= scaleX;
				height /= scaleY;

				bottom = top + height;
				right = left + width;
			}
		}

		return {
			top: top,
			left: left,
			bottom: bottom,
			right: right,
			width: width,
			height: height
		};
	}


	/**
	 * Checks if a side of an element is scrolled past a side of it's parents
	 * @param  {HTMLElement}  el       The element who's side being scrolled out of view is in question
	 * @param  {String}       side     Side of the element in question ('top', 'left', 'right', 'bottom')
	 * @return {Boolean}               Whether the element is overflowing the viewport on the given side of it's parent
	 */
	function _isScrolledPast(el, side) {
		var parent = _getParentAutoScrollElement(parent, true),
			elSide = _getRect(el)[side];

		/* jshint boss:true */
		while (parent) {
			var parentSide = _getRect(parent)[side],
				visible;

			if (side === 'top' || side === 'left') {
				visible = elSide >= parentSide;
			} else {
				visible = elSide <= parentSide;
			}

			if (!visible) return true;

			if (parent === win) break;

			parent = _getParentAutoScrollElement(parent, false);
		}

		return false;
	}

O
1.8.1  
owen-m1 已提交
2306 2307 2308 2309 2310 2311 2312
	// Fixed #973:
	_on(document, 'touchmove', function(evt) {
		if ((Sortable.active || awaitingDragStarted) && evt.cancelable) {
			evt.preventDefault();
		}
	});

L
Lebedev Konstantin 已提交
2313

R
RubaXa 已提交
2314 2315 2316 2317 2318 2319
	// Export utils
	Sortable.utils = {
		on: _on,
		off: _off,
		css: _css,
		find: _find,
R
RubaXa 已提交
2320
		is: function (el, selector) {
O
owen-m1 已提交
2321
			return !!_closest(el, selector, el, false);
R
RubaXa 已提交
2322
		},
2323
		extend: _extend,
R
RubaXa 已提交
2324
		throttle: _throttle,
R
RubaXa 已提交
2325
		closest: _closest,
2326
		toggleClass: _toggleClass,
2327
		clone: _clone,
R
RubaXa 已提交
2328
		index: _index,
R
RubaXa 已提交
2329
		nextTick: _nextTick,
O
owen-m1 已提交
2330 2331 2332
		cancelNextTick: _cancelNextTick,
		detectDirection: _detectDirection,
		getChild: _getChild
R
RubaXa 已提交
2333 2334 2335
	};


2336 2337 2338 2339 2340 2341
	/**
	 * Create sortable instance
	 * @param {HTMLElement}  el
	 * @param {Object}      [options]
	 */
	Sortable.create = function (el, options) {
R
RubaXa 已提交
2342
		return new Sortable(el, options);
2343
	};
R
RubaXa 已提交
2344

R
RubaXa 已提交
2345 2346

	// Export
O
1.8.2  
owen-m1 已提交
2347
	Sortable.version = '1.8.2';
2348
	return Sortable;
O
1.8.0  
owen-m1 已提交
2349
});