Sortable.js 36.3 KB
Newer Older
R
RubaXa 已提交
1 2 3 4 5 6
/**!
 * Sortable
 * @author	RubaXa   <trash@rubaxa.org>
 * @license MIT
 */

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

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

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

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

37 38
		scrollEl,
		scrollParentEl,
39
		scrollCustomFn,
40

R
RubaXa 已提交
41 42
		lastEl,
		lastCSS,
S
sp-kilobug 已提交
43
		lastParentCSS,
R
RubaXa 已提交
44

R
RubaXa 已提交
45 46 47
		oldIndex,
		newIndex,

R
RubaXa 已提交
48
		activeGroup,
R
RubaXa 已提交
49 50
		putSortable,

R
RubaXa 已提交
51
		autoScroll = {},
R
RubaXa 已提交
52

R
RubaXa 已提交
53 54
		tapEvt,
		touchEvt,
R
RubaXa 已提交
55

C
ChiefORZ 已提交
56 57
		moved,

R
RubaXa 已提交
58
		/** @const */
59 60
		R_SPACE = /\s+/g,
		R_FLOAT = /left|right|inline/,
R
RubaXa 已提交
61

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

R
RubaXa 已提交
64 65 66
		win = window,
		document = win.document,
		parseInt = win.parseInt,
R
RubaXa 已提交
67
		setTimeout = win.setTimeout,
R
RubaXa 已提交
68

R
* UPD  
RubaXa 已提交
69
		$ = win.jQuery || win.Zepto,
70 71
		Polymer = win.Polymer,

L
Lebedev Konstantin 已提交
72
		captureMode = false,
R
RubaXa 已提交
73
		passiveMode = false,
74

R
RubaXa 已提交
75
		supportDraggable = ('draggable' in document.createElement('div')),
76
		supportCssPointerEvents = (function (el) {
77
			// false when IE11
78
			if (!!navigator.userAgent.match(/(?:Trident.*rv[ :]?11\.|msie)/i)) {
79 80
				return false;
			}
81 82 83 84
			el = document.createElement('x');
			el.style.cssText = 'pointer-events:auto';
			return el.style.pointerEvents === 'auto';
		})(),
R
RubaXa 已提交
85

R
RubaXa 已提交
86
		_silent = false,
R
RubaXa 已提交
87

R
RubaXa 已提交
88
		abs = Math.abs,
R
RubaXa 已提交
89
		min = Math.min,
R
RubaXa 已提交
90

L
Lebedev Konstantin 已提交
91
		savedInputChecked = [],
92 93
		touchDragOverListeners = [],

R
RubaXa 已提交
94 95
		alwaysFalse = function () { return false },

96 97 98
		_autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl) {
			// Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521
			if (rootEl && options.scroll) {
J
Jang Jun 已提交
99 100
				var _this = rootEl[expando],
					el,
101 102 103 104 105 106 107 108 109 110 111
					rect,
					sens = options.scrollSensitivity,
					speed = options.scrollSpeed,

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

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

					vx,
112 113 114 115
					vy,

					scrollOffsetX,
					scrollOffsetY
D
desmaisons_david 已提交
116
				;
117 118 119 120 121

				// Delect scrollEl
				if (scrollParentEl !== rootEl) {
					scrollEl = options.scroll;
					scrollParentEl = rootEl;
122
					scrollCustomFn = options.scrollFn;
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

					if (scrollEl === true) {
						scrollEl = rootEl;

						do {
							if ((scrollEl.offsetWidth < scrollEl.scrollWidth) ||
								(scrollEl.offsetHeight < scrollEl.scrollHeight)
							) {
								break;
							}
							/* jshint boss:true */
						} while (scrollEl = scrollEl.parentNode);
					}
				}

				if (scrollEl) {
					el = scrollEl;
					rect = scrollEl.getBoundingClientRect();
					vx = (abs(rect.right - x) <= sens) - (abs(rect.left - x) <= sens);
					vy = (abs(rect.bottom - y) <= sens) - (abs(rect.top - y) <= sens);
				}


				if (!(vx || vy)) {
					vx = (winWidth - x <= sens) - (x <= sens);
					vy = (winHeight - y <= sens) - (y <= sens);

					/* jshint expr:true */
					(vx || vy) && (el = win);
				}


				if (autoScroll.vx !== vx || autoScroll.vy !== vy || autoScroll.el !== el) {
					autoScroll.el = el;
					autoScroll.vx = vx;
					autoScroll.vy = vy;

					clearInterval(autoScroll.pid);

					if (el) {
						autoScroll.pid = setInterval(function () {
164 165 166
							scrollOffsetY = vy ? vy * speed : 0;
							scrollOffsetX = vx ? vx * speed : 0;

D
desmaisons_david 已提交
167
							if ('function' === typeof(scrollCustomFn)) {
168 169 170
								return scrollCustomFn.call(_this, scrollOffsetX, scrollOffsetY, evt);
							}

171
							if (el === win) {
172
								win.scrollTo(win.pageXOffset + scrollOffsetX, win.pageYOffset + scrollOffsetY);
173
							} else {
174 175
								el.scrollTop += scrollOffsetY;
								el.scrollLeft += scrollOffsetX;
176 177 178 179 180
							}
						}, 24);
					}
				}
			}
R
RubaXa 已提交
181 182 183
		}, 30),

		_prepareGroup = function (options) {
R
RubaXa 已提交
184
			function toFn(value, pull) {
R
RubaXa 已提交
185
				if (value == null || value === true) {
186
					value = group.name;
R
RubaXa 已提交
187 188 189
					if (value == null) {
						return alwaysFalse;
					}
R
RubaXa 已提交
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
				}

				if (typeof value === 'function') {
					return value;
				} else {
					return function (to, from) {
						var fromGroup = from.options.group.name;

						return pull
							? value
							: value && (value.join
								? value.indexOf(fromGroup) > -1
								: (fromGroup == value)
							);
					};
				}
			}

R
RubaXa 已提交
208 209
			var group = {};
			var originalGroup = options.group;
R
RubaXa 已提交
210

R
RubaXa 已提交
211
			if (!originalGroup || typeof originalGroup != 'object') {
D
desmaisons_david 已提交
212
				originalGroup = {name: originalGroup};
R
RubaXa 已提交
213 214
			}

R
RubaXa 已提交
215 216 217
			group.name = originalGroup.name;
			group.checkPull = toFn(originalGroup.pull, true);
			group.checkPut = toFn(originalGroup.put);
L
Lebedev Konstantin 已提交
218
			group.revertClone = originalGroup.revertClone;
R
RubaXa 已提交
219 220

			options.group = group;
R
RubaXa 已提交
221
		}
D
desmaisons_david 已提交
222
	;
R
RubaXa 已提交
223

R
RubaXa 已提交
224 225 226 227
	// Detect support a passive mode
	try {
		window.addEventListener('test', null, Object.defineProperty({}, 'passive', {
			get: function () {
R
RubaXa 已提交
228 229 230
				// `false`, because everything starts to work incorrectly and instead of d'n'd,
				// begins the page has scrolled.
				passiveMode = false;
R
RubaXa 已提交
231 232 233 234 235 236 237
				captureMode = {
					capture: false,
					passive: passiveMode
				};
			}
		}));
	} catch (err) {}
R
RubaXa 已提交
238 239 240 241

	/**
	 * @class  Sortable
	 * @param  {HTMLElement}  el
242
	 * @param  {Object}       [options]
R
RubaXa 已提交
243
	 */
