Sortable.js 33.4 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
	}
28 29 30 31 32 33 34 35 36 37
	
	var supportsPassive = false;
	try {
		var opts = Object.defineProperty({}, 'passive', {
			get: function() {
			supportsPassive = true;
			}
		});
		window.addEventListener("test", null, opts);
	} catch (e) {}
R
RubaXa 已提交
38

R
RubaXa 已提交
39
	var dragEl,
40
		parentEl,
R
RubaXa 已提交
41 42 43 44
		ghostEl,
		cloneEl,
		rootEl,
		nextEl,
L
Lebedev Konstantin 已提交
45
		lastDownEl,
R
RubaXa 已提交
46

47 48
		scrollEl,
		scrollParentEl,
49
		scrollCustomFn,
50

R
RubaXa 已提交
51 52
		lastEl,
		lastCSS,
S
sp-kilobug 已提交
53
		lastParentCSS,
R
RubaXa 已提交
54

R
RubaXa 已提交
55 56 57
		oldIndex,
		newIndex,

R
RubaXa 已提交
58
		activeGroup,
R
RubaXa 已提交
59 60
		putSortable,

R
RubaXa 已提交
61
		autoScroll = {},
R
RubaXa 已提交
62

R
RubaXa 已提交
63 64
		tapEvt,
		touchEvt,
R
RubaXa 已提交
65

C
ChiefORZ 已提交
66 67
		moved,

R
RubaXa 已提交
68 69 70
		/** @const */
		RSPACE = /\s+/g,

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

R
RubaXa 已提交
73 74 75
		win = window,
		document = win.document,
		parseInt = win.parseInt,
R
RubaXa 已提交
76

R
* UPD  
RubaXa 已提交
77
		$ = win.jQuery || win.Zepto,
78 79
		Polymer = win.Polymer,

80
		captureMode = supportsPassive ? {capture: false, passive: false} : false,
81

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

R
RubaXa 已提交
93
		_silent = false,
R
RubaXa 已提交
94

R
RubaXa 已提交
95
		abs = Math.abs,
R
RubaXa 已提交
96
		min = Math.min,
R
RubaXa 已提交
97

98 99 100 101 102
		touchDragOverListeners = [],

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

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

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

					vx,
116 117 118 119
					vy,

					scrollOffsetX,
					scrollOffsetY
120 121 122 123 124 125
				;

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

					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 () {
168 169 170 171 172 173 174
							scrollOffsetY = vy ? vy * speed : 0;
							scrollOffsetX = vx ? vx * speed : 0;

							if ('function' === typeof(scrollCustomFn)) {
								return scrollCustomFn.call(_this, scrollOffsetX, scrollOffsetY, evt);
							}

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) {
L
Lebedev Konstantin 已提交
189
				if (value === void 0 || value === true) {
190
					value = group.name;
R
RubaXa 已提交
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
				}

				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 已提交
209 210
			var group = {};
			var originalGroup = options.group;
R
RubaXa 已提交
211

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

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

			options.group = group;
R
RubaXa 已提交
222
		}
R
RubaXa 已提交
223 224 225
	;


226

R
RubaXa 已提交
227 228 229
	/**
	 * @class  Sortable
	 * @param  {HTMLElement}  el
230
	 * @param  {Object}       [options]
R
RubaXa 已提交
231
	 */
R
RubaXa 已提交
232
	function Sortable(el, options) {
R
RubaXa 已提交
233 234 235 236
		if (!(el && el.nodeType && el.nodeType === 1)) {
			throw 'Sortable: `el` must be HTMLElement, and not ' + {}.toString.call(el);
		}

R
RubaXa 已提交
237
		this.el = el; // root element
238
		this.options = options = _extend({}, options);
R
RubaXa 已提交
239 240


241 242 243
		// Export instance
		el[expando] = this;

R
RubaXa 已提交
244
		// Default options
245 246
		var defaults = {
			group: Math.random(),
R
RubaXa 已提交
247
			sort: true,
R
RubaXa 已提交
248
			disabled: false,
249 250
			store: null,
			handle: null,
R
RubaXa 已提交
251 252 253
			scroll: true,
			scrollSensitivity: 30,
			scrollSpeed: 10,
R
RubaXa 已提交
254
			draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*',
255
			ghostClass: 'sortable-ghost',
R
RubaXa 已提交
256
			chosenClass: 'sortable-chosen',
R
RubaXa 已提交
257
			dragClass: 'sortable-drag',
258
			ignore: 'a, img',
259
			filter: null,
L
Lebedev Konstantin 已提交
260
			preventOnFilter: true,
R
RubaXa 已提交
261 262 263
			animation: 0,
			setData: function (dataTransfer, dragEl) {
				dataTransfer.setData('Text', dragEl.textContent);
264 265
			},
			dropBubble: false,
R
RubaXa 已提交
266
			dragoverBubble: false,
267
			dataIdAttr: 'data-id',
268
			delay: 0,
C
ChiefORZ 已提交
269 270
			forceFallback: false,
			fallbackClass: 'sortable-fallback',
271
			fallbackOnBody: false,
272 273
			fallbackTolerance: 0,
			fallbackOffset: {x: 0, y: 0}
R
RubaXa 已提交
274
		};
275

R
RubaXa 已提交
276

277 278
		// Set default options
		for (var name in defaults) {
R
RubaXa 已提交
279
			!(name in options) && (options[name] = defaults[name]);
280
		}
R
RubaXa 已提交
281

R
RubaXa 已提交
282
		_prepareGroup(options);
R
RubaXa 已提交
283

R
* JSDoc  
RubaXa 已提交
284
		// Bind all private methods
R
RubaXa 已提交
285
		for (var fn in this) {
D
dev101 已提交
286
			if (fn.charAt(0) === '_' && typeof this[fn] === 'function') {
287
				this[fn] = this[fn].bind(this);
R
RubaXa 已提交
288 289 290
			}
		}

R
RubaXa 已提交
291 292
		// Setup drag mode
		this.nativeDraggable = options.forceFallback ? false : supportDraggable;
R
RubaXa 已提交
293 294 295 296

		// Bind events
		_on(el, 'mousedown', this._onTapStart);
		_on(el, 'touchstart', this._onTapStart);
297
		_on(el, 'pointerdown', this._onTapStart);
R
RubaXa 已提交
298

R
RubaXa 已提交
299
		if (this.nativeDraggable) {
300 301 302
			_on(el, 'dragover', this);
			_on(el, 'dragenter', this);
		}
R
RubaXa 已提交
303 304

		touchDragOverListeners.push(this._onDragOver);
305 306 307

		// Restore sorting
		options.store && this.sort(options.store.get(this));
R
RubaXa 已提交
308 309 310
	}


