Sortable.js 36.7 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) {
R
RubaXa 已提交
189
				if (value == null || value === true) {
190
					value = group.name;
R
RubaXa 已提交
191 192 193
					if (value == null) {
						return alwaysFalse;
					}
R
RubaXa 已提交
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
				}

				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 已提交
212 213
			var group = {};
			var originalGroup = options.group;
R
RubaXa 已提交
214

R
RubaXa 已提交
215
			if (!originalGroup || typeof originalGroup != 'object') {
D
desmaisons_david 已提交
216
				originalGroup = {name: originalGroup};
R
RubaXa 已提交
217 218
			}

R
RubaXa 已提交
219 220 221
			group.name = originalGroup.name;
			group.checkPull = toFn(originalGroup.pull, true);
			group.checkPut = toFn(originalGroup.put);
L
Lebedev Konstantin 已提交
222
			group.revertClone = originalGroup.revertClone;
R
RubaXa 已提交
223 224

			options.group = group;
R
RubaXa 已提交
225
		}
D
desmaisons_david 已提交
226
	;
R
RubaXa 已提交
227

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

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

R
RubaXa 已提交
253
		this.el = el; // root element
254
		this.options = options = _extend({}, options);
R
RubaXa 已提交
255 256


257 258 259
		// Export instance
		el[expando] = this;

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

R
RubaXa 已提交
294

295 296
		// Set default options
		for (var name in defaults) {
R
RubaXa 已提交
297
			!(name in options) && (options[name] = defaults[name]);
298
		}
R
RubaXa 已提交
299

R
RubaXa 已提交
300
		_prepareGroup(options);
R
RubaXa 已提交
301

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

R
RubaXa 已提交
309 310
		// Setup drag mode
		this.nativeDraggable = options.forceFallback ? false : supportDraggable;
R
RubaXa 已提交
311 312 313 314

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

R
RubaXa 已提交
317
		if (this.nativeDraggable) {
318 319 320
			_on(el, 'dragover', this);
			_on(el, 'dragenter', this);
		}
R
RubaXa 已提交
321 322

		touchDragOverListeners.push(this._onDragOver);
323 324 325

		// Restore sorting
		options.store && this.sort(options.store.get(this));
R
RubaXa 已提交
326 327 328
	}


329
	Sortable.prototype = /** @lends Sortable.prototype */ {
R
RubaXa 已提交
330 331
		constructor: Sortable,

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

L
Lebedev Konstantin 已提交
344 345 346
			_saveInputCheckedState(el);


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

352
			if (/mousedown|pointerdown/.test(type) && evt.button !== 0 || options.disabled) {
353
				return; // only left button or enabled
R
RubaXa 已提交
354
			}
R
RubaXa 已提交
355

356 357 358 359
			// cancel dnd if original target is content editable
			if (originalTarget.isContentEditable) {
				return;
			}
360 361

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

363
			if (!target) {
364 365
				return;
			}
366

L
Lebedev Konstantin 已提交
367 368 369 370 371
			if (lastDownEl === target) {
				// Ignoring duplicate `down`
				return;
			}

R
RubaXa 已提交
372
			// Get the index of the dragged element within its parent
R
RubaXa 已提交
373
			startIndex = _index(target, options.draggable);
374 375 376 377

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

387
					if (criteria) {
J
Joey Becker 已提交
388
						_dispatchEvent(_this, criteria, 'filter', target, el, el, startIndex);
389 390 391 392 393
						return true;
					}
				});

				if (filter) {
L
Lebedev Konstantin 已提交
394
					preventOnFilter && evt.preventDefault();
395
					return; // cancel dnd
396 397 398
				}
			}

399 400 401 402
			if (options.handle && !_closest(originalTarget, options.handle, el)) {
				return;
			}

403
			// Prepare `dragstart`
R
RubaXa 已提交
404
			this._prepareDragStart(evt, touch, target, startIndex);
405 406
		},

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

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

417
				rootEl = el;
418
				dragEl = target;
R
RubaXa 已提交
419
				parentEl = dragEl.parentNode;
420
				nextEl = dragEl.nextSibling;
D
desmaisons_david 已提交
421
				lastDownEl = target;
422
				activeGroup = options.group;
R
RubaXa 已提交
423
				oldIndex = startIndex;
424

425 426
				this._lastX = (touch || evt).clientX;
				this._lastY = (touch || evt).clientY;
S
sp-kilobug 已提交
427

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