R
RubaXa 已提交
244
	function Sortable(el, options) {
R
RubaXa 已提交
245 246 247 248
		if (!(el && el.nodeType && el.nodeType === 1)) {
			throw 'Sortable: `el` must be HTMLElement, and not ' + {}.toString.call(el);
		}

R
RubaXa 已提交
249
		this.el = el; // root element
250
		this.options = options = _extend({}, options);
R
RubaXa 已提交
251 252


253 254 255
		// Export instance
		el[expando] = this;

R
RubaXa 已提交
256
		// Default options
257
		var defaults = {
R
RubaXa 已提交
258
			group: null,
R
RubaXa 已提交
259
			sort: true,
R
RubaXa 已提交
260
			disabled: false,
261 262
			store: null,
			handle: null,
R
RubaXa 已提交
263 264 265
			scroll: true,
			scrollSensitivity: 30,
			scrollSpeed: 10,
R
RubaXa 已提交
266
			draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*',
267
			ghostClass: 'sortable-ghost',
R
RubaXa 已提交
268
			chosenClass: 'sortable-chosen',
R
RubaXa 已提交
269
			dragClass: 'sortable-drag',
270
			ignore: 'a, img',
271
			filter: null,
L
Lebedev Konstantin 已提交
272
			preventOnFilter: true,
R
RubaXa 已提交
273 274 275
			animation: 0,
			setData: function (dataTransfer, dragEl) {
				dataTransfer.setData('Text', dragEl.textContent);
276 277
			},
			dropBubble: false,
R
RubaXa 已提交
278
			dragoverBubble: false,
279
			dataIdAttr: 'data-id',
280
			delay: 0,
C
ChiefORZ 已提交
281 282
			forceFallback: false,
			fallbackClass: 'sortable-fallback',
283
			fallbackOnBody: false,
284
			fallbackTolerance: 0,
R
RubaXa 已提交
285
			fallbackOffset: {x: 0, y: 0},
286
			supportPointer: Sortable.supportPointer !== false
R
RubaXa 已提交
287
		};
288

R
RubaXa 已提交
289

290 291
		// Set default options
		for (var name in defaults) {
R
RubaXa 已提交
292
			!(name in options) && (options[name] = defaults[name]);
293
		}
R
RubaXa 已提交
294

R
RubaXa 已提交
295
		_prepareGroup(options);
R
RubaXa 已提交
296

R
* JSDoc  
RubaXa 已提交
297
		// Bind all private methods
R
RubaXa 已提交
298
		for (var fn in this) {
D
dev101 已提交
299
			if (fn.charAt(0) === '_' && typeof this[fn] === 'function') {
300
				this[fn] = this[fn].bind(this);
R
RubaXa 已提交
301 302 303
			}
		}

R
RubaXa 已提交
304 305
		// Setup drag mode
		this.nativeDraggable = options.forceFallback ? false : supportDraggable;
R
RubaXa 已提交
306 307 308 309

		// Bind events
		_on(el, 'mousedown', this._onTapStart);
		_on(el, 'touchstart', this._onTapStart);
R
RubaXa 已提交
310
		options.supportPointer && _on(el, 'pointerdown', this._onTapStart);
R
RubaXa 已提交
311

R
RubaXa 已提交
312
		if (this.nativeDraggable) {
313 314 315
			_on(el, 'dragover', this);
			_on(el, 'dragenter', this);
		}
R
RubaXa 已提交
316 317

		touchDragOverListeners.push(this._onDragOver);
318 319 320

		// Restore sorting
		options.store && this.sort(options.store.get(this));
R
RubaXa 已提交
321 322 323
	}


324
	Sortable.prototype = /** @lends Sortable.prototype */ {
R
RubaXa 已提交
325 326
		constructor: Sortable,

327
		_onTapStart: function (/** Event|TouchEvent */evt) {
328 329
			var _this = this,
				el = this.el,
330
				options = this.options,
331
				preventOnFilter = options.preventOnFilter,
332 333 334
				type = evt.type,
				touch = evt.touches && evt.touches[0],
				target = (touch || evt).target,
V
Varunkumar Nagarajan 已提交
335
				originalTarget = evt.target.shadowRoot && (evt.path && evt.path[0]) || target,
R
RubaXa 已提交
336 337
				filter = options.filter,
				startIndex;
R
RubaXa 已提交
338

L
Lebedev Konstantin 已提交
339 340 341
			_saveInputCheckedState(el);


R
RubaXa 已提交
342
			// Don't trigger start event when an element is been dragged, otherwise the evt.oldindex always wrong when set option.group.
W
why520crazy 已提交
343
			if (dragEl) {
344 345
				return;
			}
R
RubaXa 已提交
346

347
			if (/mousedown|pointerdown/.test(type) && evt.button !== 0 || options.disabled) {
348
				return; // only left button or enabled
R
RubaXa 已提交
349
			}
R
RubaXa 已提交
350

351 352 353 354
			// cancel dnd if original target is content editable
			if (originalTarget.isContentEditable) {
				return;
			}
355 356

			target = _closest(target, options.draggable, el);
357

358
			if (!target) {
359 360
				return;
			}
361

L
Lebedev Konstantin 已提交
362 363 364 365 366
			if (lastDownEl === target) {
				// Ignoring duplicate `down`
				return;
			}

R
RubaXa 已提交
367
			// Get the index of the dragged element within its parent
R
RubaXa 已提交
368
			startIndex = _index(target, options.draggable);
369 370 371 372

			// Check filter
			if (typeof filter === 'function') {
				if (filter.call(this, evt, target, this)) {
J
Joey Becker 已提交
373
					_dispatchEvent(_this, originalTarget, 'filter', target, el, el, startIndex);
L
Lebedev Konstantin 已提交
374
					preventOnFilter && evt.preventDefault();
375 376
					return; // cancel dnd
				}
377
			}
378 379 380
			else if (filter) {
				filter = filter.split(',').some(function (criteria) {
					criteria = _closest(originalTarget, criteria.trim(), el);
381

382
					if (criteria) {
J
Joey Becker 已提交
383
						_dispatchEvent(_this, criteria, 'filter', target, el, el, startIndex);
384 385 386 387 388
						return true;
					}
				});

				if (filter) {
L
Lebedev Konstantin 已提交
389
					preventOnFilter && evt.preventDefault();
390
					return; // cancel dnd
391 392 393
				}
			}

394 395 396 397
			if (options.handle && !_closest(originalTarget, options.handle, el)) {
				return;
			}

398
			// Prepare `dragstart`
R
RubaXa 已提交
399
			this._prepareDragStart(evt, touch, target, startIndex);
400 401
		},

R
RubaXa 已提交
402
		_prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target, /** Number */startIndex) {
403 404 405 406 407
			var _this = this,
				el = _this.el,
				options = _this.options,
				ownerDocument = el.ownerDocument,
				dragStartFn;
408 409 410 411

			if (target && !dragEl && (target.parentNode === el)) {
				tapEvt = evt;

412
				rootEl = el;
413
				dragEl = target;
R
RubaXa 已提交
414
				parentEl = dragEl.parentNode;
415
				nextEl = dragEl.nextSibling;
D
desmaisons_david 已提交
416
				lastDownEl = target;
417
				activeGroup = options.group;
R
RubaXa 已提交
418
				oldIndex = startIndex;
419

420 421
				this._lastX = (touch || evt).clientX;
				this._lastY = (touch || evt).clientY;
S
sp-kilobug 已提交
422

R
RubaXa 已提交
423
				dragEl.style['will-change'] = 'all';
L
Lebedev Konstantin 已提交
424

425 426 427 428
				dragStartFn = function () {
					// Delayed drag has been triggered
					// we can re-enable the events: touchmove/mousemove
					_this._disableDelayedDrag();
429

430
					// Make the element draggable
431
					dragEl.draggable = _this.nativeDraggable;
432

R
RubaXa 已提交
433
					// Chosen item
R
RubaXa 已提交
434
					_toggleClass(dragEl, options.chosenClass, true);
435 436

					// Bind the events: dragstart/dragend
R
RubaXa 已提交
437
					_this._triggerDragStart(evt, touch);
T
TaliLavi 已提交
438 439

					// Drag start event
J
Joey Becker 已提交
440
					_dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, rootEl, oldIndex);
441 442
				};

R
RubaXa 已提交
443 444 445 446 447
				// Disable "draggable"
				options.ignore.split(',').forEach(function (criteria) {
					_find(dragEl, criteria.trim(), _disableDraggable);
				});

448 449 450
				_on(ownerDocument, 'mouseup', _this._onDrop);
				_on(ownerDocument, 'touchend', _this._onDrop);
				_on(ownerDocument, 'touchcancel', _this._onDrop);
451
				_on(ownerDocument, 'selectstart', _this);
R
RubaXa 已提交
452
				options.supportPointer && _on(ownerDocument, 'pointercancel', _this._onDrop);
453 454

				if (options.delay) {
455 456
					// If the user moves the pointer or let go the click or touch
					// before the delay has been reached:
457
					// disable the delayed drag
458 459 460
					_on(ownerDocument, 'mouseup', _this._disableDelayedDrag);
					_on(ownerDocument, 'touchend', _this._disableDelayedDrag);
					_on(ownerDocument, 'touchcancel', _this._disableDelayedDrag);
461 462
					_on(ownerDocument, 'mousemove', _this._disableDelayedDrag);
					_on(ownerDocument, 'touchmove', _this._disableDelayedDrag);
R
RubaXa 已提交
463
					options.supportPointer && _on(ownerDocument, 'pointermove', _this._disableDelayedDrag);
464 465 466 467

					_this._dragStartTimer = setTimeout(dragStartFn, options.delay);
				} else {
					dragStartFn();
468
				}
469

470

D
desmaisons_david 已提交
471
			}