311
	Sortable.prototype = /** @lends Sortable.prototype */ {
R
RubaXa 已提交
312 313
		constructor: Sortable,

314
		_onTapStart: function (/** Event|TouchEvent */evt) {
315 316
			var _this = this,
				el = this.el,
317
				options = this.options,
318
				preventOnFilter = options.preventOnFilter,
319 320 321
				type = evt.type,
				touch = evt.touches && evt.touches[0],
				target = (touch || evt).target,
322
				originalTarget = evt.target.shadowRoot && evt.path[0] || target,
R
RubaXa 已提交
323 324
				filter = options.filter,
				startIndex;
R
RubaXa 已提交
325 326

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

331 332
			if (type === 'mousedown' && evt.button !== 0 || options.disabled) {
				return; // only left button or enabled
R
RubaXa 已提交
333
			}
R
RubaXa 已提交
334

335 336

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

338
			if (!target) {
339 340
				return;
			}
341

L
Lebedev Konstantin 已提交
342 343 344 345 346
			if (lastDownEl === target) {
				// Ignoring duplicate `down`
				return;
			}

R
RubaXa 已提交
347
			// Get the index of the dragged element within its parent
R
RubaXa 已提交
348
			startIndex = _index(target, options.draggable);
349 350 351 352

			// Check filter
			if (typeof filter === 'function') {
				if (filter.call(this, evt, target, this)) {
R
RubaXa 已提交
353
					_dispatchEvent(_this, originalTarget, 'filter', target, el, startIndex);
L
Lebedev Konstantin 已提交
354
					preventOnFilter && evt.preventDefault();
355 356
					return; // cancel dnd
				}
357
			}
358 359 360
			else if (filter) {
				filter = filter.split(',').some(function (criteria) {
					criteria = _closest(originalTarget, criteria.trim(), el);
361

362
					if (criteria) {
R
RubaXa 已提交
363
						_dispatchEvent(_this, criteria, 'filter', target, el, startIndex);
364 365 366 367 368
						return true;
					}
				});

				if (filter) {
L
Lebedev Konstantin 已提交
369
					preventOnFilter && evt.preventDefault();
370
					return; // cancel dnd
371 372 373
				}
			}

374 375 376 377
			if (options.handle && !_closest(originalTarget, options.handle, el)) {
				return;
			}

378
			// Prepare `dragstart`
R
RubaXa 已提交
379
			this._prepareDragStart(evt, touch, target, startIndex);
380 381
		},

R
RubaXa 已提交
382
		_prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target, /** Number */startIndex) {
383 384 385 386 387
			var _this = this,
				el = _this.el,
				options = _this.options,
				ownerDocument = el.ownerDocument,
				dragStartFn;
388 389 390 391

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

392
				rootEl = el;
393
				dragEl = target;
R
RubaXa 已提交
394
				parentEl = dragEl.parentNode;
395
				nextEl = dragEl.nextSibling;
L
Lebedev Konstantin 已提交
396
				lastDownEl = target
397
				activeGroup = options.group;
R
RubaXa 已提交
398
				oldIndex = startIndex;
399

400 401
				this._lastX = (touch || evt).clientX;
				this._lastY = (touch || evt).clientY;
S
sp-kilobug 已提交
402

L
Lebedev Konstantin 已提交
403 404
				dragEl.style['will-change'] = 'transform';

405 406 407 408
				dragStartFn = function () {
					// Delayed drag has been triggered
					// we can re-enable the events: touchmove/mousemove
					_this._disableDelayedDrag();
409

410
					// Make the element draggable
411
					dragEl.draggable = _this.nativeDraggable;
412

R
RubaXa 已提交
413
					// Chosen item
R
RubaXa 已提交
414
					_toggleClass(dragEl, options.chosenClass, true);
415 416

					// Bind the events: dragstart/dragend
R
RubaXa 已提交
417
					_this._triggerDragStart(evt, touch);
T
TaliLavi 已提交
418 419

					// Drag start event
420
					_dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, oldIndex);
421 422
				};

R
RubaXa 已提交
423 424 425 426 427
				// Disable "draggable"
				options.ignore.split(',').forEach(function (criteria) {
					_find(dragEl, criteria.trim(), _disableDraggable);
				});

428 429 430
				_on(ownerDocument, 'mouseup', _this._onDrop);
				_on(ownerDocument, 'touchend', _this._onDrop);
				_on(ownerDocument, 'touchcancel', _this._onDrop);
431
				_on(ownerDocument, 'pointercancel', _this._onDrop);
432 433

				if (options.delay) {
434 435
					// If the user moves the pointer or let go the click or touch
					// before the delay has been reached:
436
					// disable the delayed drag
437 438 439
					_on(ownerDocument, 'mouseup', _this._disableDelayedDrag);
					_on(ownerDocument, 'touchend', _this._disableDelayedDrag);
					_on(ownerDocument, 'touchcancel', _this._disableDelayedDrag);
440 441
					_on(ownerDocument, 'mousemove', _this._disableDelayedDrag);
					_on(ownerDocument, 'touchmove', _this._disableDelayedDrag);
442
					_on(ownerDocument, 'pointermove', _this._disableDelayedDrag);
443 444 445 446

					_this._dragStartTimer = setTimeout(dragStartFn, options.delay);
				} else {
					dragStartFn();
447
				}