430 431 432 433
				dragStartFn = function () {
					// Delayed drag has been triggered
					// we can re-enable the events: touchmove/mousemove
					_this._disableDelayedDrag();
434

435
					// Make the element draggable
436
					dragEl.draggable = _this.nativeDraggable;
437

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

					// Bind the events: dragstart/dragend
R
RubaXa 已提交
442
					_this._triggerDragStart(evt, touch);
T
TaliLavi 已提交
443 444

					// Drag start event
J
Joey Becker 已提交
445
					_dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, rootEl, oldIndex);
446 447
				};

R
RubaXa 已提交
448 449 450 451 452
				// Disable "draggable"
				options.ignore.split(',').forEach(function (criteria) {
					_find(dragEl, criteria.trim(), _disableDraggable);
				});

453 454 455
				_on(ownerDocument, 'mouseup', _this._onDrop);
				_on(ownerDocument, 'touchend', _this._onDrop);
				_on(ownerDocument, 'touchcancel', _this._onDrop);
456
				_on(ownerDocument, 'selectstart', _this);
R
RubaXa 已提交
457
				options.supportPointer && _on(ownerDocument, 'pointercancel', _this._onDrop);
458 459

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

					_this._dragStartTimer = setTimeout(dragStartFn, options.delay);
				} else {
					dragStartFn();
473
				}
474

475

D
desmaisons_david 已提交
476
			}
477
		},
R
RubaXa 已提交
478

479 480 481 482 483 484
		_delayedDragTouchMoveHandler: function (/** TouchEvent|PointerEvent **/e) {
			if (min(abs(e.clientX - this._lastX), abs(e.clientY - this._lastY)) > this.options.touchMoveSensitivity) {
				this._disableDelayedDrag();
			}
		},

485 486
		_disableDelayedDrag: function () {
			var ownerDocument = this.el.ownerDocument;
487

488
			clearTimeout(this._dragStartTimer);
489 490 491
			_off(ownerDocument, 'mouseup', this._disableDelayedDrag);
			_off(ownerDocument, 'touchend', this._disableDelayedDrag);
			_off(ownerDocument, 'touchcancel', this._disableDelayedDrag);
492 493
			_off(ownerDocument, 'mousemove', this._disableDelayedDrag);
			_off(ownerDocument, 'touchmove', this._disableDelayedDrag);
494
			_off(ownerDocument, 'pointermove', this._disableDelayedDrag);
495
		},
R
RubaXa 已提交
496

497 498
		_triggerDragStart: function (/** Event */evt, /** Touch */touch) {
			touch = touch || (evt.pointerType == 'touch' ? evt : null);
499

500 501 502 503 504 505 506
			if (touch) {
				// Touch device support
				tapEvt = {
					target: dragEl,
					clientX: touch.clientX,
					clientY: touch.clientY
				};
507

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

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

531 532
		_dragStarted: function () {
			if (rootEl && dragEl) {
R
RubaXa 已提交
533 534
				var options = this.options;

535
				// Apply effect
R
RubaXa 已提交
536 537
				_toggleClass(dragEl, options.ghostClass, true);
				_toggleClass(dragEl, options.dragClass, false);
R
RubaXa 已提交
538

539
				Sortable.active = this;
540

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

R
RubaXa 已提交
548 549
		_emulateDragOver: function () {
			if (touchEvt) {
R
RubaXa 已提交
550
				if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY) {
551 552
					return;
				}
R
RubaXa 已提交
553 554 555

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

557 558 559
				if (!supportCssPointerEvents) {
					_css(ghostEl, 'display', 'none');
				}
R
RubaXa 已提交
560

L
Luiz Corte Real 已提交
561 562 563
				var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
				var parent = target;
				var i = touchDragOverListeners.length;
R
RubaXa 已提交
564

R
RubaXa 已提交
565
				while (target && target.shadowRoot) {
566
					target = target.shadowRoot.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
L
Luiz Corte Real 已提交
567
					parent = target;
568 569
				}

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

							break;
						}

						target = parent; // store last element
L
Larry Davis 已提交
586
					}
R
RubaXa 已提交
587 588
					/* jshint boss:true */
					while (parent = parent.parentNode);
R
RubaXa 已提交
589 590
				}

591 592 593
				if (!supportCssPointerEvents) {
					_css(ghostEl, 'display', '');
				}
R
RubaXa 已提交
594 595 596 597
			}
		},


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

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

616 617
					this._dragStarted();
				}
R
RubaXa 已提交
618

619 620
				// as well as creating the ghost element on the document body
				this._appendGhost();
R
RubaXa 已提交
621

A
Alex Wild 已提交
622
				moved = true;
R
RubaXa 已提交
623
				touchEvt = touch;
R
RubaXa 已提交
624 625 626 627 628 629

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