472
		},
R
RubaXa 已提交
473

474 475
		_disableDelayedDrag: function () {
			var ownerDocument = this.el.ownerDocument;
476

477
			clearTimeout(this._dragStartTimer);
478 479 480
			_off(ownerDocument, 'mouseup', this._disableDelayedDrag);
			_off(ownerDocument, 'touchend', this._disableDelayedDrag);
			_off(ownerDocument, 'touchcancel', this._disableDelayedDrag);
481 482
			_off(ownerDocument, 'mousemove', this._disableDelayedDrag);
			_off(ownerDocument, 'touchmove', this._disableDelayedDrag);
483
			_off(ownerDocument, 'pointermove', this._disableDelayedDrag);
484
		},
R
RubaXa 已提交
485

486 487
		_triggerDragStart: function (/** Event */evt, /** Touch */touch) {
			touch = touch || (evt.pointerType == 'touch' ? evt : null);
488

489 490 491 492 493 494 495
			if (touch) {
				// Touch device support
				tapEvt = {
					target: dragEl,
					clientX: touch.clientX,
					clientY: touch.clientY
				};
496

497
				this._onDragStart(tapEvt, 'touch');
R
RubaXa 已提交
498
			}
R
RubaXa 已提交
499
			else if (!this.nativeDraggable) {
500 501 502 503 504
				this._onDragStart(tapEvt, true);
			}
			else {
				_on(dragEl, 'dragend', this);
				_on(rootEl, 'dragstart', this._onDragStart);
505 506
			}

507
			try {
508 509
				if (document.selection) {
					// Timeout neccessary for IE9
R
RubaXa 已提交
510
					_nextTick(function () {
511
						document.selection.empty();
512
					});
513 514
				} else {
					window.getSelection().removeAllRanges();
515
				}
516
			} catch (err) {
517
			}
518
		},
519

520 521
		_dragStarted: function () {
			if (rootEl && dragEl) {
R
RubaXa 已提交
522 523
				var options = this.options;

524
				// Apply effect
R
RubaXa 已提交
525 526
				_toggleClass(dragEl, options.ghostClass, true);
				_toggleClass(dragEl, options.dragClass, false);
R
RubaXa 已提交
527

528
				Sortable.active = this;
529

530
				// Drag start event
J
Joey Becker 已提交
531
				_dispatchEvent(this, rootEl, 'start', dragEl, rootEl, rootEl, oldIndex);
L
#1009  
Lebedev Konstantin 已提交
532 533
			} else {
				this._nulling();
534
			}
R
RubaXa 已提交
535 536
		},

R
RubaXa 已提交
537 538
		_emulateDragOver: function () {
			if (touchEvt) {
R
RubaXa 已提交
539
				if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY) {
540 541
					return;
				}
R
RubaXa 已提交
542 543 544

				this._lastX = touchEvt.clientX;
				this._lastY = touchEvt.clientY;
545

546 547 548
				if (!supportCssPointerEvents) {
					_css(ghostEl, 'display', 'none');
				}
R
RubaXa 已提交
549

L
Luiz Corte Real 已提交
550 551 552
				var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
				var parent = target;
				var i = touchDragOverListeners.length;
R
RubaXa 已提交
553

554 555
				if (target && target.shadowRoot) {
					target = target.shadowRoot.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
L
Luiz Corte Real 已提交
556
					parent = target;
557 558
				}

R
RubaXa 已提交
559 560
				if (parent) {
					do {
R
RubaXa 已提交
561
						if (parent[expando]) {
R
RubaXa 已提交
562 563 564 565 566 567 568 569 570 571 572 573 574
							while (i--) {
								touchDragOverListeners[i]({
									clientX: touchEvt.clientX,
									clientY: touchEvt.clientY,
									target: target,
									rootEl: parent
								});
							}

							break;
						}

						target = parent; // store last element
L
Larry Davis 已提交
575
					}
R
RubaXa 已提交
576 577
					/* jshint boss:true */
					while (parent = parent.parentNode);
R
RubaXa 已提交
578 579
				}

580 581 582
				if (!supportCssPointerEvents) {
					_css(ghostEl, 'display', '');
				}
R
RubaXa 已提交
583 584 585 586
			}
		},


