Sortable.js 36.8 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,

58 59
		forRepaintDummy,

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

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

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

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

L
Lebedev Konstantin 已提交
74
		captureMode = false,
R
RubaXa 已提交
75
		passiveMode = false,
76

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

R
RubaXa 已提交
88
		_silent = false,
R
RubaXa 已提交
89

R
RubaXa 已提交
90
		abs = Math.abs,
R
RubaXa 已提交
91
		min = Math.min,
R
RubaXa 已提交
92

L
Lebedev Konstantin 已提交
93
		savedInputChecked = [],
94 95
		touchDragOverListeners = [],

96
		alwaysFalse = function () { return false; },
R
RubaXa 已提交
97

98 99 100
		_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 已提交
101 102
				var _this = rootEl[expando],
					el,
103 104 105 106 107 108 109 110 111 112 113
					rect,
					sens = options.scrollSensitivity,
					speed = options.scrollSpeed,

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

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

					vx,
114 115 116 117
					vy,

					scrollOffsetX,
					scrollOffsetY
D
desmaisons_david 已提交
118
				;
119 120 121 122 123

				// Delect scrollEl
				if (scrollParentEl !== rootEl) {
					scrollEl = options.scroll;
					scrollParentEl = rootEl;
124
					scrollCustomFn = options.scrollFn;
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 164 165

					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 () {
166 167 168
							scrollOffsetY = vy ? vy * speed : 0;
							scrollOffsetX = vx ? vx * speed : 0;

D
desmaisons_david 已提交
169
							if ('function' === typeof(scrollCustomFn)) {
N
Noel Heesen 已提交
170 171 172
								if (scrollCustomFn.call(_this, scrollOffsetX, scrollOffsetY, evt, touchEvt, el) !== 'continue') {
									return;
								}
173 174
							}

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

		_prepareGroup = function (options) {
R
RubaXa 已提交
188
			function toFn(value, pull) {
F
Flame2057 已提交
189 190
				if (value == null || value === false) {
					return alwaysFalse;
F
Flame2057 已提交
191 192 193 194
				} else if (pull && value === 'clone') {
					return function() {
						return value;
					};
F
Flame2057 已提交
195
				} else if (typeof value === 'function') {
R
RubaXa 已提交
196 197 198
					return value;
				} else {
					return function (to, from) {
F
Flame2057 已提交
199 200 201 202 203
						var otherGroup = (pull ? to : from).options.group.name;

						return value === true ||
						(typeof value === 'string' && value === otherGroup) ||
						(value.indexOf && value.indexOf(otherGroup) > -1);
R
RubaXa 已提交
204 205 206 207
					};
				}
			}

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,
N
Noel Heesen 已提交
263
      scroll: true,
R
RubaXa 已提交
264 265
			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,
281
			touchStartThreshold: parseInt(window.devicePixelRatio, 10) || 1,
C
ChiefORZ 已提交
282 283
			forceFallback: false,
			fallbackClass: 'sortable-fallback',
284
			fallbackOnBody: false,
285
			fallbackTolerance: 0,
R
RubaXa 已提交
286
			fallbackOffset: {x: 0, y: 0},
287
			supportPointer: Sortable.supportPointer !== false
R
RubaXa 已提交
288
		};
289

R
RubaXa 已提交
290

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

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

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

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

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

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

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

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


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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

431
					// Make the element draggable
432
					dragEl.draggable = _this.nativeDraggable;
433
					
434 435 436
					// Bind the events: dragstart/dragend
					_this._triggerDragStart(evt, touch);

437 438
					// Drag start event
					_dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, rootEl, oldIndex);
F
Flame2057 已提交
439

R
RubaXa 已提交
440
					// Chosen item
R
RubaXa 已提交
441
					_toggleClass(dragEl, options.chosenClass, true);
442 443
				};

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

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

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

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

471

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

475
		_delayedDragTouchMoveHandler: function (/** TouchEvent|PointerEvent **/e) {
476
			if (min(abs(e.clientX - this._lastX), abs(e.clientY - this._lastY)) >= this.options.touchStartThreshold) {
477 478 479 480
				this._disableDelayedDrag();
			}
		},

481 482
		_disableDelayedDrag: function () {
			var ownerDocument = this.el.ownerDocument;
483

484
			clearTimeout(this._dragStartTimer);
485 486 487
			_off(ownerDocument, 'mouseup', this._disableDelayedDrag);
			_off(ownerDocument, 'touchend', this._disableDelayedDrag);
			_off(ownerDocument, 'touchcancel', this._disableDelayedDrag);
488 489
			_off(ownerDocument, 'mousemove', this._disableDelayedDrag);
			_off(ownerDocument, 'touchmove', this._disableDelayedDrag);
490
			_off(ownerDocument, 'pointermove', this._disableDelayedDrag);
491
		},
R
RubaXa 已提交
492

493 494
		_triggerDragStart: function (/** Event */evt, /** Touch */touch) {
			touch = touch || (evt.pointerType == 'touch' ? evt : null);
495

496 497 498 499 500 501 502
			if (touch) {
				// Touch device support
				tapEvt = {
					target: dragEl,
					clientX: touch.clientX,
					clientY: touch.clientY
				};
503

504
				this._onDragStart(tapEvt, 'touch');
R
RubaXa 已提交
505
			}
R
RubaXa 已提交
506
			else if (!this.nativeDraggable) {
507 508 509 510 511
				this._onDragStart(tapEvt, true);
			}
			else {
				_on(dragEl, 'dragend', this);
				_on(rootEl, 'dragstart', this._onDragStart);
512 513
			}

514
			try {
515 516
				if (document.selection) {
					// Timeout neccessary for IE9
R
RubaXa 已提交
517
					_nextTick(function () {
518
						document.selection.empty();
519
					});
520 521
				} else {
					window.getSelection().removeAllRanges();
522
				}
523
			} catch (err) {
524
			}
525
		},