M
Marius Petcu 已提交
630
				evt.preventDefault();
R
RubaXa 已提交
631 632 633
			}
		},

R
RubaXa 已提交
634 635
		_appendGhost: function () {
			if (!ghostEl) {
R
RubaXa 已提交
636 637
				var rect = dragEl.getBoundingClientRect(),
					css = _css(dragEl),
R
RubaXa 已提交
638
					options = this.options,
R
RubaXa 已提交
639
					ghostRect;
R
RubaXa 已提交
640

641
				ghostEl = dragEl.cloneNode(true);
R
RubaXa 已提交
642

R
RubaXa 已提交
643 644
				_toggleClass(ghostEl, options.ghostClass, false);
				_toggleClass(ghostEl, options.fallbackClass, true);
R
RubaXa 已提交
645
				_toggleClass(ghostEl, options.dragClass, true);
C
ChiefORZ 已提交
646

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

R
RubaXa 已提交
656
				options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
R
RubaXa 已提交
657 658 659

				// Fixing dimensions.
				ghostRect = ghostEl.getBoundingClientRect();
R
RubaXa 已提交
660 661
				_css(ghostEl, 'width', rect.width * 2 - ghostRect.width);
				_css(ghostEl, 'height', rect.height * 2 - ghostRect.height);
662 663 664 665
			}
		},

		_onDragStart: function (/**Event*/evt, /**boolean*/useFallback) {
R
RubaXa 已提交
666 667
			var _this = this;
			var dataTransfer = evt.dataTransfer;
R
RubaXa 已提交
668
			var options = _this.options;
669

R
RubaXa 已提交
670
			_this._offUpEvents();
671

R
RubaXa 已提交
672
			if (activeGroup.checkPull(_this, _this, dragEl, evt)) {
673
				cloneEl = _clone(dragEl);
L
Lebedev Konstantin 已提交
674 675 676

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

678
				_css(cloneEl, 'display', 'none');
R
RubaXa 已提交
679
				_toggleClass(cloneEl, _this.options.chosenClass, false);
L
Lebedev Konstantin 已提交
680

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

R
RubaXa 已提交
688 689
			_toggleClass(dragEl, options.dragClass, true);

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

					if (options.supportPointer) {
						_on(document, 'pointermove', _this._onTouchMove);
						_on(document, 'pointerup', _this._onDrop);
					}
R
RubaXa 已提交
701 702
				} else {
					// Old brwoser
R
RubaXa 已提交
703 704
					_on(document, 'mousemove', _this._onTouchMove);
					_on(document, 'mouseup', _this._onDrop);
R
RubaXa 已提交
705
				}
R
RubaXa 已提交
706

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

R
RubaXa 已提交
715
				_on(document, 'drop', _this);
R
RubaXa 已提交
716 717

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

R
RubaXa 已提交
722
				_this._dragStartId = _nextTick(_this._dragStarted);
R
RubaXa 已提交
723 724 725
			}
		},

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

R
RubaXa 已提交
739
			if (evt.preventDefault !== void 0) {
R
RubaXa 已提交
740
				evt.preventDefault();
741
				!options.dragoverBubble && evt.stopPropagation();
R
RubaXa 已提交
742
			}
R
RubaXa 已提交
743

744 745 746 747
			if (dragEl.animated) {
				return;
			}

A
Alex Wild 已提交
748 749
			moved = true;

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

				if (_silent) {
					return;
				}

R
RubaXa 已提交
770
				target = _closest(evt.target, options.draggable, el);
R
RubaXa 已提交
771
				dragRect = dragEl.getBoundingClientRect();
772 773

				if (putSortable !== this) {
D
desmaisons_david 已提交
774
					putSortable = this;
775 776
					isMovingBetweenSortable = true;
				}
R
RubaXa 已提交
777

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

782 783 784 785 786 787 788
					if (cloneEl || nextEl) {
						rootEl.insertBefore(dragEl, cloneEl || nextEl);
					}
					else if (!canSort) {
						rootEl.appendChild(dragEl);
					}

R
RubaXa 已提交
789 790
					return;
				}
R
RubaXa 已提交
791

R
RubaXa 已提交
792

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

R
RubaXa 已提交
801 802 803 804
					if (target) {
						if (target.animated) {
							return;
						}
R
RubaXa 已提交
805

R
RubaXa 已提交
806 807
						targetRect = target.getBoundingClientRect();
					}
R
RubaXa 已提交
808

L
Lebedev Konstantin 已提交
809
					_cloneHide(activeSortable, isOwner);
R
RubaXa 已提交
810

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

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

R
#930  
RubaXa 已提交
828
					targetRect = target.getBoundingClientRect();