R
RubaXa 已提交
587 588
		_onTouchMove: function (/**TouchEvent*/evt) {
			if (tapEvt) {
D
desmaisons_david 已提交
589
				var	options = this.options,
R
RubaXa 已提交
590
					fallbackTolerance = options.fallbackTolerance,
591
					fallbackOffset = options.fallbackOffset,
R
RubaXa 已提交
592
					touch = evt.touches ? evt.touches[0] : evt,
593 594
					dx = (touch.clientX - tapEvt.clientX) + fallbackOffset.x,
					dy = (touch.clientY - tapEvt.clientY) + fallbackOffset.y,
R
RubaXa 已提交
595
					translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';
S
sp-kilobug 已提交
596

597
				// only set the status to dragging, when we are actually dragging
R
RubaXa 已提交
598
				if (!Sortable.active) {
R
RubaXa 已提交
599 600 601 602
					if (fallbackTolerance &&
						min(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY)) < fallbackTolerance
					) {
						return;
S
sp-kilobug 已提交
603
					}
R
RubaXa 已提交
604

605 606
					this._dragStarted();
				}
R
RubaXa 已提交
607

608 609
				// as well as creating the ghost element on the document body
				this._appendGhost();
R
RubaXa 已提交
610

A
Alex Wild 已提交
611
				moved = true;
R
RubaXa 已提交
612
				touchEvt = touch;
R
RubaXa 已提交
613 614 615 616 617 618

				_css(ghostEl, 'webkitTransform', translate3d);
				_css(ghostEl, 'mozTransform', translate3d);
				_css(ghostEl, 'msTransform', translate3d);
				_css(ghostEl, 'transform', translate3d);

M
Marius Petcu 已提交
619
				evt.preventDefault();
R
RubaXa 已提交
620 621 622
			}
		},

R
RubaXa 已提交
623 624
		_appendGhost: function () {
			if (!ghostEl) {
R
RubaXa 已提交
625 626
				var rect = dragEl.getBoundingClientRect(),
					css = _css(dragEl),
R
RubaXa 已提交
627
					options = this.options,
R
RubaXa 已提交
628
					ghostRect;
R
RubaXa 已提交
629

630
				ghostEl = dragEl.cloneNode(true);
R
RubaXa 已提交
631

R
RubaXa 已提交
632 633
				_toggleClass(ghostEl, options.ghostClass, false);
				_toggleClass(ghostEl, options.fallbackClass, true);
R
RubaXa 已提交
634
				_toggleClass(ghostEl, options.dragClass, true);
C
ChiefORZ 已提交
635

R
RubaXa 已提交
636 637
				_css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10));
				_css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10));
R
RubaXa 已提交
638 639
				_css(ghostEl, 'width', rect.width);
				_css(ghostEl, 'height', rect.height);
R
RubaXa 已提交
640 641 642
				_css(ghostEl, 'opacity', '0.8');
				_css(ghostEl, 'position', 'fixed');
				_css(ghostEl, 'zIndex', '100000');
643
				_css(ghostEl, 'pointerEvents', 'none');
R
RubaXa 已提交
644

R
RubaXa 已提交
645
				options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
R
RubaXa 已提交
646 647 648

				// Fixing dimensions.
				ghostRect = ghostEl.getBoundingClientRect();
R
RubaXa 已提交
649 650
				_css(ghostEl, 'width', rect.width * 2 - ghostRect.width);
				_css(ghostEl, 'height', rect.height * 2 - ghostRect.height);
651 652 653 654
			}
		},

		_onDragStart: function (/**Event*/evt, /**boolean*/useFallback) {
R
RubaXa 已提交
655 656
			var _this = this;
			var dataTransfer = evt.dataTransfer;
R
RubaXa 已提交
657
			var options = _this.options;
658

R
RubaXa 已提交
659
			_this._offUpEvents();
660

R
RubaXa 已提交
661
			if (activeGroup.checkPull(_this, _this, dragEl, evt)) {
662
				cloneEl = _clone(dragEl);
L
Lebedev Konstantin 已提交
663 664 665

				cloneEl.draggable = false;
				cloneEl.style['will-change'] = '';
L
Lebedev Konstantin 已提交
666

667
				_css(cloneEl, 'display', 'none');
R
RubaXa 已提交
668
				_toggleClass(cloneEl, _this.options.chosenClass, false);
L
Lebedev Konstantin 已提交
669

R
RubaXa 已提交
670
				// #1143: IFrame support workaround
R
RubaXa 已提交
671 672 673
				_this._cloneId = _nextTick(function () {
					rootEl.insertBefore(cloneEl, dragEl);
					_dispatchEvent(_this, rootEl, 'clone', dragEl);
R
RubaXa 已提交
674
				});
675 676
			}

R
RubaXa 已提交
677 678
			_toggleClass(dragEl, options.dragClass, true);

679
			if (useFallback) {
R
RubaXa 已提交
680 681
				if (useFallback === 'touch') {
					// Bind touch events
R
RubaXa 已提交
682 683 684
					_on(document, 'touchmove', _this._onTouchMove);
					_on(document, 'touchend', _this._onDrop);
					_on(document, 'touchcancel', _this._onDrop);
R
RubaXa 已提交
685 686 687 688 689

					if (options.supportPointer) {
						_on(document, 'pointermove', _this._onTouchMove);
						_on(document, 'pointerup', _this._onDrop);
					}
R
RubaXa 已提交
690 691
				} else {
					// Old brwoser
R
RubaXa 已提交
692 693
					_on(document, 'mousemove', _this._onTouchMove);
					_on(document, 'mouseup', _this._onDrop);
R
RubaXa 已提交
694
				}
R
RubaXa 已提交
695

R
RubaXa 已提交
696
				_this._loopId = setInterval(_this._emulateDragOver, 50);
R
RubaXa 已提交
697 698
			}
			else {
R
RubaXa 已提交
699 700
				if (dataTransfer) {
					dataTransfer.effectAllowed = 'move';
R
RubaXa 已提交
701
					options.setData && options.setData.call(_this, dataTransfer, dragEl);
R
RubaXa 已提交
702
				}
R
RubaXa 已提交
703

R
RubaXa 已提交
704
				_on(document, 'drop', _this);
R
RubaXa 已提交
705 706

				// #1143: Бывает элемент с IFrame внутри блокирует `drop`,
R
typo  
Roman Dvornov 已提交
707
				// поэтому если вызвался `mouseover`, значит надо отменять весь d'n'd.
R
RubaXa 已提交
708 709
				// Breaking Chrome 62+
				// _on(document, 'mouseover', _this);
R
RubaXa 已提交
710

R
RubaXa 已提交
711
				_this._dragStartId = _nextTick(_this._dragStarted);
R
RubaXa 已提交
712 713 714
			}
		},