526

527 528
		_dragStarted: function () {
			if (rootEl && dragEl) {
R
RubaXa 已提交
529 530
				var options = this.options;

531
				// Apply effect
R
RubaXa 已提交
532 533
				_toggleClass(dragEl, options.ghostClass, true);
				_toggleClass(dragEl, options.dragClass, false);
R
RubaXa 已提交
534

535
				Sortable.active = this;
536

537
				// Drag start event
J
Joey Becker 已提交
538
				_dispatchEvent(this, rootEl, 'start', dragEl, rootEl, rootEl, oldIndex);
L
#1009  
Lebedev Konstantin 已提交
539 540
			} else {
				this._nulling();
541
			}
R
RubaXa 已提交
542 543
		},

R
RubaXa 已提交
544 545
		_emulateDragOver: function () {
			if (touchEvt) {
R
RubaXa 已提交
546
				if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY) {
547 548
					return;
				}
R
RubaXa 已提交
549 550 551

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

553 554 555
				if (!supportCssPointerEvents) {
					_css(ghostEl, 'display', 'none');
				}
R
RubaXa 已提交
556

L
Luiz Corte Real 已提交
557 558 559
				var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
				var parent = target;
				var i = touchDragOverListeners.length;
R
RubaXa 已提交
560

R
RubaXa 已提交
561
				while (target && target.shadowRoot) {
562
					target = target.shadowRoot.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
L
Luiz Corte Real 已提交
563
					parent = target;
564 565
				}

R
RubaXa 已提交
566 567
				if (parent) {
					do {
R
RubaXa 已提交
568
						if (parent[expando]) {
R
RubaXa 已提交
569 570 571 572 573 574 575 576 577 578 579 580 581
							while (i--) {
								touchDragOverListeners[i]({
									clientX: touchEvt.clientX,
									clientY: touchEvt.clientY,
									target: target,
									rootEl: parent
								});
							}

							break;
						}

						target = parent; // store last element
L
Larry Davis 已提交
582
					}
R
RubaXa 已提交
583 584
					/* jshint boss:true */
					while (parent = parent.parentNode);
R
RubaXa 已提交
585 586
				}

587 588 589
				if (!supportCssPointerEvents) {
					_css(ghostEl, 'display', '');
				}
R
RubaXa 已提交
590 591 592 593
			}
		},


R
RubaXa 已提交
594 595
		_onTouchMove: function (/**TouchEvent*/evt) {
			if (tapEvt) {
D
desmaisons_david 已提交
596
				var	options = this.options,
R
RubaXa 已提交
597
					fallbackTolerance = options.fallbackTolerance,
598
					fallbackOffset = options.fallbackOffset,
R
RubaXa 已提交
599
					touch = evt.touches ? evt.touches[0] : evt,
600 601
					dx = (touch.clientX - tapEvt.clientX) + fallbackOffset.x,
					dy = (touch.clientY - tapEvt.clientY) + fallbackOffset.y,
R
RubaXa 已提交
602
					translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';
S
sp-kilobug 已提交
603

604
				// only set the status to dragging, when we are actually dragging
R
RubaXa 已提交
605
				if (!Sortable.active) {
R
RubaXa 已提交
606 607 608 609
					if (fallbackTolerance &&
						min(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY)) < fallbackTolerance
					) {
						return;
S
sp-kilobug 已提交
610
					}
R
RubaXa 已提交
611

612 613
					this._dragStarted();
				}
R
RubaXa 已提交
614

615 616
				// as well as creating the ghost element on the document body
				this._appendGhost();
R
RubaXa 已提交
617

A
Alex Wild 已提交
618
				moved = true;
R
RubaXa 已提交
619
				touchEvt = touch;
R
RubaXa 已提交
620 621 622 623 624 625

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

M
Marius Petcu 已提交
626
				evt.preventDefault();
R
RubaXa 已提交
627 628 629
			}
		},

R
RubaXa 已提交
630 631
		_appendGhost: function () {
			if (!ghostEl) {
R
RubaXa 已提交
632 633
				var rect = dragEl.getBoundingClientRect(),
					css = _css(dragEl),
R
RubaXa 已提交
634
					options = this.options,
R
RubaXa 已提交
635
					ghostRect;
R
RubaXa 已提交
636

637
				ghostEl = dragEl.cloneNode(true);
R
RubaXa 已提交
638

R
RubaXa 已提交
639 640
				_toggleClass(ghostEl, options.ghostClass, false);
				_toggleClass(ghostEl, options.fallbackClass, true);
R
RubaXa 已提交
641
				_toggleClass(ghostEl, options.dragClass, true);
C
ChiefORZ 已提交
642

R
RubaXa 已提交
643 644
				_css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10));
				_css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10));
R
RubaXa 已提交
645 646
				_css(ghostEl, 'width', rect.width);
				_css(ghostEl, 'height', rect.height);
R
RubaXa 已提交
647 648 649
				_css(ghostEl, 'opacity', '0.8');
				_css(ghostEl, 'position', 'fixed');
				_css(ghostEl, 'zIndex', '100000');