R
RubaXa 已提交
829

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

841 842 843
					if (floating) {
						var elTop = dragEl.offsetTop,
							tgTop = target.offsetTop;
R
RubaXa 已提交
844

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

857 858 859
					var moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, after);

					if (moveVector !== false) {
R
RubaXa 已提交
860 861 862
						if (moveVector === 1 || moveVector === -1) {
							after = (moveVector === 1);
						}
R
RubaXa 已提交
863

864 865 866 867
						_silent = true;
						setTimeout(_unsilent, 30);

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

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

R
RubaXa 已提交
877 878
						parentEl = dragEl.parentNode; // actualization

R
RubaXa 已提交
879 880 881
						this._animate(dragRect, dragEl);
						this._animate(targetRect, target);
					}
R
RubaXa 已提交
882 883 884 885
				}
			}
		},

886 887 888 889 890 891
		_animate: function (prevRect, target) {
			var ms = this.options.animation;

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

L
Lebedev Konstantin 已提交
892 893 894 895
				if (prevRect.nodeType === 1) {
					prevRect = prevRect.getBoundingClientRect();
				}

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

902
				forRepaintDummy = target.offsetWidth; // repaint
903

R
RubaXa 已提交
904
				_css(target, 'transition', 'all ' + ms + 'ms');
905 906
				_css(target, 'transform', 'translate3d(0,0,0)');

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

916
		_offUpEvents: function () {
917 918
			var ownerDocument = this.el.ownerDocument;

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

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

R
RubaXa 已提交
933
			clearInterval(this._loopId);
R
RubaXa 已提交
934
			clearInterval(autoScroll.pid);
R
RubaXa 已提交
935
			clearTimeout(this._dragStartTimer);
936

R
RubaXa 已提交
937 938 939
			_cancelNextTick(this._cloneId);
			_cancelNextTick(this._dragStartId);

R
RubaXa 已提交
940
			// Unbind events
R
RubaXa 已提交
941
			_off(document, 'mouseover', this);
R
RubaXa 已提交
942
			_off(document, 'mousemove', this._onTouchMove);
943

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

949
			this._offUpEvents();
R
RubaXa 已提交
950

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

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

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

R
RubaXa 已提交
964
				if (dragEl) {
R
RubaXa 已提交
965
					if (this.nativeDraggable) {
966 967
						_off(dragEl, 'dragend', this);
					}
R
RubaXa 已提交
968

969
					_disableDraggable(dragEl);
L
Lebedev Konstantin 已提交
970
					dragEl.style['will-change'] = '';
R
RubaXa 已提交
971 972

					// Remove class's
R
RubaXa 已提交
973
					_toggleClass(dragEl, this.options.ghostClass, false);
R
RubaXa 已提交
974
					_toggleClass(dragEl, this.options.chosenClass, false);
R
RubaXa 已提交
975

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

979
					if (rootEl !== parentEl) {
980
						newIndex = _index(dragEl, options.draggable);
R
RubaXa 已提交
981

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

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

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

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

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

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

						// Save sorting
						this.save();
					}
R
RubaXa 已提交
1018
				}
R
RubaXa 已提交
1019

1020
			}
R
RubaXa 已提交
1021

1022 1023
			this._nulling();
		},
R
RubaXa 已提交
1024

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

D
desmaisons_david 已提交
1034 1035
			scrollEl =
			scrollParentEl =
C
ChiefORZ 已提交
1036

D
desmaisons_david 已提交
1037 1038
			tapEvt =
			touchEvt =
R
RubaXa 已提交
1039

D
desmaisons_david 已提交
1040 1041
			moved =
			newIndex =
R
RubaXa 已提交
1042

D
desmaisons_david 已提交
1043 1044
			lastEl =
			lastCSS =
R
RubaXa 已提交
1045

D
desmaisons_david 已提交
1046 1047 1048
			putSortable =
			activeGroup =
			Sortable.active = null;
L
Lebedev Konstantin 已提交
1049 1050 1051 1052 1053

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

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

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

R
RubaXa 已提交
1071 1072 1073 1074
				case 'mouseover':
					this._onDrop(evt);
					break;

1075 1076 1077
				case 'selectstart':
					evt.preventDefault();
					break;
R
RubaXa 已提交
1078
			}
R
RubaXa 已提交
1079 1080 1081
		},


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

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

			return order;
		},


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

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

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

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


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