R
RubaXa 已提交
715
		_onDragOver: function (/**Event*/evt) {
R
RubaXa 已提交
716 717 718
			var el = this.el,
				target,
				dragRect,
R
#930  
RubaXa 已提交
719
				targetRect,
R
RubaXa 已提交
720 721 722
				revert,
				options = this.options,
				group = options.group,
R
RubaXa 已提交
723
				activeSortable = Sortable.active,
724
				isOwner = (activeGroup === group),
725
				isMovingBetweenSortable = false,
726
				canSort = options.sort;
R
RubaXa 已提交
727

R
RubaXa 已提交
728
			if (evt.preventDefault !== void 0) {
R
RubaXa 已提交
729
				evt.preventDefault();
730
				!options.dragoverBubble && evt.stopPropagation();
R
RubaXa 已提交
731
			}
R
RubaXa 已提交
732

733 734 735 736
			if (dragEl.animated) {
				return;
			}

A
Alex Wild 已提交
737 738
			moved = true;

L
#1009  
Lebedev Konstantin 已提交
739
			if (activeSortable && !options.disabled &&
740
				(isOwner
R
RubaXa 已提交
741
					? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list
R
RubaXa 已提交
742 743
					: (
						putSortable === this ||
744 745 746 747
						(
							(activeSortable.lastPullMode = activeGroup.checkPull(this, activeSortable, dragEl, evt)) &&
							group.checkPut(this, activeSortable, dragEl, evt)
						)
R
RubaXa 已提交
748
					)
749
				) &&
R
RubaXa 已提交
750
				(evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback
R
RubaXa 已提交
751
			) {
R
RubaXa 已提交
752 753 754 755 756 757 758
				// Smart auto-scrolling
				_autoScroll(evt, options, this.el);

				if (_silent) {
					return;
				}

R
RubaXa 已提交
759
				target = _closest(evt.target, options.draggable, el);
R
RubaXa 已提交
760
				dragRect = dragEl.getBoundingClientRect();
761 762

				if (putSortable !== this) {
D
desmaisons_david 已提交
763
					putSortable = this;
764 765
					isMovingBetweenSortable = true;
				}
R
RubaXa 已提交
766

767
				if (revert) {
L
Lebedev Konstantin 已提交
768
					_cloneHide(activeSortable, true);
R
RubaXa 已提交
769
					parentEl = rootEl; // actualization
R
RubaXa 已提交
770

771 772 773 774 775 776 777
					if (cloneEl || nextEl) {
						rootEl.insertBefore(dragEl, cloneEl || nextEl);
					}
					else if (!canSort) {
						rootEl.appendChild(dragEl);
					}

R
RubaXa 已提交
778 779
					return;
				}
R
RubaXa 已提交
780

R
RubaXa 已提交
781

R
RubaXa 已提交
782
				if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
783
					(el === evt.target) && (_ghostIsLast(el, evt))
R
RubaXa 已提交
784
				) {
785 786 787 788 789
					//assign target only if condition is true
					if (el.children.length !== 0 && el.children[0] !== ghostEl && el === evt.target) {
						target = el.lastElementChild;
					}

R
RubaXa 已提交
790 791 792 793
					if (target) {
						if (target.animated) {
							return;
						}
R
RubaXa 已提交
794

R
RubaXa 已提交
795 796
						targetRect = target.getBoundingClientRect();
					}
R
RubaXa 已提交
797

L
Lebedev Konstantin 已提交
798
					_cloneHide(activeSortable, isOwner);
R
RubaXa 已提交
799

800
					if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt) !== false) {
801 802
						if (!dragEl.contains(el)) {
							el.appendChild(dragEl);
R
RubaXa 已提交
803
							parentEl = el; // actualization
804
						}
R
RubaXa 已提交
805

R
RubaXa 已提交
806 807 808
						this._animate(dragRect, dragEl);
						target && this._animate(targetRect, target);
					}
R
RubaXa 已提交
809
				}
R
RubaXa 已提交
810 811
				else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) {
					if (lastEl !== target) {
R
RubaXa 已提交
812
						lastEl = target;
R
RubaXa 已提交
813
						lastCSS = _css(target);
S
sp-kilobug 已提交
814
						lastParentCSS = _css(target.parentNode);
R
RubaXa 已提交
815 816
					}

R
#930  
RubaXa 已提交
817
					targetRect = target.getBoundingClientRect();
R
RubaXa 已提交
818

R
#930  
RubaXa 已提交
819
					var width = targetRect.right - targetRect.left,
R
RubaXa 已提交
820
						height = targetRect.bottom - targetRect.top,
821
						floating = R_FLOAT.test(lastCSS.cssFloat + lastCSS.display)
S
sp-kilobug 已提交
822
							|| (lastParentCSS.display == 'flex' && lastParentCSS['flex-direction'].indexOf('row') === 0),
R
RubaXa 已提交
823 824 825 826
						isWide = (target.offsetWidth > dragEl.offsetWidth),
						isLong = (target.offsetHeight > dragEl.offsetHeight),
						halfway = (floating ? (evt.clientX - targetRect.left) / width : (evt.clientY - targetRect.top) / height) > 0.5,
						nextSibling = target.nextElementSibling,
827
						after = false
D
desmaisons_david 已提交
828
					;
R
RubaXa 已提交
829

830 831 832
					if (floating) {
						var elTop = dragEl.offsetTop,
							tgTop = target.offsetTop;
R
RubaXa 已提交
833

834 835 836 837 838 839 840 841
						if (elTop === tgTop) {
							after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide;
						}
						else if (target.previousElementSibling === dragEl || dragEl.previousElementSibling === target) {
							after = (evt.clientY - targetRect.top) / height > 0.5;
						} else {
							after = tgTop > elTop;
						}
D
desmaisons_david 已提交
842
						} else if (!isMovingBetweenSortable) {
843 844
						after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
					}
R
RubaXa 已提交
845

846 847 848
					var moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, after);

					if (moveVector !== false) {
R
RubaXa 已提交
849 850 851
						if (moveVector === 1 || moveVector === -1) {
							after = (moveVector === 1);
						}
R
RubaXa 已提交
852

853 854 855 856
						_silent = true;
						setTimeout(_unsilent, 30);

						_cloneHide(activeSortable, isOwner);
R
RubaXa 已提交
857

858 859 860 861 862 863
						if (!dragEl.contains(el)) {
							if (after && !nextSibling) {
								el.appendChild(dragEl);
							} else {
								target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
							}
R
RubaXa 已提交
864
						}
R
RubaXa 已提交
865

R
RubaXa 已提交
866 867
						parentEl = dragEl.parentNode; // actualization

R
RubaXa 已提交
868 869 870
						this._animate(dragRect, dragEl);
						this._animate(targetRect, target);
					}
R
RubaXa 已提交
871 872 873 874
				}
			}
		},

875 876 877 878 879 880
		_animate: function (prevRect, target) {
			var ms = this.options.animation;

			if (ms) {
				var currentRect = target.getBoundingClientRect();

L
Lebedev Konstantin 已提交
881 882 883 884
				if (prevRect.nodeType === 1) {
					prevRect = prevRect.getBoundingClientRect();
				}

R
RubaXa 已提交
885
				_css(target, 'transition', 'none');
886 887 888 889 890 891 892
				_css(target, 'transform', 'translate3d('
					+ (prevRect.left - currentRect.left) + 'px,'
					+ (prevRect.top - currentRect.top) + 'px,0)'
				);

				target.offsetWidth; // repaint

R
RubaXa 已提交
893
				_css(target, 'transition', 'all ' + ms + 'ms');
894 895
				_css(target, 'transform', 'translate3d(0,0,0)');

R
* anim  
RubaXa 已提交
896 897
				clearTimeout(target.animated);
				target.animated = setTimeout(function () {
898
					_css(target, 'transition', '');
899
					_css(target, 'transform', '');
900 901 902 903 904
					target.animated = false;
				}, ms);
			}
		},

905
		_offUpEvents: function () {
906 907
			var ownerDocument = this.el.ownerDocument;

908
			_off(document, 'touchmove', this._onTouchMove);
909
			_off(document, 'pointermove', this._onTouchMove);
910 911
			_off(ownerDocument, 'mouseup', this._onDrop);
			_off(ownerDocument, 'touchend', this._onDrop);
912
			_off(ownerDocument, 'pointerup', this._onDrop);
913
			_off(ownerDocument, 'touchcancel', this._onDrop);
진유정 已提交
914
			_off(ownerDocument, 'pointercancel', this._onDrop);
915
			_off(ownerDocument, 'selectstart', this);
916
		},
R
RubaXa 已提交
917