650
				_css(ghostEl, 'pointerEvents', 'none');
R
RubaXa 已提交
651

R
RubaXa 已提交
652
				options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
R
RubaXa 已提交
653 654 655

				// Fixing dimensions.
				ghostRect = ghostEl.getBoundingClientRect();
R
RubaXa 已提交
656 657
				_css(ghostEl, 'width', rect.width * 2 - ghostRect.width);
				_css(ghostEl, 'height', rect.height * 2 - ghostRect.height);
658 659 660 661
			}
		},

		_onDragStart: function (/**Event*/evt, /**boolean*/useFallback) {
R
RubaXa 已提交
662 663
			var _this = this;
			var dataTransfer = evt.dataTransfer;
R
RubaXa 已提交
664
			var options = _this.options;
665

R
RubaXa 已提交
666
			_this._offUpEvents();
667

R
RubaXa 已提交
668
			if (activeGroup.checkPull(_this, _this, dragEl, evt)) {
669
				cloneEl = _clone(dragEl);
L
Lebedev Konstantin 已提交
670 671 672

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

674
				_css(cloneEl, 'display', 'none');
R
RubaXa 已提交
675
				_toggleClass(cloneEl, _this.options.chosenClass, false);
L
Lebedev Konstantin 已提交
676

R
RubaXa 已提交
677
				// #1143: IFrame support workaround
R
RubaXa 已提交
678 679 680
				_this._cloneId = _nextTick(function () {
					rootEl.insertBefore(cloneEl, dragEl);
					_dispatchEvent(_this, rootEl, 'clone', dragEl);
R
RubaXa 已提交
681
				});
682 683
			}

R
RubaXa 已提交
684 685
			_toggleClass(dragEl, options.dragClass, true);

686
			if (useFallback) {
R
RubaXa 已提交
687 688
				if (useFallback === 'touch') {
					// Bind touch events
R
RubaXa 已提交
689 690 691
					_on(document, 'touchmove', _this._onTouchMove);
					_on(document, 'touchend', _this._onDrop);
					_on(document, 'touchcancel', _this._onDrop);
R
RubaXa 已提交
692 693 694 695 696

					if (options.supportPointer) {
						_on(document, 'pointermove', _this._onTouchMove);
						_on(document, 'pointerup', _this._onDrop);
					}
R
RubaXa 已提交
697 698
				} else {
					// Old brwoser
R
RubaXa 已提交
699 700
					_on(document, 'mousemove', _this._onTouchMove);
					_on(document, 'mouseup', _this._onDrop);
R
RubaXa 已提交
701
				}
R
RubaXa 已提交
702

R
RubaXa 已提交
703
				_this._loopId = setInterval(_this._emulateDragOver, 50);
R
RubaXa 已提交
704 705
			}
			else {
R
RubaXa 已提交
706 707
				if (dataTransfer) {
					dataTransfer.effectAllowed = 'move';
R
RubaXa 已提交
708
					options.setData && options.setData.call(_this, dataTransfer, dragEl);
R
RubaXa 已提交
709
				}
R
RubaXa 已提交
710

R
RubaXa 已提交
711
				_on(document, 'drop', _this);
R
RubaXa 已提交
712 713

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

R
RubaXa 已提交
718
				_this._dragStartId = _nextTick(_this._dragStarted);
R
RubaXa 已提交
719 720 721
			}
		},

R
RubaXa 已提交
722
		_onDragOver: function (/**Event*/evt) {
R
RubaXa 已提交
723 724 725
			var el = this.el,
				target,
				dragRect,
R
#930  
RubaXa 已提交
726
				targetRect,
R
RubaXa 已提交
727 728 729
				revert,
				options = this.options,
				group = options.group,
R
RubaXa 已提交
730
				activeSortable = Sortable.active,
731
				isOwner = (activeGroup.name === group.name),
732
				isMovingBetweenSortable = false,
733
				canSort = options.sort;
R
RubaXa 已提交
734

R
RubaXa 已提交
735
			if (evt.preventDefault !== void 0) {
R
RubaXa 已提交
736
				evt.preventDefault();
737
				!options.dragoverBubble && evt.stopPropagation();
R
RubaXa 已提交
738
			}
R
RubaXa 已提交
739

740 741 742 743
			if (dragEl.animated) {
				return;
			}

A
Alex Wild 已提交
744
			moved = true;
F
Flame2057 已提交
745 746
			
			target = evt.target == el ? evt.target : _closest(evt.target, options.draggable, el);
A
Alex Wild 已提交
747

L
#1009  
Lebedev Konstantin 已提交
748
			if (activeSortable && !options.disabled &&
749
				(isOwner
R
RubaXa 已提交
750
					? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list
R
RubaXa 已提交
751 752
					: (
						putSortable === this ||
753 754 755 756
						(
							(activeSortable.lastPullMode = activeGroup.checkPull(this, activeSortable, dragEl, evt)) &&
							group.checkPut(this, activeSortable, dragEl, evt)
						)
R
RubaXa 已提交
757
					)
758
				) &&
R
RubaXa 已提交
759
				(evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback
R
RubaXa 已提交
760
			) {
R
RubaXa 已提交
761 762 763 764 765 766 767
				// Smart auto-scrolling
				_autoScroll(evt, options, this.el);

				if (_silent) {
					return;
				}

R
RubaXa 已提交
768
				dragRect = dragEl.getBoundingClientRect();
769 770

				if (putSortable !== this) {
D
desmaisons_david 已提交
771
					putSortable = this;
772 773
					isMovingBetweenSortable = true;
				}
R
RubaXa 已提交
774

775
				if (revert) {
L
Lebedev Konstantin 已提交
776
					_cloneHide(activeSortable, true);
R
RubaXa 已提交
777
					parentEl = rootEl; // actualization
R
RubaXa 已提交
778

779 780 781 782 783 784 785
					if (cloneEl || nextEl) {
						rootEl.insertBefore(dragEl, cloneEl || nextEl);
					}
					else if (!canSort) {
						rootEl.appendChild(dragEl);
					}

R
RubaXa 已提交
786 787
					return;
				}
R
RubaXa 已提交
788

R
RubaXa 已提交
789

R
RubaXa 已提交
790
				if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
791
					(el === evt.target) && (_ghostIsLast(el, evt))
R
RubaXa 已提交
792
				) {
793 794 795 796 797
					//assign target only if condition is true
					if (el.children.length !== 0 && el.children[0] !== ghostEl && el === evt.target) {
						target = el.lastElementChild;
					}

R
RubaXa 已提交
798 799 800 801
					if (target) {
						if (target.animated) {
							return;
						}
R
RubaXa 已提交
802

R
RubaXa 已提交
803 804
						targetRect = target.getBoundingClientRect();
					}
R
RubaXa 已提交
805

L
Lebedev Konstantin 已提交
806
					_cloneHide(activeSortable, isOwner);
R
RubaXa 已提交
807

808
					if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt) !== false) {
809 810
						if (!dragEl.contains(el)) {
							el.appendChild(dragEl);
R
RubaXa 已提交
811
							parentEl = el; // actualization
812
						}
R
RubaXa 已提交
813

R
RubaXa 已提交
814 815 816
						this._animate(dragRect, dragEl);
						target && this._animate(targetRect, target);
					}
R
RubaXa 已提交
817
				}