448 449 450 451

				if (options.forceFallback) {
					evt.preventDefault();
				}
452 453
			}
		},
R
RubaXa 已提交
454

455 456
		_disableDelayedDrag: function () {
			var ownerDocument = this.el.ownerDocument;
457

458
			clearTimeout(this._dragStartTimer);
459 460 461
			_off(ownerDocument, 'mouseup', this._disableDelayedDrag);
			_off(ownerDocument, 'touchend', this._disableDelayedDrag);
			_off(ownerDocument, 'touchcancel', this._disableDelayedDrag);
462 463
			_off(ownerDocument, 'mousemove', this._disableDelayedDrag);
			_off(ownerDocument, 'touchmove', this._disableDelayedDrag);
464
			_off(ownerDocument, 'pointermove', this._disableDelayedDrag);
465
		},
R
RubaXa 已提交
466

467 468
		_triggerDragStart: function (/** Event */evt, /** Touch */touch) {
			touch = touch || (evt.pointerType == 'touch' ? evt : null);
469 470 471 472 473 474 475
			if (touch) {
				// Touch device support
				tapEvt = {
					target: dragEl,
					clientX: touch.clientX,
					clientY: touch.clientY
				};
476

477
				this._onDragStart(tapEvt, 'touch');
R
RubaXa 已提交
478
			}
R
RubaXa 已提交
479
			else if (!this.nativeDraggable) {
480 481 482 483 484
				this._onDragStart(tapEvt, true);
			}
			else {
				_on(dragEl, 'dragend', this);
				_on(rootEl, 'dragstart', this._onDragStart);
485 486
			}

487
			try {
488 489 490 491 492
				if (document.selection) {					
					// Timeout neccessary for IE9					
					setTimeout(function () {
						document.selection.empty();
					});					
493 494
				} else {
					window.getSelection().removeAllRanges();
495
				}
496
			} catch (err) {
497
			}
498
		},
499

500 501
		_dragStarted: function () {
			if (rootEl && dragEl) {
R
RubaXa 已提交
502 503
				var options = this.options;

504
				// Apply effect
R
RubaXa 已提交
505 506
				_toggleClass(dragEl, options.ghostClass, true);
				_toggleClass(dragEl, options.dragClass, false);
R
RubaXa 已提交
507

508
				Sortable.active = this;
509

510
				// Drag start event
511
				_dispatchEvent(this, rootEl, 'start', dragEl, rootEl, oldIndex);
L
#1009  
Lebedev Konstantin 已提交
512 513
			} else {
				this._nulling();
514
			}
R
RubaXa 已提交
515 516
		},

R
RubaXa 已提交
517 518
		_emulateDragOver: function () {
			if (touchEvt) {
R
RubaXa 已提交
519
				if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY) {
520 521
					return;
				}
R
RubaXa 已提交
522 523 524

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

526 527 528
				if (!supportCssPointerEvents) {
					_css(ghostEl, 'display', 'none');
				}
R
RubaXa 已提交
529

R
RubaXa 已提交
530
				var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY),
R
RubaXa 已提交
531
					parent = target,
R
RubaXa 已提交
532
					i = touchDragOverListeners.length;
R
RubaXa 已提交
533

R
RubaXa 已提交
534 535
				if (parent) {
					do {
R
RubaXa 已提交
536
						if (parent[expando]) {
R
RubaXa 已提交
537 538 539 540 541 542 543 544 545 546 547 548 549
							while (i--) {
								touchDragOverListeners[i]({
									clientX: touchEvt.clientX,
									clientY: touchEvt.clientY,
									target: target,
									rootEl: parent
								});
							}

							break;
						}

						target = parent; // store last element
L
Larry Davis 已提交
550
					}
R
RubaXa 已提交
551 552
					/* jshint boss:true */
					while (parent = parent.parentNode);
R
RubaXa 已提交
553 554
				}

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


R
RubaXa 已提交
562 563
		_onTouchMove: function (/**TouchEvent*/evt) {
			if (tapEvt) {
R
RubaXa 已提交
564 565
				var	options = this.options,
					fallbackTolerance = options.fallbackTolerance,
566
					fallbackOffset = options.fallbackOffset,
R
RubaXa 已提交
567
					touch = evt.touches ? evt.touches[0] : evt,
568 569
					dx = (touch.clientX - tapEvt.clientX) + fallbackOffset.x,
					dy = (touch.clientY - tapEvt.clientY) + fallbackOffset.y,
R
RubaXa 已提交
570
					translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';
S
sp-kilobug 已提交
571

572
				// only set the status to dragging, when we are actually dragging
R
RubaXa 已提交
573
				if (!Sortable.active) {
R
RubaXa 已提交
574 575 576 577
					if (fallbackTolerance &&
						min(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY)) < fallbackTolerance
					) {
						return;
S
sp-kilobug 已提交
578
					}
R
RubaXa 已提交
579

580 581
					this._dragStarted();
				}
R
RubaXa 已提交
582

583 584
				// as well as creating the ghost element on the document body
				this._appendGhost();
R
RubaXa 已提交
585

A
Alex Wild 已提交
586
				moved = true;
R
RubaXa 已提交
587
				touchEvt = touch;
R
RubaXa 已提交
588 589 590 591 592 593

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

M
Marius Petcu 已提交
594
				evt.preventDefault();
R
RubaXa 已提交
595 596 597
			}
		},

R
RubaXa 已提交
598 599
		_appendGhost: function () {
			if (!ghostEl) {
R
RubaXa 已提交
600 601
				var rect = dragEl.getBoundingClientRect(),
					css = _css(dragEl),
R
RubaXa 已提交
602
					options = this.options,
R
RubaXa 已提交
603
					ghostRect;
R
RubaXa 已提交
604

605
				ghostEl = dragEl.cloneNode(true);
R
RubaXa 已提交
606

R
RubaXa 已提交
607 608
				_toggleClass(ghostEl, options.ghostClass, false);
				_toggleClass(ghostEl, options.fallbackClass, true);
R
RubaXa 已提交
609
				_toggleClass(ghostEl, options.dragClass, true);
C
ChiefORZ 已提交
610

R
RubaXa 已提交
611 612
				_css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10));
				_css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10));