R
RubaXa 已提交
918
		_onDrop: function (/**Event*/evt) {
919 920
			var el = this.el,
				options = this.options;
R
RubaXa 已提交
921

R
RubaXa 已提交
922
			clearInterval(this._loopId);
R
RubaXa 已提交
923
			clearInterval(autoScroll.pid);
R
RubaXa 已提交
924
			clearTimeout(this._dragStartTimer);
925

R
RubaXa 已提交
926 927 928
			_cancelNextTick(this._cloneId);
			_cancelNextTick(this._dragStartId);

R
RubaXa 已提交
929
			// Unbind events
R
RubaXa 已提交
930
			_off(document, 'mouseover', this);
R
RubaXa 已提交
931
			_off(document, 'mousemove', this._onTouchMove);
932

R
RubaXa 已提交
933
			if (this.nativeDraggable) {
934 935 936
				_off(document, 'drop', this);
				_off(el, 'dragstart', this._onDragStart);
			}
R
RubaXa 已提交
937

938
			this._offUpEvents();
R
RubaXa 已提交
939

R
RubaXa 已提交
940
			if (evt) {
R
RubaXa 已提交
941
				if (moved) {
R
RubaXa 已提交
942
					evt.preventDefault();
C
ChiefORZ 已提交
943 944
					!options.dropBubble && evt.stopPropagation();
				}
R
RubaXa 已提交
945

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

L
Lebedev Konstantin 已提交
948 949
				if (rootEl === parentEl || Sortable.active.lastPullMode !== 'clone') {
					// Remove clone
B
Billy Hardy 已提交
950
					cloneEl && cloneEl.parentNode && cloneEl.parentNode.removeChild(cloneEl);
L
Lebedev Konstantin 已提交
951 952
				}

R
RubaXa 已提交
953
				if (dragEl) {
R
RubaXa 已提交
954
					if (this.nativeDraggable) {
955 956
						_off(dragEl, 'dragend', this);
					}
R
RubaXa 已提交
957

958
					_disableDraggable(dragEl);
L
Lebedev Konstantin 已提交
959
					dragEl.style['will-change'] = '';
R
RubaXa 已提交
960 961

					// Remove class's
R
RubaXa 已提交
962
					_toggleClass(dragEl, this.options.ghostClass, false);
R
RubaXa 已提交
963
					_toggleClass(dragEl, this.options.chosenClass, false);
R
RubaXa 已提交
964

965
					// Drag stop event
J
Joey Becker 已提交
966
					_dispatchEvent(this, rootEl, 'unchoose', dragEl, parentEl, rootEl, oldIndex);
967

968
					if (rootEl !== parentEl) {
969
						newIndex = _index(dragEl, options.draggable);
R
RubaXa 已提交
970

971
						if (newIndex >= 0) {
972
							// Add event
J
Joey Becker 已提交
973
							_dispatchEvent(null, parentEl, 'add', dragEl, parentEl, rootEl, oldIndex, newIndex);
974

975
							// Remove event
J
Joey Becker 已提交
976
							_dispatchEvent(this, rootEl, 'remove', dragEl, parentEl, rootEl, oldIndex, newIndex);
N
n00dl3 已提交
977

N
n00dl3 已提交
978
							// drag from one list and drop into another
J
Joey Becker 已提交
979 980
							_dispatchEvent(null, parentEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex);
							_dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex);
981
						}
R
RubaXa 已提交
982
					}
R
RubaXa 已提交
983 984 985
					else {
						if (dragEl.nextSibling !== nextEl) {
							// Get the index of the dragged element within its parent
986
							newIndex = _index(dragEl, options.draggable);
987 988

							if (newIndex >= 0) {
989
								// drag & drop within the same list
J
Joey Becker 已提交
990 991
								_dispatchEvent(this, rootEl, 'update', dragEl, parentEl, rootEl, oldIndex, newIndex);
								_dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex);
992
							}
R
RubaXa 已提交
993
						}
R
RubaXa 已提交
994
					}
995

R
RubaXa 已提交
996
					if (Sortable.active) {
997
						/* jshint eqnull:true */
L
Lebedev Konstantin 已提交
998
						if (newIndex == null || newIndex === -1) {
R
RubaXa 已提交
999 1000 1001
							newIndex = oldIndex;
						}

J
Joey Becker 已提交
1002
						_dispatchEvent(this, rootEl, 'end', dragEl, parentEl, rootEl, oldIndex, newIndex);
R
RubaXa 已提交
1003 1004 1005 1006

						// Save sorting
						this.save();
					}
R
RubaXa 已提交
1007
				}
R
RubaXa 已提交
1008

1009
			}
R
RubaXa 已提交
1010

1011 1012
			this._nulling();
		},
R
RubaXa 已提交
1013

D
desmaisons_david 已提交
1014
		_nulling: function() {
L
Lebedev Konstantin 已提交
1015
			rootEl =
D
desmaisons_david 已提交
1016 1017 1018 1019 1020 1021
			dragEl =
			parentEl =
			ghostEl =
			nextEl =
			cloneEl =
			lastDownEl =
R
RubaXa 已提交
1022

D
desmaisons_david 已提交
1023 1024
			scrollEl =
			scrollParentEl =
C
ChiefORZ 已提交
1025

D
desmaisons_david 已提交
1026 1027
			tapEvt =
			touchEvt =
R
RubaXa 已提交
1028

D
desmaisons_david 已提交
1029 1030
			moved =
			newIndex =
R
RubaXa 已提交
1031

D
desmaisons_david 已提交
1032 1033
			lastEl =
			lastCSS =
R
RubaXa 已提交
1034

D
desmaisons_david 已提交
1035 1036 1037
			putSortable =
			activeGroup =
			Sortable.active = null;
L
Lebedev Konstantin 已提交
1038 1039 1040 1041 1042

			savedInputChecked.forEach(function (el) {
				el.checked = true;
			});
			savedInputChecked.length = 0;
1043
		},
R
RubaXa 已提交
1044

R
RubaXa 已提交
1045
		handleEvent: function (/**Event*/evt) {
1046 1047 1048 1049 1050 1051 1052 1053
			switch (evt.type) {
				case 'drop':
				case 'dragend':
					this._onDrop(evt);
					break;

				case 'dragover':
				case 'dragenter':
D
desmaisons_david 已提交
1054 1055 1056 1057
					if (dragEl) {
						this._onDragOver(evt);
						_globalDragOver(evt);
					}
1058
					break;
R
RubaXa 已提交
1059

R
RubaXa 已提交
1060 1061 1062 1063
				case 'mouseover':
					this._onDrop(evt);
					break;

1064 1065 1066
				case 'selectstart':
					evt.preventDefault();
					break;
R
RubaXa 已提交
1067
			}
R
RubaXa 已提交
1068 1069 1070
		},


1071 1072 1073 1074 1075 1076 1077 1078 1079
		/**
		 * Serializes the item into an array of string.
		 * @returns {String[]}
		 */
		toArray: function () {
			var order = [],
				el,
				children = this.el.children,
				i = 0,
R
RubaXa 已提交
1080 1081
				n = children.length,
				options = this.options;
1082 1083 1084

			for (; i < n; i++) {
				el = children[i];
R
RubaXa 已提交
1085
				if (_closest(el, options.draggable, this.el)) {
R
RubaXa 已提交
1086
					order.push(el.getAttribute(options.dataIdAttr) || _generateId(el));
R
RubaXa 已提交
1087
				}
1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098
			}

			return order;
		},


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

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

R
RubaXa 已提交
1104
				if (_closest(el, this.options.draggable, rootEl)) {
R
RubaXa 已提交
1105 1106 1107
					items[id] = el;
				}
			}, this);