R
RubaXa 已提交
818 819
				else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) {
					if (lastEl !== target) {
R
RubaXa 已提交
820
						lastEl = target;
R
RubaXa 已提交
821
						lastCSS = _css(target);
S
sp-kilobug 已提交
822
						lastParentCSS = _css(target.parentNode);
R
RubaXa 已提交
823 824
					}

R
#930  
RubaXa 已提交
825
					targetRect = target.getBoundingClientRect();
R
RubaXa 已提交
826

R
#930  
RubaXa 已提交
827
					var width = targetRect.right - targetRect.left,
R
RubaXa 已提交
828
						height = targetRect.bottom - targetRect.top,
829
						floating = R_FLOAT.test(lastCSS.cssFloat + lastCSS.display)
S
sp-kilobug 已提交
830
							|| (lastParentCSS.display == 'flex' && lastParentCSS['flex-direction'].indexOf('row') === 0),
R
RubaXa 已提交
831 832 833 834
						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,
835
						after = false
D
desmaisons_david 已提交
836
					;
R
RubaXa 已提交
837

838 839 840
					if (floating) {
						var elTop = dragEl.offsetTop,
							tgTop = target.offsetTop;
R
RubaXa 已提交
841

842 843 844 845 846 847 848 849
						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 已提交
850
						} else if (!isMovingBetweenSortable) {
851 852
						after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
					}
R
RubaXa 已提交
853

854 855 856
					var moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, after);

					if (moveVector !== false) {
R
RubaXa 已提交
857 858 859
						if (moveVector === 1 || moveVector === -1) {
							after = (moveVector === 1);
						}
R
RubaXa 已提交
860

861 862 863 864
						_silent = true;
						setTimeout(_unsilent, 30);

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

866 867 868 869 870 871
						if (!dragEl.contains(el)) {
							if (after && !nextSibling) {
								el.appendChild(dragEl);
							} else {
								target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
							}
R
RubaXa 已提交
872
						}
R
RubaXa 已提交
873

R
RubaXa 已提交
874 875
						parentEl = dragEl.parentNode; // actualization

R
RubaXa 已提交
876 877 878
						this._animate(dragRect, dragEl);
						this._animate(targetRect, target);
					}
R
RubaXa 已提交
879 880 881 882
				}
			}
		},

883 884 885 886 887 888
		_animate: function (prevRect, target) {
			var ms = this.options.animation;

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

L
Lebedev Konstantin 已提交
889 890 891 892
				if (prevRect.nodeType === 1) {
					prevRect = prevRect.getBoundingClientRect();
				}

R
RubaXa 已提交
893
				_css(target, 'transition', 'none');
894 895 896 897 898
				_css(target, 'transform', 'translate3d('
					+ (prevRect.left - currentRect.left) + 'px,'
					+ (prevRect.top - currentRect.top) + 'px,0)'
				);

899
				forRepaintDummy = target.offsetWidth; // repaint
900

R
RubaXa 已提交
901
				_css(target, 'transition', 'all ' + ms + 'ms');
902 903
				_css(target, 'transform', 'translate3d(0,0,0)');

R
* anim  
RubaXa 已提交
904 905
				clearTimeout(target.animated);
				target.animated = setTimeout(function () {
906
					_css(target, 'transition', '');
907
					_css(target, 'transform', '');
908 909 910 911 912
					target.animated = false;
				}, ms);
			}
		},