R
RubaXa 已提交
613 614
				_css(ghostEl, 'width', rect.width);
				_css(ghostEl, 'height', rect.height);
R
RubaXa 已提交
615 616 617
				_css(ghostEl, 'opacity', '0.8');
				_css(ghostEl, 'position', 'fixed');
				_css(ghostEl, 'zIndex', '100000');
618
				_css(ghostEl, 'pointerEvents', 'none');
R
RubaXa 已提交
619

R
RubaXa 已提交
620
				options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
R
RubaXa 已提交
621 622 623

				// Fixing dimensions.
				ghostRect = ghostEl.getBoundingClientRect();
R
RubaXa 已提交
624 625
				_css(ghostEl, 'width', rect.width * 2 - ghostRect.width);
				_css(ghostEl, 'height', rect.height * 2 - ghostRect.height);
626 627 628 629 630 631 632 633 634
			}
		},

		_onDragStart: function (/**Event*/evt, /**boolean*/useFallback) {
			var dataTransfer = evt.dataTransfer,
				options = this.options;

			this._offUpEvents();

635
			if (activeGroup.checkPull(this, this, dragEl, evt)) {
636
				cloneEl = _clone(dragEl);
L
Lebedev Konstantin 已提交
637 638 639

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

641
				_css(cloneEl, 'display', 'none');
L
Lebedev Konstantin 已提交
642
				_toggleClass(cloneEl, this.options.chosenClass, false);
L
Lebedev Konstantin 已提交
643

644
				rootEl.insertBefore(cloneEl, dragEl);
R
RubaXa 已提交
645
				_dispatchEvent(this, rootEl, 'clone', dragEl);
646 647
			}

R
RubaXa 已提交
648 649
			_toggleClass(dragEl, options.dragClass, true);

650
			if (useFallback) {
R
RubaXa 已提交
651 652 653 654 655
				if (useFallback === 'touch') {
					// Bind touch events
					_on(document, 'touchmove', this._onTouchMove);
					_on(document, 'touchend', this._onDrop);
					_on(document, 'touchcancel', this._onDrop);
656 657
					_on(document, 'pointermove', this._onTouchMove);
					_on(document, 'pointerup', this._onDrop);
R
RubaXa 已提交
658 659 660 661 662
				} else {
					// Old brwoser
					_on(document, 'mousemove', this._onTouchMove);
					_on(document, 'mouseup', this._onDrop);
				}
R
RubaXa 已提交
663

664
				this._loopId = setInterval(this._emulateDragOver, 50);
R
RubaXa 已提交
665 666
			}
			else {
R
RubaXa 已提交
667 668 669 670
				if (dataTransfer) {
					dataTransfer.effectAllowed = 'move';
					options.setData && options.setData.call(this, dataTransfer, dragEl);
				}
R
RubaXa 已提交
671

R
RubaXa 已提交
672
				_on(document, 'drop', this);
673
				setTimeout(this._dragStarted, 0);
R
RubaXa 已提交
674 675 676
			}
		},

R
RubaXa 已提交
677
		_onDragOver: function (/**Event*/evt) {
R
RubaXa 已提交
678 679 680
			var el = this.el,
				target,
				dragRect,
R
#930  
RubaXa 已提交
681
				targetRect,
R
RubaXa 已提交
682 683 684
				revert,
				options = this.options,
				group = options.group,
R
RubaXa 已提交
685
				activeSortable = Sortable.active,
686 687
				isOwner = (activeGroup === group),
				canSort = options.sort;
R
RubaXa 已提交
688

R
RubaXa 已提交
689 690
			if (evt.preventDefault !== void 0) {
				evt.preventDefault();
691
				!options.dragoverBubble && evt.stopPropagation();
R
RubaXa 已提交
692
			}
R
RubaXa 已提交
693

A
Alex Wild 已提交
694 695
			moved = true;

L
#1009  
Lebedev Konstantin 已提交
696
			if (activeSortable && !options.disabled &&
697
				(isOwner
R
RubaXa 已提交
698
					? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list
R
RubaXa 已提交
699 700
					: (
						putSortable === this ||
701 702 703 704
						(
							(activeSortable.lastPullMode = activeGroup.checkPull(this, activeSortable, dragEl, evt)) &&
							group.checkPut(this, activeSortable, dragEl, evt)
						)
R
RubaXa 已提交
705
					)
706
				) &&
R
RubaXa 已提交
707
				(evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback
R
RubaXa 已提交
708
			) {
R
RubaXa 已提交
709 710 711 712 713 714 715
				// Smart auto-scrolling
				_autoScroll(evt, options, this.el);

				if (_silent) {
					return;
				}

R
RubaXa 已提交
716
				target = _closest(evt.target, options.draggable, el);
R
RubaXa 已提交
717
				dragRect = dragEl.getBoundingClientRect();
R
RubaXa 已提交
718
				putSortable = this;
R
RubaXa 已提交
719

720
				if (revert) {
L
Lebedev Konstantin 已提交
721
					_cloneHide(activeSortable, true);
R
RubaXa 已提交
722
					parentEl = rootEl; // actualization
R
RubaXa 已提交
723

724 725 726 727 728 729 730
					if (cloneEl || nextEl) {
						rootEl.insertBefore(dragEl, cloneEl || nextEl);
					}
					else if (!canSort) {
						rootEl.appendChild(dragEl);
					}

R
RubaXa 已提交
731 732
					return;
				}
R
RubaXa 已提交
733

R
RubaXa 已提交
734

R
RubaXa 已提交
735
				if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
736
					(el === evt.target) && (target = _ghostIsLast(el, evt))
R
RubaXa 已提交
737
				) {
R
RubaXa 已提交
738 739 740 741
					if (target) {
						if (target.animated) {
							return;
						}
R
RubaXa 已提交
742

R
RubaXa 已提交
743 744
						targetRect = target.getBoundingClientRect();
					}
R
RubaXa 已提交
745

L
Lebedev Konstantin 已提交
746
					_cloneHide(activeSortable, isOwner);
R
RubaXa 已提交
747

748
					if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt) !== false) {
749 750
						if (!dragEl.contains(el)) {
							el.appendChild(dragEl);
R
RubaXa 已提交
751
							parentEl = el; // actualization
752
						}
R
RubaXa 已提交
753

R
RubaXa 已提交
754 755 756
						this._animate(dragRect, dragEl);
						target && this._animate(targetRect, target);
					}
R
RubaXa 已提交
757
				}