1108 1109 1110

			order.forEach(function (id) {
				if (items[id]) {
R
RubaXa 已提交
1111 1112
					rootEl.removeChild(items[id]);
					rootEl.appendChild(items[id]);
1113 1114 1115 1116 1117
				}
			});
		},


R
RubaXa 已提交
1118 1119 1120 1121 1122 1123 1124 1125 1126
		/**
		 * Save the current sorting
		 */
		save: function () {
			var store = this.options.store;
			store && store.set(this);
		},


1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
		/**
		 * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
		 * @param   {HTMLElement}  el
		 * @param   {String}       [selector]  default: `options.draggable`
		 * @returns {HTMLElement|null}
		 */
		closest: function (el, selector) {
			return _closest(el, selector || this.options.draggable, this.el);
		},


1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
		/**
		 * 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 已提交
1151 1152 1153 1154

				if (name === 'group') {
					_prepareGroup(options);
				}
1155 1156 1157 1158
			}
		},


1159 1160 1161 1162
		/**
		 * Destroy
		 */
		destroy: function () {
1163
			var el = this.el;
R
RubaXa 已提交
1164

1165
			el[expando] = null;
1166

R
RubaXa 已提交
1167 1168
			_off(el, 'mousedown', this._onTapStart);
			_off(el, 'touchstart', this._onTapStart);
1169
			_off(el, 'pointerdown', this._onTapStart);
R
RubaXa 已提交
1170

R
RubaXa 已提交
1171
			if (this.nativeDraggable) {
1172 1173 1174
				_off(el, 'dragover', this);
				_off(el, 'dragenter', this);
			}
R
RubaXa 已提交
1175

1176
			// Remove draggable attributes
R
RubaXa 已提交
1177
			Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) {
1178 1179 1180
				el.removeAttribute('draggable');
			});

R
RubaXa 已提交
1181 1182 1183 1184
			touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1);

			this._onDrop();

1185
			this.el = el = null;
R
RubaXa 已提交
1186 1187 1188
		}
	};

1189

L
Lebedev Konstantin 已提交
1190
	function _cloneHide(sortable, state) {
1191 1192 1193 1194
		if (sortable.lastPullMode !== 'clone') {
			state = true;
		}

R
RubaXa 已提交
1195 1196
		if (cloneEl && (cloneEl.state !== state)) {
			_css(cloneEl, 'display', state ? 'none' : '');
L
Lebedev Konstantin 已提交
1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208

			if (!state) {
				if (cloneEl.state) {
					if (sortable.options.group.revertClone) {
						rootEl.insertBefore(cloneEl, nextEl);
						sortable._animate(dragEl, cloneEl);
					} else {
						rootEl.insertBefore(cloneEl, dragEl);
					}
				}
			}

R
RubaXa 已提交
1209 1210 1211 1212 1213
			cloneEl.state = state;
		}
	}


R
RubaXa 已提交
1214
	function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) {
R
RubaXa 已提交
1215
		if (el) {
R
RubaXa 已提交
1216 1217 1218
			ctx = ctx || document;

			do {
R
RubaXa 已提交
1219
				if ((selector === '>*' && el.parentNode === ctx) || _matches(el, selector)) {
R
RubaXa 已提交
1220
					return el;
R
RubaXa 已提交
1221
				}
R
#930  
RubaXa 已提交
1222 1223
				/* jshint boss:true */
			} while (el = _getParentOrHost(el));
R
RubaXa 已提交
1224 1225
		}

R
RubaXa 已提交
1226
		return null;
R
RubaXa 已提交
1227 1228 1229
	}


N
Nikita SVIRIDENKO 已提交
1230 1231 1232 1233 1234
	function _getParentOrHost(el) {
		var parent = el.host;

		return (parent && parent.nodeType) ? parent : el.parentNode;
	}
1235 1236


1237
	function _globalDragOver(/**Event*/evt) {
1238 1239 1240
		if (evt.dataTransfer) {
			evt.dataTransfer.dropEffect = 'move';
		}
R
RubaXa 已提交
1241
		evt.preventDefault();
R
RubaXa 已提交
1242 1243 1244
	}


R
RubaXa 已提交
1245
	function _on(el, event, fn) {
1246
		el.addEventListener(event, fn, captureMode);
R
RubaXa 已提交
1247 1248 1249
	}


R
RubaXa 已提交
1250
	function _off(el, event, fn) {
1251
		el.removeEventListener(event, fn, captureMode);
R
RubaXa 已提交
1252 1253 1254
	}


R
RubaXa 已提交
1255 1256 1257
	function _toggleClass(el, name, state) {
		if (el) {
			if (el.classList) {
R
RubaXa 已提交
1258 1259 1260
				el.classList[state ? 'add' : 'remove'](name);
			}
			else {
1261 1262
				var className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' ');
				el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' ');
R
RubaXa 已提交
1263 1264 1265 1266 1267
			}
		}
	}


R
RubaXa 已提交
1268
	function _css(el, prop, val) {
R
RubaXa 已提交
1269 1270
		var style = el && el.style;

R
RubaXa 已提交
1271 1272 1273
		if (style) {
			if (val === void 0) {
				if (document.defaultView && document.defaultView.getComputedStyle) {
R
RubaXa 已提交
1274 1275
					val = document.defaultView.getComputedStyle(el, '');
				}
R
RubaXa 已提交
1276 1277
				else if (el.currentStyle) {
					val = el.currentStyle;
R
RubaXa 已提交
1278
				}
R
RubaXa 已提交
1279 1280 1281 1282 1283 1284 1285 1286 1287

				return prop === void 0 ? val : val[prop];
			}
			else {
				if (!(prop in style)) {
					prop = '-webkit-' + prop;
				}

				style[prop] = val + (typeof val === 'string' ? '' : 'px');
R
RubaXa 已提交
1288 1289 1290 1291 1292
			}
		}
	}


R
RubaXa 已提交
1293 1294
	function _find(ctx, tagName, iterator) {
		if (ctx) {
R
RubaXa 已提交
1295
			var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length;
R
RubaXa 已提交
1296

R
RubaXa 已提交
1297 1298
			if (iterator) {
				for (; i < n; i++) {
R
RubaXa 已提交
1299 1300 1301
					iterator(list[i], i);
				}
			}
R
RubaXa 已提交
1302

R
RubaXa 已提交
1303
			return list;
R
RubaXa 已提交
1304
		}
R
RubaXa 已提交
1305 1306

		return [];
R
RubaXa 已提交
1307 1308 1309
	}


R
RubaXa 已提交
1310

J
Joey Becker 已提交
1311
	function _dispatchEvent(sortable, rootEl, name, targetEl, toEl, fromEl, startIndex, newIndex) {
1312 1313
		sortable = (sortable || rootEl[expando]);

R
RubaXa 已提交
1314
		var evt = document.createEvent('Event'),
1315
			options = sortable.options,
R
RubaXa 已提交
1316 1317 1318 1319
			onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1);

		evt.initEvent(name, true, true);

J
Joey Becker 已提交
1320
		evt.to = toEl || rootEl;
R
RubaXa 已提交
1321 1322 1323 1324 1325 1326 1327
		evt.from = fromEl || rootEl;
		evt.item = targetEl || rootEl;
		evt.clone = cloneEl;

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

R
RubaXa 已提交
1328 1329
		rootEl.dispatchEvent(evt);

R
RubaXa 已提交
1330 1331 1332 1333 1334 1335
		if (options[onName]) {
			options[onName].call(sortable, evt);
		}
	}