913
		_offUpEvents: function () {
914 915
			var ownerDocument = this.el.ownerDocument;

916
			_off(document, 'touchmove', this._onTouchMove);
917
			_off(document, 'pointermove', this._onTouchMove);
918 919
			_off(ownerDocument, 'mouseup', this._onDrop);
			_off(ownerDocument, 'touchend', this._onDrop);
920
			_off(ownerDocument, 'pointerup', this._onDrop);
921
			_off(ownerDocument, 'touchcancel', this._onDrop);
진유정 已提交
922
			_off(ownerDocument, 'pointercancel', this._onDrop);
923
			_off(ownerDocument, 'selectstart', this);
924
		},
R
RubaXa 已提交
925

R
RubaXa 已提交
926
		_onDrop: function (/**Event*/evt) {
927 928
			var el = this.el,
				options = this.options;
R
RubaXa 已提交
929

R
RubaXa 已提交
930
			clearInterval(this._loopId);
R
RubaXa 已提交
931
			clearInterval(autoScroll.pid);
R
RubaXa 已提交
932
			clearTimeout(this._dragStartTimer);
933

R
RubaXa 已提交
934 935 936
			_cancelNextTick(this._cloneId);
			_cancelNextTick(this._dragStartId);

R
RubaXa 已提交
937
			// Unbind events
R
RubaXa 已提交
938
			_off(document, 'mouseover', this);
R
RubaXa 已提交
939
			_off(document, 'mousemove', this._onTouchMove);
940

R
RubaXa 已提交
941
			if (this.nativeDraggable) {
942 943 944
				_off(document, 'drop', this);
				_off(el, 'dragstart', this._onDragStart);
			}
R
RubaXa 已提交
945

946
			this._offUpEvents();
R
RubaXa 已提交
947

R
RubaXa 已提交
948
			if (evt) {
R
RubaXa 已提交
949
				if (moved) {
R
RubaXa 已提交
950
					evt.preventDefault();
C
ChiefORZ 已提交
951 952
					!options.dropBubble && evt.stopPropagation();
				}
R
RubaXa 已提交
953

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

L
Lebedev Konstantin 已提交
956 957
				if (rootEl === parentEl || Sortable.active.lastPullMode !== 'clone') {
					// Remove clone
B
Billy Hardy 已提交
958
					cloneEl && cloneEl.parentNode && cloneEl.parentNode.removeChild(cloneEl);
L
Lebedev Konstantin 已提交
959 960
				}

R
RubaXa 已提交
961
				if (dragEl) {
R
RubaXa 已提交
962
					if (this.nativeDraggable) {
963 964
						_off(dragEl, 'dragend', this);
					}
R
RubaXa 已提交
965

966
					_disableDraggable(dragEl);
L
Lebedev Konstantin 已提交
967
					dragEl.style['will-change'] = '';
R
RubaXa 已提交
968 969

					// Remove class's
R
RubaXa 已提交
970
					_toggleClass(dragEl, this.options.ghostClass, false);
R
RubaXa 已提交
971
					_toggleClass(dragEl, this.options.chosenClass, false);
R
RubaXa 已提交
972

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

976
					if (rootEl !== parentEl) {
977
						newIndex = _index(dragEl, options.draggable);
R
RubaXa 已提交
978

979
						if (newIndex >= 0) {
980
							// Add event
S
Sam Wray 已提交
981
							_dispatchEvent(null, parentEl, 'add', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
982

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

N
n00dl3 已提交
986
							// drag from one list and drop into another
S
Sam Wray 已提交
987 988
							_dispatchEvent(null, parentEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
							_dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
989
						}
R
RubaXa 已提交
990
					}
R
RubaXa 已提交
991 992 993
					else {
						if (dragEl.nextSibling !== nextEl) {
							// Get the index of the dragged element within its parent
994
							newIndex = _index(dragEl, options.draggable);
995 996

							if (newIndex >= 0) {
997
								// drag & drop within the same list
S
Sam Wray 已提交
998 999
								_dispatchEvent(this, rootEl, 'update', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
								_dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
1000
							}
R
RubaXa 已提交
1001
						}
R
RubaXa 已提交
1002
					}
1003

R
RubaXa 已提交
1004
					if (Sortable.active) {
1005
						/* jshint eqnull:true */
L
Lebedev Konstantin 已提交
1006
						if (newIndex == null || newIndex === -1) {
R
RubaXa 已提交
1007 1008 1009
							newIndex = oldIndex;
						}

S
Sam Wray 已提交
1010
						_dispatchEvent(this, rootEl, 'end', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
R
RubaXa 已提交
1011 1012 1013 1014

						// Save sorting
						this.save();
					}
R
RubaXa 已提交
1015
				}
R
RubaXa 已提交
1016

1017
			}
R
RubaXa 已提交
1018

1019 1020
			this._nulling();
		},
R
RubaXa 已提交
1021

D
desmaisons_david 已提交
1022
		_nulling: function() {
L
Lebedev Konstantin 已提交
1023
			rootEl =
D
desmaisons_david 已提交
1024 1025 1026 1027 1028 1029
			dragEl =
			parentEl =
			ghostEl =
			nextEl =
			cloneEl =
			lastDownEl =
R
RubaXa 已提交
1030

D
desmaisons_david 已提交
1031 1032
			scrollEl =
			scrollParentEl =
C
ChiefORZ 已提交
1033

D
desmaisons_david 已提交
1034 1035
			tapEvt =
			touchEvt =
R
RubaXa 已提交
1036

D
desmaisons_david 已提交
1037 1038
			moved =
			newIndex =
R
RubaXa 已提交
1039

D
desmaisons_david 已提交
1040 1041
			lastEl =
			lastCSS =
R
RubaXa 已提交
1042

D
desmaisons_david 已提交
1043 1044 1045
			putSortable =
			activeGroup =
			Sortable.active = null;
L
Lebedev Konstantin 已提交
1046 1047 1048 1049 1050

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

R
RubaXa 已提交
1053
		handleEvent: function (/**Event*/evt) {
1054 1055 1056 1057 1058 1059 1060 1061
			switch (evt.type) {
				case 'drop':
				case 'dragend':
					this._onDrop(evt);
					break;

				case 'dragover':
				case 'dragenter':
D
desmaisons_david 已提交
1062 1063 1064 1065
					if (dragEl) {
						this._onDragOver(evt);
						_globalDragOver(evt);
					}
1066
					break;
R
RubaXa 已提交
1067

R
RubaXa 已提交
1068 1069 1070 1071
				case 'mouseover':
					this._onDrop(evt);
					break;

1072 1073 1074
				case 'selectstart':
					evt.preventDefault();
					break;
R
RubaXa 已提交
1075
			}
R
RubaXa 已提交
1076 1077 1078
		},


1079 1080 1081 1082 1083 1084 1085 1086 1087
		/**
		 * Serializes the item into an array of string.
		 * @returns {String[]}
		 */
		toArray: function () {
			var order = [],
				el,
				children = this.el.children,
				i = 0,
R
RubaXa 已提交
1088 1089
				n = children.length,
				options = this.options;
1090 1091 1092

			for (; i < n; i++) {
				el = children[i];
R
RubaXa 已提交
1093
				if (_closest(el, options.draggable, this.el)) {
R
RubaXa 已提交
1094
					order.push(el.getAttribute(options.dataIdAttr) || _generateId(el));
R
RubaXa 已提交
1095
				}
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
			}

			return order;
		},


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

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

R
RubaXa 已提交
1112
				if (_closest(el, this.options.draggable, rootEl)) {
R
RubaXa 已提交
1113 1114 1115
					items[id] = el;
				}
			}, this);
1116 1117 1118

			order.forEach(function (id) {
				if (items[id]) {
R
RubaXa 已提交
1119 1120
					rootEl.removeChild(items[id]);
					rootEl.appendChild(items[id]);
1121 1122 1123 1124 1125
				}
			});
		},


R
RubaXa 已提交
1126 1127 1128 1129 1130 1131 1132 1133 1134
		/**
		 * Save the current sorting
		 */
		save: function () {
			var store = this.options.store;
			store && store.set(this);
		},


1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145
		/**
		 * 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);
		},


1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158
		/**
		 * 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 已提交
1159 1160 1161 1162

				if (name === 'group') {
					_prepareGroup(options);
				}
1163 1164 1165 1166
			}
		},


1167 1168 1169 1170
		/**
		 * Destroy
		 */
		destroy: function () {
1171
			var el = this.el;
R
RubaXa 已提交
1172

1173
			el[expando] = null;
1174

R
RubaXa 已提交
1175 1176
			_off(el, 'mousedown', this._onTapStart);
			_off(el, 'touchstart', this._onTapStart);
1177
			_off(el, 'pointerdown', this._onTapStart);
R
RubaXa 已提交
1178

R
RubaXa 已提交
1179
			if (this.nativeDraggable) {
1180 1181 1182
				_off(el, 'dragover', this);
				_off(el, 'dragenter', this);
			}
R
RubaXa 已提交
1183

1184
			// Remove draggable attributes
R
RubaXa 已提交
1185
			Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) {
1186 1187 1188
				el.removeAttribute('draggable');
			});

R
RubaXa 已提交
1189 1190 1191 1192
			touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1);

			this._onDrop();