1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148
		/**
		 * 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);
		},


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

				if (name === 'group') {
					_prepareGroup(options);
				}
1166 1167 1168 1169
			}
		},


1170 1171 1172 1173
		/**
		 * Destroy
		 */
		destroy: function () {
1174
			var el = this.el;
R
RubaXa 已提交
1175

1176
			el[expando] = null;
1177

R
RubaXa 已提交
1178 1179
			_off(el, 'mousedown', this._onTapStart);
			_off(el, 'touchstart', this._onTapStart);
1180
			_off(el, 'pointerdown', this._onTapStart);
R
RubaXa 已提交
1181

R
RubaXa 已提交
1182
			if (this.nativeDraggable) {
1183 1184 1185
				_off(el, 'dragover', this);
				_off(el, 'dragenter', this);
			}
R
RubaXa 已提交
1186

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

R
RubaXa 已提交
1192 1193 1194 1195
			touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1);

			this._onDrop();

1196
			this.el = el = null;
R
RubaXa 已提交
1197 1198 1199
		}
	};

1200

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

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

			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 已提交
1220 1221 1222 1223 1224
			cloneEl.state = state;
		}
	}


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

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

R
RubaXa 已提交
1237
		return null;
R
RubaXa 已提交
1238 1239 1240
	}


N
Nikita SVIRIDENKO 已提交
1241 1242 1243 1244 1245
	function _getParentOrHost(el) {
		var parent = el.host;

		return (parent && parent.nodeType) ? parent : el.parentNode;
	}
1246 1247


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


R
RubaXa 已提交
1256
	function _on(el, event, fn) {
1257
		el.addEventListener(event, fn, captureMode);
R
RubaXa 已提交
1258 1259 1260
	}


R
RubaXa 已提交
1261
	function _off(el, event, fn) {
1262
		el.removeEventListener(event, fn, captureMode);
R
RubaXa 已提交
1263 1264 1265
	}


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


R
RubaXa 已提交
1279
	function _css(el, prop, val) {
R
RubaXa 已提交
1280 1281
		var style = el && el.style;

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

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

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


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

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

R
RubaXa 已提交
1314
			return list;
R
RubaXa 已提交
1315
		}
R
RubaXa 已提交
1316 1317

		return [];
R
RubaXa 已提交
1318 1319 1320
	}


R
RubaXa 已提交
1321

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

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

		evt.initEvent(name, true, true);

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

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

1339 1340
		evt.originalEvent = originalEvt;

R
RubaXa 已提交
1341 1342
		rootEl.dispatchEvent(evt);

R
RubaXa 已提交
1343 1344 1345 1346 1347 1348
		if (options[onName]) {
			options[onName].call(sortable, evt);
		}
	}


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

C
ChiefORZ 已提交
1355 1356
		evt = document.createEvent('Event');
		evt.initEvent('move', true, true);
R
RubaXa 已提交
1357

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

1366 1367
		evt.originalEvent = originalEvt;

C
ChiefORZ 已提交
1368
		fromEl.dispatchEvent(evt);
R
RubaXa 已提交
1369

C
ChiefORZ 已提交
1370
		if (onMoveFn) {
1371
			retVal = onMoveFn.call(sortable, evt, originalEvt);
R
RubaXa 已提交
1372 1373
		}

R
RubaXa 已提交
1374
		return retVal;
R
RubaXa 已提交
1375 1376 1377
	}


R
RubaXa 已提交
1378
	function _disableDraggable(el) {
R
RubaXa 已提交
1379
		el.draggable = false;
R
RubaXa 已提交
1380 1381 1382
	}


R
RubaXa 已提交
1383
	function _unsilent() {
R
RubaXa 已提交
1384 1385 1386 1387
		_silent = false;
	}


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

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


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

1411 1412 1413
		while (i--) {
			sum += str.charCodeAt(i);
		}
1414

1415 1416 1417
		return sum.toString(36);
	}

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

1428 1429 1430
		if (!el || !el.parentNode) {
			return -1;
		}
1431

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

1438 1439
		return index;
	}
R
RubaXa 已提交
1440

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

		return false;
1455 1456
	}

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

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

		return dst;
	}

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

L
Lebedev Konstantin 已提交
1502
	function _saveInputCheckedState(root) {
1503
		savedInputChecked.length = 0;
1504

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

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

R
RubaXa 已提交
1514
	function _nextTick(fn) {
R
RubaXa 已提交
1515 1516 1517 1518 1519
		return setTimeout(fn, 0);
	}

	function _cancelNextTick(id) {
		return clearTimeout(id);
R
RubaXa 已提交
1520 1521
	}

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

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


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

R
RubaXa 已提交
1558 1559

	// Export
1560
	Sortable.version = '1.7.0';
1561
	return Sortable;
R
RubaXa 已提交
1562
});