1336
	function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvt, willInsertAfter) {
R
RubaXa 已提交
1337 1338 1339 1340 1341
		var evt,
			sortable = fromEl[expando],
			onMoveFn = sortable.options.onMove,
			retVal;

C
ChiefORZ 已提交
1342 1343
		evt = document.createEvent('Event');
		evt.initEvent('move', true, true);
R
RubaXa 已提交
1344

C
ChiefORZ 已提交
1345 1346 1347 1348 1349 1350
		evt.to = toEl;
		evt.from = fromEl;
		evt.dragged = dragEl;
		evt.draggedRect = dragRect;
		evt.related = targetEl || toEl;
		evt.relatedRect = targetRect || toEl.getBoundingClientRect();
1351
		evt.willInsertAfter = willInsertAfter;
R
RubaXa 已提交
1352

C
ChiefORZ 已提交
1353
		fromEl.dispatchEvent(evt);
R
RubaXa 已提交
1354

C
ChiefORZ 已提交
1355
		if (onMoveFn) {
1356
			retVal = onMoveFn.call(sortable, evt, originalEvt);
R
RubaXa 已提交
1357 1358
		}

R
RubaXa 已提交
1359
		return retVal;
R
RubaXa 已提交
1360 1361 1362
	}


R
RubaXa 已提交
1363
	function _disableDraggable(el) {
R
RubaXa 已提交
1364
		el.draggable = false;
R
RubaXa 已提交
1365 1366 1367
	}


R
RubaXa 已提交
1368
	function _unsilent() {
R
RubaXa 已提交
1369 1370 1371 1372
		_silent = false;
	}


R
RubaXa 已提交
1373
	/** @returns {HTMLElement|false} */
1374
	function _ghostIsLast(el, evt) {
R
RubaXa 已提交
1375
		var lastEl = el.lastElementChild,
R
RubaXa 已提交
1376
			rect = lastEl.getBoundingClientRect();
R
RubaXa 已提交
1377

R
RubaXa 已提交
1378
		// 5 — min delta
R
RubaXa 已提交
1379
		// abs — нельзя добавлять, а то глюки при наведении сверху
1380 1381
		return (evt.clientY - (rect.top + rect.height) > 5) ||
			(evt.clientX - (rect.left + rect.width) > 5);
R
RubaXa 已提交
1382 1383 1384
	}


1385 1386 1387 1388 1389 1390 1391
	/**
	 * Generate id
	 * @param   {HTMLElement} el
	 * @returns {String}
	 * @private
	 */
	function _generateId(el) {
R
RubaXa 已提交
1392
		var str = el.tagName + el.className + el.src + el.href + el.textContent,
1393
			i = str.length,
R
RubaXa 已提交
1394
			sum = 0;
1395

1396 1397 1398
		while (i--) {
			sum += str.charCodeAt(i);
		}
1399

1400 1401 1402
		return sum.toString(36);
	}

1403
	/**
1404 1405
	 * Returns the index of an element within its parent for a selected set of
	 * elements
1406
	 * @param  {HTMLElement} el
1407
	 * @param  {selector} selector
1408
	 * @return {number}
1409
	 */
1410
	function _index(el, selector) {
1411 1412
		var index = 0;

1413 1414 1415
		if (!el || !el.parentNode) {
			return -1;
		}
1416

1417
		while (el && (el = el.previousElementSibling)) {
R
RubaXa 已提交
1418
			if ((el.nodeName.toUpperCase() !== 'TEMPLATE') && (selector === '>*' || _matches(el, selector))) {
1419 1420
				index++;
			}
1421
		}
1422

1423 1424
		return index;
	}
R
RubaXa 已提交
1425

1426
	function _matches(/**HTMLElement*/el, /**String*/selector) {
1427 1428 1429 1430 1431 1432 1433 1434 1435 1436
		if (el) {
			selector = selector.split('.');

			var tag = selector.shift().toUpperCase(),
				re = new RegExp('\\s(' + selector.join('|') + ')(?=\\s)', 'g');

			return (
				(tag === '' || el.nodeName.toUpperCase() == tag) &&
				(!selector.length || ((' ' + el.className + ' ').match(re) || []).length == selector.length)
			);
1437
		}
1438 1439

		return false;
1440 1441
	}

R
RubaXa 已提交
1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462
	function _throttle(callback, ms) {
		var args, _this;

		return function () {
			if (args === void 0) {
				args = arguments;
				_this = this;

				setTimeout(function () {
					if (args.length === 1) {
						callback.call(_this, args[0]);
					} else {
						callback.apply(_this, args);
					}

					args = void 0;
				}, ms);
			}
		};
	}

1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474
	function _extend(dst, src) {
		if (dst && src) {
			for (var key in src) {
				if (src.hasOwnProperty(key)) {
					dst[key] = src[key];
				}
			}
		}

		return dst;
	}

1475
	function _clone(el) {
C
camargo 已提交
1476
		if (Polymer && Polymer.dom) {
C
camargo 已提交
1477
			return Polymer.dom(el).cloneNode(true);
C
camargo 已提交
1478 1479
		}
		else if ($) {
C
camargo 已提交
1480
			return $(el).clone(true)[0];
C
camargo 已提交
1481 1482
		}
		else {
C
camargo 已提交
1483
			return el.cloneNode(true);
C
camargo 已提交
1484
		}
1485 1486
	}

L
Lebedev Konstantin 已提交
1487
	function _saveInputCheckedState(root) {
1488 1489
		savedInputChecked.length = 0;
		
L
Lebedev Konstantin 已提交
1490 1491 1492 1493 1494 1495 1496 1497 1498
		var inputs = root.getElementsByTagName('input');
		var idx = inputs.length;

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

R
RubaXa 已提交
1499
	function _nextTick(fn) {
R
RubaXa 已提交
1500 1501 1502 1503 1504
		return setTimeout(fn, 0);
	}

	function _cancelNextTick(id) {
		return clearTimeout(id);
R
RubaXa 已提交
1505 1506
	}

C
camargo 已提交
1507
	// Fixed #973:
L
Lebedev Konstantin 已提交
1508 1509 1510 1511 1512 1513
	_on(document, 'touchmove', function (evt) {
		if (Sortable.active) {
			evt.preventDefault();
		}
	});

R
RubaXa 已提交
1514 1515 1516 1517 1518 1519
	// Export utils
	Sortable.utils = {
		on: _on,
		off: _off,
		css: _css,
		find: _find,
R
RubaXa 已提交
1520 1521 1522
		is: function (el, selector) {
			return !!_closest(el, selector, el);
		},
1523
		extend: _extend,
R
RubaXa 已提交
1524
		throttle: _throttle,
R
RubaXa 已提交
1525
		closest: _closest,
1526
		toggleClass: _toggleClass,
1527
		clone: _clone,
R
RubaXa 已提交
1528
		index: _index,
R
RubaXa 已提交
1529 1530
		nextTick: _nextTick,
		cancelNextTick: _cancelNextTick
R
RubaXa 已提交
1531 1532 1533
	};


1534 1535 1536 1537 1538 1539
	/**
	 * Create sortable instance
	 * @param {HTMLElement}  el
	 * @param {Object}      [options]
	 */
	Sortable.create = function (el, options) {
R
RubaXa 已提交
1540
		return new Sortable(el, options);
1541
	};
R
RubaXa 已提交
1542

R
RubaXa 已提交
1543 1544

	// Export
1545
	Sortable.version = '1.7.0';
1546
	return Sortable;
R
RubaXa 已提交
1547
});