1193
			this.el = el = null;
R
RubaXa 已提交
1194 1195 1196
		}
	};

1197

L
Lebedev Konstantin 已提交
1198
	function _cloneHide(sortable, state) {
1199 1200 1201 1202
		if (sortable.lastPullMode !== 'clone') {
			state = true;
		}

R
RubaXa 已提交
1203 1204
		if (cloneEl && (cloneEl.state !== state)) {
			_css(cloneEl, 'display', state ? 'none' : '');
L
Lebedev Konstantin 已提交
1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216

			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 已提交
1217 1218 1219 1220 1221
			cloneEl.state = state;
		}
	}


R
RubaXa 已提交
1222
	function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) {
R
RubaXa 已提交
1223
		if (el) {
R
RubaXa 已提交
1224 1225 1226
			ctx = ctx || document;

			do {
R
RubaXa 已提交
1227
				if ((selector === '>*' && el.parentNode === ctx) || _matches(el, selector)) {
R
RubaXa 已提交
1228
					return el;
R
RubaXa 已提交
1229
				}
R
#930  
RubaXa 已提交
1230 1231
				/* jshint boss:true */
			} while (el = _getParentOrHost(el));
R
RubaXa 已提交
1232 1233
		}

R
RubaXa 已提交
1234
		return null;
R
RubaXa 已提交
1235 1236 1237
	}


N
Nikita SVIRIDENKO 已提交
1238
	function _getParentOrHost(el) {
1239 1240 1241
		return (el.host && el !== document && el.host.nodeType)
			? el.host
			: el.parentNode;
N
Nikita SVIRIDENKO 已提交
1242
	}
1243 1244