R
RubaXa 已提交
758 759
				else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) {
					if (lastEl !== target) {
R
RubaXa 已提交
760
						lastEl = target;
R
RubaXa 已提交
761
						lastCSS = _css(target);
S
sp-kilobug 已提交
762
						lastParentCSS = _css(target.parentNode);
R
RubaXa 已提交
763 764
					}

R
#930  
RubaXa 已提交
765
					targetRect = target.getBoundingClientRect();
R
RubaXa 已提交
766

R
#930  
RubaXa 已提交
767
					var width = targetRect.right - targetRect.left,
R
RubaXa 已提交
768
						height = targetRect.bottom - targetRect.top,
S
sp-kilobug 已提交
769 770
						floating = /left|right|inline/.test(lastCSS.cssFloat + lastCSS.display)
							|| (lastParentCSS.display == 'flex' && lastParentCSS['flex-direction'].indexOf('row') === 0),
R
RubaXa 已提交
771 772 773 774
						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,
775
						moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt),
R
RubaXa 已提交
776
						after
R
RubaXa 已提交
777
					;
R
RubaXa 已提交
778

R
RubaXa 已提交
779
					if (moveVector !== false) {
R
RubaXa 已提交
780 781
						_silent = true;
						setTimeout(_unsilent, 30);
R
RubaXa 已提交
782

L
Lebedev Konstantin 已提交
783
						_cloneHide(activeSortable, isOwner);
R
RubaXa 已提交
784

R
RubaXa 已提交
785 786 787 788
						if (moveVector === 1 || moveVector === -1) {
							after = (moveVector === 1);
						}
						else if (floating) {
S
sp-kilobug 已提交
789 790
							var elTop = dragEl.offsetTop,
								tgTop = target.offsetTop;
R
RubaXa 已提交
791 792

							if (elTop === tgTop) {
S
sp-kilobug 已提交
793
								after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide;
794
							}
795
							else if (target.previousElementSibling === dragEl || dragEl.previousElementSibling === target) {
796
								after = (evt.clientY - targetRect.top) / height > 0.5;
S
sp-kilobug 已提交
797 798 799
							} else {
								after = tgTop > elTop;
							}
R
RubaXa 已提交
800 801 802
						} else {
							after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
						}
R
RubaXa 已提交
803

804 805 806 807 808 809
						if (!dragEl.contains(el)) {
							if (after && !nextSibling) {
								el.appendChild(dragEl);
							} else {
								target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
							}
R
RubaXa 已提交
810
						}
R
RubaXa 已提交
811

R
RubaXa 已提交
812 813
						parentEl = dragEl.parentNode; // actualization

R
RubaXa 已提交
814 815 816
						this._animate(dragRect, dragEl);
						this._animate(targetRect, target);
					}
R
RubaXa 已提交
817 818 819 820
				}
			}
		},

821 822 823 824 825 826
		_animate: function (prevRect, target) {
			var ms = this.options.animation;

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

L
Lebedev Konstantin 已提交
827 828 829 830
				if (prevRect.nodeType === 1) {
					prevRect = prevRect.getBoundingClientRect();
				}

R
RubaXa 已提交
831
				_css(target, 'transition', 'none');
832 833 834 835 836 837 838
				_css(target, 'transform', 'translate3d('
					+ (prevRect.left - currentRect.left) + 'px,'
					+ (prevRect.top - currentRect.top) + 'px,0)'
				);

				target.offsetWidth; // repaint

R
RubaXa 已提交
839
				_css(target, 'transition', 'all ' + ms + 'ms');
840 841
				_css(target, 'transform', 'translate3d(0,0,0)');

R
* anim  
RubaXa 已提交
842 843
				clearTimeout(target.animated);
				target.animated = setTimeout(function () {
844
					_css(target, 'transition', '');
845
					_css(target, 'transform', '');
846 847 848 849 850
					target.animated = false;
				}, ms);
			}
		},

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

854
			_off(document, 'touchmove', this._onTouchMove);
855
			_off(document, 'pointermove', this._onTouchMove);
856 857
			_off(ownerDocument, 'mouseup', this._onDrop);
			_off(ownerDocument, 'touchend', this._onDrop);
858
			_off(ownerDocument, 'pointerup', this._onDrop);
859
			_off(ownerDocument, 'touchcancel', this._onDrop);
860
		},
R
RubaXa 已提交
861