1245
	function _globalDragOver(/**Event*/evt) {
1246 1247 1248
		if (evt.dataTransfer) {
			evt.dataTransfer.dropEffect = 'move';
		}
R
RubaXa 已提交
1249
		evt.preventDefault();
R
RubaXa 已提交
1250 1251 1252
	}


R
RubaXa 已提交
1253
	function _on(el, event, fn) {
1254
		el.addEventListener(event, fn, captureMode);
R
RubaXa 已提交
1255 1256 1257
	}


R
RubaXa 已提交
1258
	function _off(el, event, fn) {
1259
		el.removeEventListener(event, fn, captureMode);
R
RubaXa 已提交
1260 1261 1262
	}


R
RubaXa 已提交
1263 1264 1265
	function _toggleClass(el, name, state) {
		if (el) {
			if (el.classList) {
R
RubaXa 已提交
1266 1267 1268
				el.classList[state ? 'add' : 'remove'](name);
			}
			else {
1269 1270
				var className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' ');
				el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' ');
R
RubaXa 已提交
1271 1272 1273 1274 1275
			}
		}
	}


R
RubaXa 已提交
1276
	function _css(el, prop, val) {
R
RubaXa 已提交
1277 1278
		var style = el && el.style;

R
RubaXa 已提交
1279 1280 1281
		if (style) {
			if (val === void 0) {
				if (document.defaultView && document.defaultView.getComputedStyle) {
R
RubaXa 已提交
1282 1283
					val = document.defaultView.getComputedStyle(el, '');
				}
R
RubaXa 已提交
1284 1285
				else if (el.currentStyle) {
					val = el.currentStyle;
R
RubaXa 已提交
1286
				}
R
RubaXa 已提交
1287 1288 1289 1290 1291 1292 1293 1294 1295

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

				style[prop] = val + (typeof val === 'string' ? '' : 'px');
R
RubaXa 已提交
1296 1297 1298 1299 1300
			}
		}
	}


R
RubaXa 已提交
1301 1302
	function _find(ctx, tagName, iterator) {
		if (ctx) {
R
RubaXa 已提交
1303
			var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length;
R
RubaXa 已提交
1304

R
RubaXa 已提交
1305 1306
			if (iterator) {
				for (; i < n; i++) {
R
RubaXa 已提交
1307 1308 1309
					iterator(list[i], i);
				}
			}
R
RubaXa 已提交
1310

R
RubaXa 已提交
1311
			return list;
R
RubaXa 已提交
1312
		}
R
RubaXa 已提交
1313 1314

		return [];
R
RubaXa 已提交
1315 1316 1317
	}


R
RubaXa 已提交
1318

S
Sam Wray 已提交
1319
	function _dispatchEvent(sortable, rootEl, name, targetEl, toEl, fromEl, startIndex, newIndex, originalEvt) {
1320 1321
		sortable = (sortable || rootEl[expando]);

R
RubaXa 已提交
1322
		var evt = document.createEvent('Event'),
1323
			options = sortable.options,
R
RubaXa 已提交
1324 1325 1326 1327
			onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1);

		evt.initEvent(name, true, true);

J
Joey Becker 已提交
1328
		evt.to = toEl || rootEl;
R
RubaXa 已提交
1329 1330 1331 1332 1333 1334 1335
		evt.from = fromEl || rootEl;
		evt.item = targetEl || rootEl;
		evt.clone = cloneEl;

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

1336 1337
		evt.originalEvent = originalEvt;

R
RubaXa 已提交
1338 1339
		rootEl.dispatchEvent(evt);

R
RubaXa 已提交
1340 1341 1342 1343 1344 1345
		if (options[onName]) {
			options[onName].call(sortable, evt);
		}
	}


1346
	function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvt, willInsertAfter) {
R
RubaXa 已提交
1347 1348 1349 1350 1351
		var evt,
			sortable = fromEl[expando],
			onMoveFn = sortable.options.onMove,
			retVal;

C
ChiefORZ 已提交
1352 1353
		evt = document.createEvent('Event');
		evt.initEvent('move', true, true);
R
RubaXa 已提交
1354

C
ChiefORZ 已提交
1355 1356 1357 1358 1359 1360
		evt.to = toEl;
		evt.from = fromEl;
		evt.dragged = dragEl;
		evt.draggedRect = dragRect;
		evt.related = targetEl || toEl;
		evt.relatedRect = targetRect || toEl.getBoundingClientRect();
1361
		evt.willInsertAfter = willInsertAfter;
R
RubaXa 已提交
1362

1363 1364
		evt.originalEvent = originalEvt;

C
ChiefORZ 已提交
1365
		fromEl.dispatchEvent(evt);
R
RubaXa 已提交
1366

C
ChiefORZ 已提交
1367
		if (onMoveFn) {
1368
			retVal = onMoveFn.call(sortable, evt, originalEvt);
R
RubaXa 已提交
1369 1370
		}

R
RubaXa 已提交
1371
		return retVal;
R
RubaXa 已提交
1372 1373 1374
	}


R
RubaXa 已提交
1375
	function _disableDraggable(el) {
R
RubaXa 已提交
1376
		el.draggable = false;
R
RubaXa 已提交
1377 1378 1379
	}


R
RubaXa 已提交
1380
	function _unsilent() {
R
RubaXa 已提交
1381 1382 1383 1384
		_silent = false;
	}


R
RubaXa 已提交
1385
	/** @returns {HTMLElement|false} */