R
RubaXa 已提交
862
		_onDrop: function (/**Event*/evt) {
863 864
			var el = this.el,
				options = this.options;
R
RubaXa 已提交
865

R
RubaXa 已提交
866
			clearInterval(this._loopId);
R
RubaXa 已提交
867
			clearInterval(autoScroll.pid);
R
RubaXa 已提交
868
			clearTimeout(this._dragStartTimer);
869

R
RubaXa 已提交
870
			// Unbind events
R
RubaXa 已提交
871
			_off(document, 'mousemove', this._onTouchMove);
872

R
RubaXa 已提交
873
			if (this.nativeDraggable) {
874 875 876
				_off(document, 'drop', this);
				_off(el, 'dragstart', this._onDragStart);
			}
R
RubaXa 已提交
877

878
			this._offUpEvents();
R
RubaXa 已提交
879

R
RubaXa 已提交
880
			if (evt) {
R
RubaXa 已提交
881
				if (moved) {
C
ChiefORZ 已提交
882 883 884
					evt.preventDefault();
					!options.dropBubble && evt.stopPropagation();
				}
R
RubaXa 已提交
885

R
RubaXa 已提交
886
				ghostEl && ghostEl.parentNode.removeChild(ghostEl);
R
RubaXa 已提交
887

R
RubaXa 已提交
888
				if (dragEl) {
R
RubaXa 已提交
889
					if (this.nativeDraggable) {
890 891
						_off(dragEl, 'dragend', this);
					}
R
RubaXa 已提交
892

893
					_disableDraggable(dragEl);
L
Lebedev Konstantin 已提交
894
					dragEl.style['will-change'] = '';
R
RubaXa 已提交
895 896

					// Remove class's
R
RubaXa 已提交
897
					_toggleClass(dragEl, this.options.ghostClass, false);
R
RubaXa 已提交
898
					_toggleClass(dragEl, this.options.chosenClass, false);
R
RubaXa 已提交
899

900
					if (rootEl !== parentEl) {
901
						newIndex = _index(dragEl, options.draggable);
R
RubaXa 已提交
902

903
						if (newIndex >= 0) {
R
RubaXa 已提交
904

905 906
							// Add event
							_dispatchEvent(null, parentEl, 'add', dragEl, rootEl, oldIndex, newIndex);
907

908 909
							// Remove event
							_dispatchEvent(this, rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex);
N
n00dl3 已提交
910

N
n00dl3 已提交
911 912 913
							// drag from one list and drop into another
							_dispatchEvent(null, parentEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
							_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
914
						}
R
RubaXa 已提交
915
					}
R
RubaXa 已提交
916 917
					else {
						// Remove clone
R
RubaXa 已提交
918
						cloneEl && cloneEl.parentNode.removeChild(cloneEl);
R
RubaXa 已提交
919

R
RubaXa 已提交
920 921
						if (dragEl.nextSibling !== nextEl) {
							// Get the index of the dragged element within its parent
922
							newIndex = _index(dragEl, options.draggable);
923 924

							if (newIndex >= 0) {
925 926 927 928
								// drag & drop within the same list
								_dispatchEvent(this, rootEl, 'update', dragEl, rootEl, oldIndex, newIndex);
								_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
							}
R
RubaXa 已提交
929
						}
R
RubaXa 已提交
930
					}
931

R
RubaXa 已提交
932
					if (Sortable.active) {
933
						/* jshint eqnull:true */
L
Lebedev Konstantin 已提交
934
						if (newIndex == null || newIndex === -1) {
R
RubaXa 已提交
935 936 937
							newIndex = oldIndex;
						}

R
RubaXa 已提交
938 939 940 941 942
						_dispatchEvent(this, rootEl, 'end', dragEl, rootEl, oldIndex, newIndex);

						// Save sorting
						this.save();
					}
R
RubaXa 已提交
943
				}
R
RubaXa 已提交
944

945
			}
R
RubaXa 已提交
946

947 948
			this._nulling();
		},
R
RubaXa 已提交
949

950
		_nulling: function() {
L
Lebedev Konstantin 已提交
951 952 953 954 955 956
			rootEl =
			dragEl =
			parentEl =
			ghostEl =
			nextEl =
			cloneEl =
L
Lebedev Konstantin 已提交
957
			lastDownEl =
R
RubaXa 已提交
958

L
Lebedev Konstantin 已提交
959 960
			scrollEl =
			scrollParentEl =
C
ChiefORZ 已提交
961

L
Lebedev Konstantin 已提交
962 963
			tapEvt =
			touchEvt =
R
RubaXa 已提交
964

L
Lebedev Konstantin 已提交
965 966
			moved =
			newIndex =
R
RubaXa 已提交
967

L
Lebedev Konstantin 已提交
968 969
			lastEl =
			lastCSS =
R
RubaXa 已提交
970

L
Lebedev Konstantin 已提交
971 972 973
			putSortable =
			activeGroup =
			Sortable.active = null;
974
		},
R
RubaXa 已提交
975

R
RubaXa 已提交
976 977 978
		handleEvent: function (/**Event*/evt) {
			var type = evt.type;

R
RubaXa 已提交
979
			if (type === 'dragover' || type === 'dragenter') {
R
RubaXa 已提交
980 981 982 983
				if (dragEl) {
					this._onDragOver(evt);
					_globalDragOver(evt);
				}
R
RubaXa 已提交
984
			}
R
RubaXa 已提交
985
			else if (type === 'drop' || type === 'dragend') {
R
RubaXa 已提交
986 987
				this._onDrop(evt);
			}
R
RubaXa 已提交
988 989 990
		},


991 992 993 994 995 996 997 998 999
		/**
		 * Serializes the item into an array of string.
		 * @returns {String[]}
		 */
		toArray: function () {
			var order = [],
				el,
				children = this.el.children,
				i = 0,
R
RubaXa 已提交
1000 1001
				n = children.length,
				options = this.options;
1002 1003 1004

			for (; i < n; i++) {
				el = children[i];
R
RubaXa 已提交
1005
				if (_closest(el, options.draggable, this.el)) {
R
RubaXa 已提交
1006
					order.push(el.getAttribute(options.dataIdAttr) || _generateId(el));
R
RubaXa 已提交
1007
				}
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
			}

			return order;
		},


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

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

R
RubaXa 已提交
1024
				if (_closest(el, this.options.draggable, rootEl)) {
R
RubaXa 已提交
1025 1026 1027
					items[id] = el;
				}
			}, this);
1028 1029 1030

			order.forEach(function (id) {
				if (items[id]) {
R
RubaXa 已提交
1031 1032
					rootEl.removeChild(items[id]);
					rootEl.appendChild(items[id]);
1033 1034 1035 1036 1037
				}
			});
		},


R
RubaXa 已提交
1038 1039 1040 1041 1042 1043 1044 1045 1046
		/**
		 * Save the current sorting
		 */
		save: function () {
			var store = this.options.store;
			store && store.set(this);
		},