1386
	function _ghostIsLast(el, evt) {
R
RubaXa 已提交
1387
		var lastEl = el.lastElementChild,
R
RubaXa 已提交
1388
			rect = lastEl.getBoundingClientRect();
R
RubaXa 已提交
1389

R
RubaXa 已提交
1390
		// 5 — min delta
R
RubaXa 已提交
1391
		// abs — нельзя добавлять, а то глюки при наведении сверху
1392 1393
		return (evt.clientY - (rect.top + rect.height) > 5) ||
			(evt.clientX - (rect.left + rect.width) > 5);
R
RubaXa 已提交
1394 1395 1396
	}


1397 1398 1399 1400 1401 1402 1403
	/**
	 * Generate id
	 * @param   {HTMLElement} el
	 * @returns {String}
	 * @private
	 */
	function _generateId(el) {
R
RubaXa 已提交
1404
		var str = el.tagName + el.className + el.src + el.href + el.textContent,
1405
			i = str.length,
R
RubaXa 已提交
1406
			sum = 0;
1407

1408 1409 1410
		while (i--) {
			sum += str.charCodeAt(i);
		}
1411

1412 1413 1414
		return sum.toString(36);
	}

1415
	/**
1416 1417
	 * Returns the index of an element within its parent for a selected set of
	 * elements
1418
	 * @param  {HTMLElement} el
1419
	 * @param  {selector} selector
1420
	 * @return {number}
1421
	 */
1422
	function _index(el, selector) {
1423 1424
		var index = 0;

1425 1426 1427
		if (!el || !el.parentNode) {
			return -1;
		}
1428

1429
		while (el && (el = el.previousElementSibling)) {
R
RubaXa 已提交
1430
			if ((el.nodeName.toUpperCase() !== 'TEMPLATE') && (selector === '>*' || _matches(el, selector))) {
1431 1432
				index++;
			}
1433
		}
1434

1435 1436
		return index;
	}
R
RubaXa 已提交
1437

1438
	function _matches(/**HTMLElement*/el, /**String*/selector) {
1439
		if (el) {
S
Sam Wray 已提交
1440 1441
			try {
				if (el.matches) {
1442
					return el.matches(selector);
S
Sam Wray 已提交
1443
				} else if (el.msMatchesSelector) {
1444 1445
					return el.msMatchesSelector(selector);
				}
S
Sam Wray 已提交
1446 1447
			} catch(_) {
				return false;
1448
			}
1449
		}
1450 1451

		return false;
1452 1453
	}

R
RubaXa 已提交
1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474
	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);
			}
		};
	}

1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486
	function _extend(dst, src) {
		if (dst && src) {
			for (var key in src) {
				if (src.hasOwnProperty(key)) {
					dst[key] = src[key];
				}
			}
		}

		return dst;
	}

1487
	function _clone(el) {
C
camargo 已提交
1488
		if (Polymer && Polymer.dom) {
C
camargo 已提交
1489
			return Polymer.dom(el).cloneNode(true);
C
camargo 已提交
1490 1491
		}
		else if ($) {
C
camargo 已提交
1492
			return $(el).clone(true)[0];
C
camargo 已提交
1493 1494
		}
		else {
C
camargo 已提交
1495
			return el.cloneNode(true);
C
camargo 已提交
1496
		}
1497 1498
	}

L
Lebedev Konstantin 已提交
1499
	function _saveInputCheckedState(root) {
1500
		savedInputChecked.length = 0;
1501

L
Lebedev Konstantin 已提交
1502 1503 1504 1505 1506 1507 1508 1509 1510
		var inputs = root.getElementsByTagName('input');
		var idx = inputs.length;

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

R
RubaXa 已提交
1511
	function _nextTick(fn) {
R
RubaXa 已提交
1512 1513 1514 1515 1516
		return setTimeout(fn, 0);
	}

	function _cancelNextTick(id) {
		return clearTimeout(id);
R
RubaXa 已提交
1517 1518
	}

C
camargo 已提交
1519
	// Fixed #973:
L
Lebedev Konstantin 已提交
1520 1521 1522 1523 1524 1525
	_on(document, 'touchmove', function (evt) {
		if (Sortable.active) {
			evt.preventDefault();
		}
	});

R
RubaXa 已提交
1526 1527 1528 1529 1530 1531
	// Export utils
	Sortable.utils = {
		on: _on,
		off: _off,
		css: _css,
		find: _find,
R
RubaXa 已提交
1532 1533 1534
		is: function (el, selector) {
			return !!_closest(el, selector, el);
		},
1535
		extend: _extend,
R
RubaXa 已提交
1536
		throttle: _throttle,
R
RubaXa 已提交
1537
		closest: _closest,
1538
		toggleClass: _toggleClass,
1539
		clone: _clone,
R
RubaXa 已提交
1540
		index: _index,
R
RubaXa 已提交
1541 1542
		nextTick: _nextTick,
		cancelNextTick: _cancelNextTick
R
RubaXa 已提交
1543 1544 1545
	};


1546 1547 1548 1549 1550 1551
	/**
	 * Create sortable instance
	 * @param {HTMLElement}  el
	 * @param {Object}      [options]
	 */
	Sortable.create = function (el, options) {
R
RubaXa 已提交
1552
		return new Sortable(el, options);
1553
	};
R
RubaXa 已提交
1554

R
RubaXa 已提交
1555 1556

	// Export
1557
	Sortable.version = '1.7.0';
1558
	return Sortable;
R
RubaXa 已提交
1559
});