1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
		/**
		 * 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);
		},


1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
		/**
		 * 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 已提交
1071 1072 1073 1074

				if (name === 'group') {
					_prepareGroup(options);
				}
1075 1076 1077 1078
			}
		},


1079 1080 1081 1082
		/**
		 * Destroy
		 */
		destroy: function () {
1083
			var el = this.el;
R
RubaXa 已提交
1084

1085
			el[expando] = null;
1086

R
RubaXa 已提交
1087 1088
			_off(el, 'mousedown', this._onTapStart);
			_off(el, 'touchstart', this._onTapStart);
1089
			_off(el, 'pointerdown', this._onTapStart);
R
RubaXa 已提交
1090

R
RubaXa 已提交
1091
			if (this.nativeDraggable) {
1092 1093 1094
				_off(el, 'dragover', this);
				_off(el, 'dragenter', this);
			}
R
RubaXa 已提交
1095

1096
			// Remove draggable attributes
R
RubaXa 已提交
1097
			Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) {
1098 1099 1100
				el.removeAttribute('draggable');
			});

R
RubaXa 已提交
1101 1102 1103 1104
			touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1);

			this._onDrop();

1105
			this.el = el = null;
R
RubaXa 已提交
1106 1107 1108
		}
	};

1109

L
Lebedev Konstantin 已提交
1110
	function _cloneHide(sortable, state) {
1111 1112 1113 1114
		if (sortable.lastPullMode !== 'clone') {
			state = true;
		}

R
RubaXa 已提交
1115 1116
		if (cloneEl && (cloneEl.state !== state)) {
			_css(cloneEl, 'display', state ? 'none' : '');
L
Lebedev Konstantin 已提交
1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128

			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 已提交
1129 1130 1131 1132 1133
			cloneEl.state = state;
		}
	}


R
RubaXa 已提交
1134
	function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) {
R
RubaXa 已提交
1135
		if (el) {
R
RubaXa 已提交
1136 1137 1138
			ctx = ctx || document;

			do {
R
RubaXa 已提交
1139
				if ((selector === '>*' && el.parentNode === ctx) || _matches(el, selector)) {
R
RubaXa 已提交
1140
					return el;
R
RubaXa 已提交
1141
				}
R
#930  
RubaXa 已提交
1142 1143
				/* jshint boss:true */
			} while (el = _getParentOrHost(el));
R
RubaXa 已提交
1144 1145
		}

R
RubaXa 已提交
1146
		return null;
R
RubaXa 已提交
1147 1148 1149
	}


N
Nikita SVIRIDENKO 已提交
1150 1151 1152 1153 1154
	function _getParentOrHost(el) {
		var parent = el.host;

		return (parent && parent.nodeType) ? parent : el.parentNode;
	}
1155 1156


1157
	function _globalDragOver(/**Event*/evt) {
1158 1159 1160
		if (evt.dataTransfer) {
			evt.dataTransfer.dropEffect = 'move';
		}
R
RubaXa 已提交
1161 1162 1163 1164
		evt.preventDefault();
	}


R
RubaXa 已提交
1165
	function _on(el, event, fn) {
1166
		el.addEventListener(event, fn, captureMode);
R
RubaXa 已提交
1167 1168 1169
	}


R
RubaXa 已提交
1170
	function _off(el, event, fn) {
1171
		el.removeEventListener(event, fn, captureMode);
R
RubaXa 已提交
1172 1173 1174
	}


R
RubaXa 已提交
1175 1176 1177
	function _toggleClass(el, name, state) {
		if (el) {
			if (el.classList) {
R
RubaXa 已提交
1178 1179 1180
				el.classList[state ? 'add' : 'remove'](name);
			}
			else {
B
Bogdan Mustiata 已提交
1181 1182
				var className = (' ' + el.className + ' ').replace(RSPACE, ' ').replace(' ' + name + ' ', ' ');
				el.className = (className + (state ? ' ' + name : '')).replace(RSPACE, ' ');
R
RubaXa 已提交
1183 1184 1185 1186 1187
			}
		}
	}


R
RubaXa 已提交
1188
	function _css(el, prop, val) {
R
RubaXa 已提交
1189 1190
		var style = el && el.style;

R
RubaXa 已提交
1191 1192 1193
		if (style) {
			if (val === void 0) {
				if (document.defaultView && document.defaultView.getComputedStyle) {
R
RubaXa 已提交
1194 1195
					val = document.defaultView.getComputedStyle(el, '');
				}
R
RubaXa 已提交
1196 1197
				else if (el.currentStyle) {
					val = el.currentStyle;
R
RubaXa 已提交
1198
				}
R
RubaXa 已提交
1199 1200 1201 1202 1203 1204 1205 1206 1207

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

				style[prop] = val + (typeof val === 'string' ? '' : 'px');
R
RubaXa 已提交
1208 1209 1210 1211 1212
			}
		}
	}


R
RubaXa 已提交
1213 1214
	function _find(ctx, tagName, iterator) {
		if (ctx) {
R
RubaXa 已提交
1215
			var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length;
R
RubaXa 已提交
1216

R
RubaXa 已提交
1217 1218
			if (iterator) {
				for (; i < n; i++) {
R
RubaXa 已提交
1219 1220 1221
					iterator(list[i], i);
				}
			}
R
RubaXa 已提交
1222

R
RubaXa 已提交
1223
			return list;
R
RubaXa 已提交
1224
		}
R
RubaXa 已提交
1225 1226

		return [];
R
RubaXa 已提交
1227 1228 1229
	}


R
RubaXa 已提交
1230 1231

	function _dispatchEvent(sortable, rootEl, name, targetEl, fromEl, startIndex, newIndex) {
1232 1233
		sortable = (sortable || rootEl[expando]);

R
RubaXa 已提交
1234
		var evt = document.createEvent('Event'),
1235
			options = sortable.options,
R
RubaXa 已提交
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247
			onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1);

		evt.initEvent(name, true, true);

		evt.to = rootEl;
		evt.from = fromEl || rootEl;
		evt.item = targetEl || rootEl;
		evt.clone = cloneEl;

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

R
RubaXa 已提交
1248 1249
		rootEl.dispatchEvent(evt);

R
RubaXa 已提交
1250 1251 1252 1253 1254 1255
		if (options[onName]) {
			options[onName].call(sortable, evt);
		}
	}


1256
	function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvt) {
R
RubaXa 已提交
1257 1258 1259 1260 1261
		var evt,
			sortable = fromEl[expando],
			onMoveFn = sortable.options.onMove,
			retVal;

C
ChiefORZ 已提交
1262 1263
		evt = document.createEvent('Event');
		evt.initEvent('move', true, true);
R
RubaXa 已提交
1264

C
ChiefORZ 已提交
1265 1266 1267 1268 1269 1270
		evt.to = toEl;
		evt.from = fromEl;
		evt.dragged = dragEl;
		evt.draggedRect = dragRect;
		evt.related = targetEl || toEl;
		evt.relatedRect = targetRect || toEl.getBoundingClientRect();
R
RubaXa 已提交
1271

C
ChiefORZ 已提交
1272
		fromEl.dispatchEvent(evt);
R
RubaXa 已提交
1273

C
ChiefORZ 已提交
1274
		if (onMoveFn) {
1275
			retVal = onMoveFn.call(sortable, evt, originalEvt);
R
RubaXa 已提交
1276 1277
		}

R
RubaXa 已提交
1278
		return retVal;
R
RubaXa 已提交
1279 1280 1281
	}


R
RubaXa 已提交
1282
	function _disableDraggable(el) {
R
RubaXa 已提交
1283
		el.draggable = false;
R
RubaXa 已提交
1284 1285 1286
	}


R
RubaXa 已提交
1287
	function _unsilent() {
R
RubaXa 已提交
1288 1289 1290 1291
		_silent = false;
	}


R
RubaXa 已提交
1292
	/** @returns {HTMLElement|false} */
1293
	function _ghostIsLast(el, evt) {
R
RubaXa 已提交
1294
		var lastEl = el.lastElementChild,
R
RubaXa 已提交
1295
			rect = lastEl.getBoundingClientRect();
R
RubaXa 已提交
1296

R
RubaXa 已提交
1297
		// 5 — min delta
R
RubaXa 已提交
1298
		// abs — нельзя добавлять, а то глюки при наведении сверху
R
RubaXa 已提交
1299
		return (
R
RubaXa 已提交
1300 1301
			(evt.clientY - (rect.top + rect.height) > 5) ||
			(evt.clientX - (rect.right + rect.width) > 5)
R
RubaXa 已提交
1302
		) && lastEl;
R
RubaXa 已提交
1303 1304 1305
	}


1306 1307 1308 1309 1310 1311 1312
	/**
	 * Generate id
	 * @param   {HTMLElement} el
	 * @returns {String}
	 * @private
	 */
	function _generateId(el) {
R
RubaXa 已提交
1313
		var str = el.tagName + el.className + el.src + el.href + el.textContent,
1314
			i = str.length,
R
RubaXa 已提交
1315
			sum = 0;
1316

1317 1318 1319
		while (i--) {
			sum += str.charCodeAt(i);
		}
1320

1321 1322 1323
		return sum.toString(36);
	}

1324
	/**
1325 1326
	 * Returns the index of an element within its parent for a selected set of
	 * elements
1327
	 * @param  {HTMLElement} el
1328
	 * @param  {selector} selector
1329
	 * @return {number}
1330
	 */
1331
	function _index(el, selector) {
1332 1333
		var index = 0;

1334 1335 1336
		if (!el || !el.parentNode) {
			return -1;
		}
1337

1338
		while (el && (el = el.previousElementSibling)) {
R
RubaXa 已提交
1339
			if ((el.nodeName.toUpperCase() !== 'TEMPLATE') && (selector === '>*' || _matches(el, selector))) {
1340 1341
				index++;
			}
1342
		}
1343

1344 1345
		return index;
	}
R
RubaXa 已提交
1346

1347
	function _matches(/**HTMLElement*/el, /**String*/selector) {
1348 1349 1350 1351 1352 1353 1354 1355 1356 1357
		if (el) {
			selector = selector.split('.');

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

			return (
				(tag === '' || el.nodeName.toUpperCase() == tag) &&
				(!selector.length || ((' ' + el.className + ' ').match(re) || []).length == selector.length)
			);
1358
		}
1359 1360

		return false;
1361 1362
	}

R
RubaXa 已提交
1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383
	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);
			}
		};
	}

1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395
	function _extend(dst, src) {
		if (dst && src) {
			for (var key in src) {
				if (src.hasOwnProperty(key)) {
					dst[key] = src[key];
				}
			}
		}

		return dst;
	}

1396 1397 1398 1399 1400 1401 1402 1403 1404
	function _clone(el) {
		return $
			? $(el).clone(true)[0]
			: (Polymer && Polymer.dom
				? Polymer.dom(el).cloneNode(true)
				: el.cloneNode(true)
			);
	}

R
RubaXa 已提交
1405 1406 1407 1408 1409 1410
	// Export utils
	Sortable.utils = {
		on: _on,
		off: _off,
		css: _css,
		find: _find,
R
RubaXa 已提交
1411 1412 1413
		is: function (el, selector) {
			return !!_closest(el, selector, el);
		},
1414
		extend: _extend,
R
RubaXa 已提交
1415
		throttle: _throttle,
R
RubaXa 已提交
1416
		closest: _closest,
1417
		toggleClass: _toggleClass,
1418
		clone: _clone,
1419
		index: _index
R
RubaXa 已提交
1420 1421 1422
	};


1423 1424 1425 1426 1427 1428
	/**
	 * Create sortable instance
	 * @param {HTMLElement}  el
	 * @param {Object}      [options]
	 */
	Sortable.create = function (el, options) {
R
RubaXa 已提交
1429
		return new Sortable(el, options);
1430
	};
R
RubaXa 已提交
1431

R
RubaXa 已提交
1432 1433

	// Export
R
RubaXa 已提交
1434
	Sortable.version = '1.5.0-rc1';
1435
	return Sortable;
R
RubaXa 已提交
1436
});