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


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

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

R
RubaXa 已提交
27
	var dragEl,
28
		parentEl,
R
RubaXa 已提交
29 30 31 32
		ghostEl,
		cloneEl,
		rootEl,
		nextEl,
R
RubaXa 已提交
33

34 35 36
		scrollEl,
		scrollParentEl,

R
RubaXa 已提交
37 38
		lastEl,
		lastCSS,
S
sp-kilobug 已提交
39
		lastParentCSS,
R
RubaXa 已提交
40

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

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

R
RubaXa 已提交
47
		autoScroll = {},
R
RubaXa 已提交
48

R
RubaXa 已提交
49 50
		tapEvt,
		touchEvt,
R
RubaXa 已提交
51

C
ChiefORZ 已提交
52 53
		moved,

R
RubaXa 已提交
54 55 56
		/** @const */
		RSPACE = /\s+/g,

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

R
RubaXa 已提交
59 60 61
		win = window,
		document = win.document,
		parseInt = win.parseInt,
R
RubaXa 已提交
62

R
* UPD  
RubaXa 已提交
63
		$ = win.jQuery || win.Zepto,
64 65
		Polymer = win.Polymer,

R
RubaXa 已提交
66
		supportDraggable = !!('draggable' in document.createElement('div')),
67 68 69 70 71
		supportCssPointerEvents = (function (el) {
			el = document.createElement('x');
			el.style.cssText = 'pointer-events:auto';
			return el.style.pointerEvents === 'auto';
		})(),
R
RubaXa 已提交
72

R
RubaXa 已提交
73
		_silent = false,
R
RubaXa 已提交
74

R
RubaXa 已提交
75
		abs = Math.abs,
R
RubaXa 已提交
76
		min = Math.min,
R
RubaXa 已提交
77
		slice = [].slice,
R
RubaXa 已提交
78

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
		touchDragOverListeners = [],

		_autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl) {
			// Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521
			if (rootEl && options.scroll) {
				var el,
					rect,
					sens = options.scrollSensitivity,
					speed = options.scrollSpeed,

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

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

					vx,
					vy
				;

				// Delect scrollEl
				if (scrollParentEl !== rootEl) {
					scrollEl = options.scroll;
					scrollParentEl = rootEl;

					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 () {
							if (el === win) {
145
								win.scrollTo(win.pageXOffset + vx * speed, win.pageYOffset + vy * speed);
146 147 148 149 150 151 152 153
							} else {
								vy && (el.scrollTop += vy * speed);
								vx && (el.scrollLeft += vx * speed);
							}
						}, 24);
					}
				}
			}
R
RubaXa 已提交
154 155 156
		}, 30),

		_prepareGroup = function (options) {
R
RubaXa 已提交
157 158
			function toFn(value, pull) {
				if (value === void 0) {
159
					value = group.name;
R
RubaXa 已提交
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
				}

				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 已提交
178 179
			var group = {};
			var originalGroup = options.group;
R
RubaXa 已提交
180

R
RubaXa 已提交
181 182
			if (!originalGroup || typeof originalGroup != 'object') {
				originalGroup = {name: originalGroup};
R
RubaXa 已提交
183 184
			}

R
RubaXa 已提交
185 186 187 188 189
			group.name = originalGroup.name;
			group.checkPull = toFn(originalGroup.pull, true);
			group.checkPut = toFn(originalGroup.put);

			options.group = group;
R
RubaXa 已提交
190
		}
R
RubaXa 已提交
191 192 193
	;


194

R
RubaXa 已提交
195 196 197
	/**
	 * @class  Sortable
	 * @param  {HTMLElement}  el
198
	 * @param  {Object}       [options]
R
RubaXa 已提交
199
	 */
R
RubaXa 已提交
200
	function Sortable(el, options) {
R
RubaXa 已提交
201 202 203 204
		if (!(el && el.nodeType && el.nodeType === 1)) {
			throw 'Sortable: `el` must be HTMLElement, and not ' + {}.toString.call(el);
		}

R
RubaXa 已提交
205
		this.el = el; // root element
206
		this.options = options = _extend({}, options);
R
RubaXa 已提交
207 208


209 210 211 212
		// Export instance
		el[expando] = this;


R
RubaXa 已提交
213
		// Default options
214 215
		var defaults = {
			group: Math.random(),
R
RubaXa 已提交
216
			sort: true,
R
RubaXa 已提交
217
			disabled: false,
218 219
			store: null,
			handle: null,
R
RubaXa 已提交
220 221 222
			scroll: true,
			scrollSensitivity: 30,
			scrollSpeed: 10,
R
RubaXa 已提交
223
			draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*',
224
			ghostClass: 'sortable-ghost',
R
RubaXa 已提交
225
			chosenClass: 'sortable-chosen',
226
			ignore: 'a, img',
227
			filter: null,
R
RubaXa 已提交
228 229 230
			animation: 0,
			setData: function (dataTransfer, dragEl) {
				dataTransfer.setData('Text', dragEl.textContent);
231 232
			},
			dropBubble: false,
R
RubaXa 已提交
233
			dragoverBubble: false,
234
			dataIdAttr: 'data-id',
235
			delay: 0,
C
ChiefORZ 已提交
236 237
			forceFallback: false,
			fallbackClass: 'sortable-fallback',
238 239
			fallbackOnBody: false,
			fallbackTolerance: 0
R
RubaXa 已提交
240
		};
241

R
RubaXa 已提交
242

243 244
		// Set default options
		for (var name in defaults) {
R
RubaXa 已提交
245
			!(name in options) && (options[name] = defaults[name]);
246
		}
R
RubaXa 已提交
247

R
RubaXa 已提交
248
		_prepareGroup(options);
R
RubaXa 已提交
249

R
* JSDoc  
RubaXa 已提交
250
		// Bind all private methods
R
RubaXa 已提交
251 252
		for (var fn in this) {
			if (fn.charAt(0) === '_') {
253
				this[fn] = this[fn].bind(this);
R
RubaXa 已提交
254 255 256
			}
		}

R
RubaXa 已提交
257 258
		// Setup drag mode
		this.nativeDraggable = options.forceFallback ? false : supportDraggable;
R
RubaXa 已提交
259 260 261 262 263

		// Bind events
		_on(el, 'mousedown', this._onTapStart);
		_on(el, 'touchstart', this._onTapStart);

R
RubaXa 已提交
264
		if (this.nativeDraggable) {
265 266 267
			_on(el, 'dragover', this);
			_on(el, 'dragenter', this);
		}
R
RubaXa 已提交
268 269

		touchDragOverListeners.push(this._onDragOver);
270 271 272

		// Restore sorting
		options.store && this.sort(options.store.get(this));
R
RubaXa 已提交
273 274 275
	}


276
	Sortable.prototype = /** @lends Sortable.prototype */ {
R
RubaXa 已提交
277 278
		constructor: Sortable,

279
		_onTapStart: function (/** Event|TouchEvent */evt) {
280 281
			var _this = this,
				el = this.el,
282 283 284 285 286
				options = this.options,
				type = evt.type,
				touch = evt.touches && evt.touches[0],
				target = (touch || evt).target,
				originalTarget = target,
R
RubaXa 已提交
287 288
				filter = options.filter,
				startIndex;
R
RubaXa 已提交
289 290

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

295 296
			if (type === 'mousedown' && evt.button !== 0 || options.disabled) {
				return; // only left button or enabled
R
RubaXa 已提交
297
			}
R
RubaXa 已提交
298

299
			target = _closest(target, options.draggable, el);
300

301 302
			if (!target) {
				return;
303
			}
304

305 306 307
			if (options.handle && !_closest(originalTarget, options.handle, el)) {
				return;
			}
308

R
RubaXa 已提交
309
			// Get the index of the dragged element within its parent
R
RubaXa 已提交
310
			startIndex = _index(target, options.draggable);
311 312 313 314

			// Check filter
			if (typeof filter === 'function') {
				if (filter.call(this, evt, target, this)) {
R
RubaXa 已提交
315
					_dispatchEvent(_this, originalTarget, 'filter', target, el, startIndex);
316 317 318
					evt.preventDefault();
					return; // cancel dnd
				}
319
			}
320 321 322
			else if (filter) {
				filter = filter.split(',').some(function (criteria) {
					criteria = _closest(originalTarget, criteria.trim(), el);
323

324
					if (criteria) {
R
RubaXa 已提交
325
						_dispatchEvent(_this, criteria, 'filter', target, el, startIndex);
326 327 328 329 330 331 332
						return true;
					}
				});

				if (filter) {
					evt.preventDefault();
					return; // cancel dnd
333 334 335
				}
			}

336
			// Prepare `dragstart`
R
RubaXa 已提交
337
			this._prepareDragStart(evt, touch, target, startIndex);
338 339
		},

R
RubaXa 已提交
340
		_prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target, /** Number */startIndex) {
341 342 343 344 345
			var _this = this,
				el = _this.el,
				options = _this.options,
				ownerDocument = el.ownerDocument,
				dragStartFn;
346 347 348 349

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

350
				rootEl = el;
351
				dragEl = target;
R
RubaXa 已提交
352
				parentEl = dragEl.parentNode;
353
				nextEl = dragEl.nextSibling;
354
				activeGroup = options.group;
R
RubaXa 已提交
355
				oldIndex = startIndex;
356

357 358
				this._lastX = (touch || evt).clientX;
				this._lastY = (touch || evt).clientY;
S
sp-kilobug 已提交
359

360 361 362 363
				dragStartFn = function () {
					// Delayed drag has been triggered
					// we can re-enable the events: touchmove/mousemove
					_this._disableDelayedDrag();
364

365 366
					// Make the element draggable
					dragEl.draggable = true;
367

R
RubaXa 已提交
368 369
					// Chosen item
					_toggleClass(dragEl, _this.options.chosenClass, true);
370 371 372

					// Bind the events: dragstart/dragend
					_this._triggerDragStart(touch);
T
TaliLavi 已提交
373 374

					// Drag start event
375
					_dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, oldIndex);
376 377
				};

R
RubaXa 已提交
378 379 380 381 382
				// Disable "draggable"
				options.ignore.split(',').forEach(function (criteria) {
					_find(dragEl, criteria.trim(), _disableDraggable);
				});

383 384 385 386 387
				_on(ownerDocument, 'mouseup', _this._onDrop);
				_on(ownerDocument, 'touchend', _this._onDrop);
				_on(ownerDocument, 'touchcancel', _this._onDrop);

				if (options.delay) {
388 389
					// If the user moves the pointer or let go the click or touch
					// before the delay has been reached:
390
					// disable the delayed drag
391 392 393
					_on(ownerDocument, 'mouseup', _this._disableDelayedDrag);
					_on(ownerDocument, 'touchend', _this._disableDelayedDrag);
					_on(ownerDocument, 'touchcancel', _this._disableDelayedDrag);
394 395 396 397 398 399
					_on(ownerDocument, 'mousemove', _this._disableDelayedDrag);
					_on(ownerDocument, 'touchmove', _this._disableDelayedDrag);

					_this._dragStartTimer = setTimeout(dragStartFn, options.delay);
				} else {
					dragStartFn();
400 401 402
				}
			}
		},
R
RubaXa 已提交
403

404 405
		_disableDelayedDrag: function () {
			var ownerDocument = this.el.ownerDocument;
406

407
			clearTimeout(this._dragStartTimer);
408 409 410
			_off(ownerDocument, 'mouseup', this._disableDelayedDrag);
			_off(ownerDocument, 'touchend', this._disableDelayedDrag);
			_off(ownerDocument, 'touchcancel', this._disableDelayedDrag);
411 412 413
			_off(ownerDocument, 'mousemove', this._disableDelayedDrag);
			_off(ownerDocument, 'touchmove', this._disableDelayedDrag);
		},
R
RubaXa 已提交
414

415 416 417 418 419 420 421 422
		_triggerDragStart: function (/** Touch */touch) {
			if (touch) {
				// Touch device support
				tapEvt = {
					target: dragEl,
					clientX: touch.clientX,
					clientY: touch.clientY
				};
423

424
				this._onDragStart(tapEvt, 'touch');
R
RubaXa 已提交
425
			}
R
RubaXa 已提交
426
			else if (!this.nativeDraggable) {
427 428 429 430 431
				this._onDragStart(tapEvt, true);
			}
			else {
				_on(dragEl, 'dragend', this);
				_on(rootEl, 'dragstart', this._onDragStart);
432 433
			}

434 435 436 437 438
			try {
				if (document.selection) {
					document.selection.empty();
				} else {
					window.getSelection().removeAllRanges();
439
				}
440
			} catch (err) {
441
			}
442
		},
443

444 445 446 447
		_dragStarted: function () {
			if (rootEl && dragEl) {
				// Apply effect
				_toggleClass(dragEl, this.options.ghostClass, true);
R
RubaXa 已提交
448

449
				Sortable.active = this;
450

451
				// Drag start event
452
				_dispatchEvent(this, rootEl, 'start', dragEl, rootEl, oldIndex);
453
			}
R
RubaXa 已提交
454 455
		},

R
RubaXa 已提交
456 457
		_emulateDragOver: function () {
			if (touchEvt) {
R
RubaXa 已提交
458
				if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY) {
459 460
					return;
				}
R
RubaXa 已提交
461 462 463

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

465 466 467
				if (!supportCssPointerEvents) {
					_css(ghostEl, 'display', 'none');
				}
R
RubaXa 已提交
468

R
RubaXa 已提交
469
				var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY),
R
RubaXa 已提交
470
					parent = target,
471
					groupName = ' ' + this.options.group.name + '',
R
RubaXa 已提交
472
					i = touchDragOverListeners.length;
R
RubaXa 已提交
473

R
RubaXa 已提交
474 475
				if (parent) {
					do {
R
RubaXa 已提交
476
						if (parent[expando]) {
R
RubaXa 已提交
477 478 479 480 481 482 483 484 485 486 487 488 489
							while (i--) {
								touchDragOverListeners[i]({
									clientX: touchEvt.clientX,
									clientY: touchEvt.clientY,
									target: target,
									rootEl: parent
								});
							}

							break;
						}

						target = parent; // store last element
L
Larry Davis 已提交
490
					}
R
RubaXa 已提交
491 492
					/* jshint boss:true */
					while (parent = parent.parentNode);
R
RubaXa 已提交
493 494
				}

495 496 497
				if (!supportCssPointerEvents) {
					_css(ghostEl, 'display', '');
				}
R
RubaXa 已提交
498 499 500 501
			}
		},


R
RubaXa 已提交
502 503
		_onTouchMove: function (/**TouchEvent*/evt) {
			if (tapEvt) {
R
RubaXa 已提交
504 505 506 507 508 509
				var	options = this.options,
					fallbackTolerance = options.fallbackTolerance,
					touch = evt.touches ? evt.touches[0] : evt,
					dx = touch.clientX - tapEvt.clientX,
					dy = touch.clientY - tapEvt.clientY,
					translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';
S
sp-kilobug 已提交
510

511
				// only set the status to dragging, when we are actually dragging
R
RubaXa 已提交
512
				if (!Sortable.active) {
R
RubaXa 已提交
513 514 515 516
					if (fallbackTolerance &&
						min(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY)) < fallbackTolerance
					) {
						return;
S
sp-kilobug 已提交
517
					}
R
RubaXa 已提交
518

519 520
					this._dragStarted();
				}
R
RubaXa 已提交
521

522 523
				// as well as creating the ghost element on the document body
				this._appendGhost();
R
RubaXa 已提交
524

A
Alex Wild 已提交
525
				moved = true;
R
RubaXa 已提交
526
				touchEvt = touch;
R
RubaXa 已提交
527 528 529 530 531 532

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

M
Marius Petcu 已提交
533
				evt.preventDefault();
R
RubaXa 已提交
534 535 536
			}
		},

R
RubaXa 已提交
537 538
		_appendGhost: function () {
			if (!ghostEl) {
R
RubaXa 已提交
539 540
				var rect = dragEl.getBoundingClientRect(),
					css = _css(dragEl),
R
RubaXa 已提交
541
					options = this.options,
R
RubaXa 已提交
542
					ghostRect;
R
RubaXa 已提交
543

544
				ghostEl = dragEl.cloneNode(true);
R
RubaXa 已提交
545

R
RubaXa 已提交
546 547
				_toggleClass(ghostEl, options.ghostClass, false);
				_toggleClass(ghostEl, options.fallbackClass, true);
C
ChiefORZ 已提交
548

R
RubaXa 已提交
549 550
				_css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10));
				_css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10));
R
RubaXa 已提交
551 552
				_css(ghostEl, 'width', rect.width);
				_css(ghostEl, 'height', rect.height);
R
RubaXa 已提交
553 554 555
				_css(ghostEl, 'opacity', '0.8');
				_css(ghostEl, 'position', 'fixed');
				_css(ghostEl, 'zIndex', '100000');
556
				_css(ghostEl, 'pointerEvents', 'none');
R
RubaXa 已提交
557

R
RubaXa 已提交
558
				options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
R
RubaXa 已提交
559 560 561

				// Fixing dimensions.
				ghostRect = ghostEl.getBoundingClientRect();
R
RubaXa 已提交
562 563
				_css(ghostEl, 'width', rect.width * 2 - ghostRect.width);
				_css(ghostEl, 'height', rect.height * 2 - ghostRect.height);
564 565 566 567 568 569 570 571 572
			}
		},

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

			this._offUpEvents();

R
RubaXa 已提交
573
			if (activeGroup.checkPull(this, this, dragEl, evt) == 'clone') {
574
				cloneEl = _clone(dragEl);
575 576
				_css(cloneEl, 'display', 'none');
				rootEl.insertBefore(cloneEl, dragEl);
R
RubaXa 已提交
577
				_dispatchEvent(this, rootEl, 'clone', dragEl);
578 579 580
			}

			if (useFallback) {
R
RubaXa 已提交
581 582 583 584 585 586 587 588 589 590
				if (useFallback === 'touch') {
					// Bind touch events
					_on(document, 'touchmove', this._onTouchMove);
					_on(document, 'touchend', this._onDrop);
					_on(document, 'touchcancel', this._onDrop);
				} else {
					// Old brwoser
					_on(document, 'mousemove', this._onTouchMove);
					_on(document, 'mouseup', this._onDrop);
				}
R
RubaXa 已提交
591

592
				this._loopId = setInterval(this._emulateDragOver, 50);
R
RubaXa 已提交
593 594
			}
			else {
R
RubaXa 已提交
595 596 597 598
				if (dataTransfer) {
					dataTransfer.effectAllowed = 'move';
					options.setData && options.setData.call(this, dataTransfer, dragEl);
				}
R
RubaXa 已提交
599

R
RubaXa 已提交
600
				_on(document, 'drop', this);
601
				setTimeout(this._dragStarted, 0);
R
RubaXa 已提交
602 603 604
			}
		},

R
RubaXa 已提交
605
		_onDragOver: function (/**Event*/evt) {
R
RubaXa 已提交
606 607 608 609 610 611
			var el = this.el,
				target,
				dragRect,
				revert,
				options = this.options,
				group = options.group,
R
RubaXa 已提交
612
				activeSortable = Sortable.active,
613 614
				isOwner = (activeGroup === group),
				canSort = options.sort;
R
RubaXa 已提交
615

R
RubaXa 已提交
616 617
			if (evt.preventDefault !== void 0) {
				evt.preventDefault();
618
				!options.dragoverBubble && evt.stopPropagation();
R
RubaXa 已提交
619
			}
R
RubaXa 已提交
620

A
Alex Wild 已提交
621 622
			moved = true;

R
RubaXa 已提交
623
			if (activeGroup && !options.disabled &&
624
				(isOwner
R
RubaXa 已提交
625
					? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list
R
RubaXa 已提交
626 627 628
					: (
						putSortable === this ||
						activeGroup.checkPull(this, activeSortable, dragEl, evt) && group.checkPut(this, activeSortable, dragEl, evt)
R
RubaXa 已提交
629
					)
630
				) &&
R
RubaXa 已提交
631
				(evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback
R
RubaXa 已提交
632
			) {
R
RubaXa 已提交
633 634 635 636 637 638 639
				// Smart auto-scrolling
				_autoScroll(evt, options, this.el);

				if (_silent) {
					return;
				}

R
RubaXa 已提交
640
				target = _closest(evt.target, options.draggable, el);
R
RubaXa 已提交
641
				dragRect = dragEl.getBoundingClientRect();
R
RubaXa 已提交
642
				putSortable = this;
R
RubaXa 已提交
643

644
				if (revert) {
R
RubaXa 已提交
645
					_cloneHide(true);
R
RubaXa 已提交
646
					parentEl = rootEl; // actualization
R
RubaXa 已提交
647

648 649 650 651 652 653 654
					if (cloneEl || nextEl) {
						rootEl.insertBefore(dragEl, cloneEl || nextEl);
					}
					else if (!canSort) {
						rootEl.appendChild(dragEl);
					}

R
RubaXa 已提交
655 656
					return;
				}
R
RubaXa 已提交
657

R
RubaXa 已提交
658

R
RubaXa 已提交
659
				if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
660
					(el === evt.target) && (target = _ghostIsLast(el, evt))
R
RubaXa 已提交
661
				) {
R
RubaXa 已提交
662

R
RubaXa 已提交
663 664 665 666
					if (target) {
						if (target.animated) {
							return;
						}
R
RubaXa 已提交
667

R
RubaXa 已提交
668 669
						targetRect = target.getBoundingClientRect();
					}
R
RubaXa 已提交
670

R
RubaXa 已提交
671 672
					_cloneHide(isOwner);

R
RubaXa 已提交
673
					if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect) !== false) {
674 675
						if (!dragEl.contains(el)) {
							el.appendChild(dragEl);
R
RubaXa 已提交
676
							parentEl = el; // actualization
677
						}
R
RubaXa 已提交
678

R
RubaXa 已提交
679 680 681
						this._animate(dragRect, dragEl);
						target && this._animate(targetRect, target);
					}
R
RubaXa 已提交
682
				}
R
RubaXa 已提交
683 684
				else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) {
					if (lastEl !== target) {
R
RubaXa 已提交
685
						lastEl = target;
R
RubaXa 已提交
686
						lastCSS = _css(target);
S
sp-kilobug 已提交
687
						lastParentCSS = _css(target.parentNode);
R
RubaXa 已提交
688 689 690
					}


R
RubaXa 已提交
691 692 693
					var targetRect = target.getBoundingClientRect(),
						width = targetRect.right - targetRect.left,
						height = targetRect.bottom - targetRect.top,
S
sp-kilobug 已提交
694 695
						floating = /left|right|inline/.test(lastCSS.cssFloat + lastCSS.display)
							|| (lastParentCSS.display == 'flex' && lastParentCSS['flex-direction'].indexOf('row') === 0),
R
RubaXa 已提交
696 697 698 699
						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,
R
RubaXa 已提交
700
						moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect),
R
RubaXa 已提交
701
						after
R
RubaXa 已提交
702
					;
R
RubaXa 已提交
703

R
RubaXa 已提交
704
					if (moveVector !== false) {
R
RubaXa 已提交
705 706
						_silent = true;
						setTimeout(_unsilent, 30);
R
RubaXa 已提交
707

R
RubaXa 已提交
708
						_cloneHide(isOwner);
R
RubaXa 已提交
709

R
RubaXa 已提交
710 711 712 713
						if (moveVector === 1 || moveVector === -1) {
							after = (moveVector === 1);
						}
						else if (floating) {
S
sp-kilobug 已提交
714 715
							var elTop = dragEl.offsetTop,
								tgTop = target.offsetTop;
R
RubaXa 已提交
716 717

							if (elTop === tgTop) {
S
sp-kilobug 已提交
718
								after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide;
719
							}
720
							else if (target.previousElementSibling === dragEl || dragEl.previousElementSibling === target) {
721
								after = (evt.clientY - targetRect.top) / height > 0.5;
S
sp-kilobug 已提交
722 723 724
							} else {
								after = tgTop > elTop;
							}
R
RubaXa 已提交
725 726 727
						} else {
							after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
						}
R
RubaXa 已提交
728

729 730 731 732 733 734
						if (!dragEl.contains(el)) {
							if (after && !nextSibling) {
								el.appendChild(dragEl);
							} else {
								target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
							}
R
RubaXa 已提交
735
						}
R
RubaXa 已提交
736

R
RubaXa 已提交
737 738
						parentEl = dragEl.parentNode; // actualization

R
RubaXa 已提交
739 740 741
						this._animate(dragRect, dragEl);
						this._animate(targetRect, target);
					}
R
RubaXa 已提交
742 743 744 745
				}
			}
		},

746 747 748 749 750 751
		_animate: function (prevRect, target) {
			var ms = this.options.animation;

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

R
RubaXa 已提交
752
				_css(target, 'transition', 'none');
753 754 755 756 757 758 759
				_css(target, 'transform', 'translate3d('
					+ (prevRect.left - currentRect.left) + 'px,'
					+ (prevRect.top - currentRect.top) + 'px,0)'
				);

				target.offsetWidth; // repaint

R
RubaXa 已提交
760
				_css(target, 'transition', 'all ' + ms + 'ms');
761 762
				_css(target, 'transform', 'translate3d(0,0,0)');

R
* anim  
RubaXa 已提交
763 764
				clearTimeout(target.animated);
				target.animated = setTimeout(function () {
765
					_css(target, 'transition', '');
766
					_css(target, 'transform', '');
767 768 769 770 771
					target.animated = false;
				}, ms);
			}
		},

772
		_offUpEvents: function () {
773 774
			var ownerDocument = this.el.ownerDocument;

775
			_off(document, 'touchmove', this._onTouchMove);
776 777 778
			_off(ownerDocument, 'mouseup', this._onDrop);
			_off(ownerDocument, 'touchend', this._onDrop);
			_off(ownerDocument, 'touchcancel', this._onDrop);
779
		},
R
RubaXa 已提交
780

R
RubaXa 已提交
781
		_onDrop: function (/**Event*/evt) {
782 783
			var el = this.el,
				options = this.options;
R
RubaXa 已提交
784

R
RubaXa 已提交
785
			clearInterval(this._loopId);
R
RubaXa 已提交
786
			clearInterval(autoScroll.pid);
R
RubaXa 已提交
787
			clearTimeout(this._dragStartTimer);
788

R
RubaXa 已提交
789
			// Unbind events
R
RubaXa 已提交
790
			_off(document, 'mousemove', this._onTouchMove);
791

R
RubaXa 已提交
792
			if (this.nativeDraggable) {
793 794 795
				_off(document, 'drop', this);
				_off(el, 'dragstart', this._onDragStart);
			}
R
RubaXa 已提交
796

797
			this._offUpEvents();
R
RubaXa 已提交
798

R
RubaXa 已提交
799
			if (evt) {
R
RubaXa 已提交
800
				if (moved) {
C
ChiefORZ 已提交
801 802 803
					evt.preventDefault();
					!options.dropBubble && evt.stopPropagation();
				}
R
RubaXa 已提交
804

R
RubaXa 已提交
805
				ghostEl && ghostEl.parentNode.removeChild(ghostEl);
R
RubaXa 已提交
806

R
RubaXa 已提交
807
				if (dragEl) {
R
RubaXa 已提交
808
					if (this.nativeDraggable) {
809 810
						_off(dragEl, 'dragend', this);
					}
R
RubaXa 已提交
811

812
					_disableDraggable(dragEl);
R
RubaXa 已提交
813 814

					// Remove class's
R
RubaXa 已提交
815
					_toggleClass(dragEl, this.options.ghostClass, false);
R
RubaXa 已提交
816
					_toggleClass(dragEl, this.options.chosenClass, false);
R
RubaXa 已提交
817

818
					if (rootEl !== parentEl) {
819
						newIndex = _index(dragEl, options.draggable);
R
RubaXa 已提交
820

821
						if (newIndex >= 0) {
822 823 824
							// drag from one list and drop into another
							_dispatchEvent(null, parentEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
							_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
R
RubaXa 已提交
825

826 827
							// Add event
							_dispatchEvent(null, parentEl, 'add', dragEl, rootEl, oldIndex, newIndex);
828

829 830 831
							// Remove event
							_dispatchEvent(this, rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex);
						}
R
RubaXa 已提交
832
					}
R
RubaXa 已提交
833 834
					else {
						// Remove clone
R
RubaXa 已提交
835
						cloneEl && cloneEl.parentNode.removeChild(cloneEl);
R
RubaXa 已提交
836

R
RubaXa 已提交
837 838
						if (dragEl.nextSibling !== nextEl) {
							// Get the index of the dragged element within its parent
839
							newIndex = _index(dragEl, options.draggable);
840 841

							if (newIndex >= 0) {
842 843 844 845
								// drag & drop within the same list
								_dispatchEvent(this, rootEl, 'update', dragEl, rootEl, oldIndex, newIndex);
								_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
							}
R
RubaXa 已提交
846
						}
R
RubaXa 已提交
847
					}
848

R
RubaXa 已提交
849
					if (Sortable.active) {
R
RubaXa 已提交
850
						if (newIndex === null || newIndex === -1) {
R
RubaXa 已提交
851 852 853
							newIndex = oldIndex;
						}

R
RubaXa 已提交
854 855 856 857 858
						_dispatchEvent(this, rootEl, 'end', dragEl, rootEl, oldIndex, newIndex);

						// Save sorting
						this.save();
					}
R
RubaXa 已提交
859
				}
R
RubaXa 已提交
860

861
			}
R
RubaXa 已提交
862

863 864
			this._nulling();
		},
R
RubaXa 已提交
865

R
RubaXa 已提交
866
		_nulling: function () {
867 868 869 870 871 872
			rootEl =
			dragEl =
			parentEl =
			ghostEl =
			nextEl =
			cloneEl =
873

874 875
			scrollEl =
			scrollParentEl =
R
RubaXa 已提交
876

877 878
			tapEvt =
			touchEvt =
C
ChiefORZ 已提交
879

880 881
			moved =
			newIndex =
R
RubaXa 已提交
882

883 884
			lastEl =
			lastCSS =
R
RubaXa 已提交
885

R
RubaXa 已提交
886
			putSortable =
887
			activeGroup =
R
RubaXa 已提交
888

889 890
			Sortable.active = null;
		},
R
RubaXa 已提交
891

R
RubaXa 已提交
892 893 894
		handleEvent: function (/**Event*/evt) {
			var type = evt.type;

R
RubaXa 已提交
895
			if (type === 'dragover' || type === 'dragenter') {
R
RubaXa 已提交
896 897 898 899
				if (dragEl) {
					this._onDragOver(evt);
					_globalDragOver(evt);
				}
R
RubaXa 已提交
900
			}
R
RubaXa 已提交
901
			else if (type === 'drop' || type === 'dragend') {
R
RubaXa 已提交
902 903
				this._onDrop(evt);
			}
R
RubaXa 已提交
904 905 906
		},


907 908 909 910 911 912 913 914 915
		/**
		 * Serializes the item into an array of string.
		 * @returns {String[]}
		 */
		toArray: function () {
			var order = [],
				el,
				children = this.el.children,
				i = 0,
R
RubaXa 已提交
916 917
				n = children.length,
				options = this.options;
918 919 920

			for (; i < n; i++) {
				el = children[i];
R
RubaXa 已提交
921
				if (_closest(el, options.draggable, this.el)) {
R
RubaXa 已提交
922
					order.push(el.getAttribute(options.dataIdAttr) || _generateId(el));
R
RubaXa 已提交
923
				}
924 925 926 927 928 929 930 931 932 933 934
			}

			return order;
		},


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

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

R
RubaXa 已提交
940
				if (_closest(el, this.options.draggable, rootEl)) {
R
RubaXa 已提交
941 942 943
					items[id] = el;
				}
			}, this);
944 945 946

			order.forEach(function (id) {
				if (items[id]) {
R
RubaXa 已提交
947 948
					rootEl.removeChild(items[id]);
					rootEl.appendChild(items[id]);
949 950 951 952 953
				}
			});
		},


R
RubaXa 已提交
954 955 956 957 958 959 960 961 962
		/**
		 * Save the current sorting
		 */
		save: function () {
			var store = this.options.store;
			store && store.set(this);
		},


963 964 965 966 967 968 969 970 971 972 973
		/**
		 * 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);
		},


974 975 976 977 978 979 980 981 982 983 984 985 986
		/**
		 * 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 已提交
987 988 989 990

				if (name === 'group') {
					_prepareGroup(options);
				}
991 992 993 994
			}
		},


995 996 997 998
		/**
		 * Destroy
		 */
		destroy: function () {
999
			var el = this.el;
R
RubaXa 已提交
1000

1001
			el[expando] = null;
1002

R
RubaXa 已提交
1003 1004 1005
			_off(el, 'mousedown', this._onTapStart);
			_off(el, 'touchstart', this._onTapStart);

R
RubaXa 已提交
1006
			if (this.nativeDraggable) {
1007 1008 1009
				_off(el, 'dragover', this);
				_off(el, 'dragenter', this);
			}
R
RubaXa 已提交
1010

1011
			// Remove draggable attributes
R
RubaXa 已提交
1012
			Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) {
1013 1014 1015
				el.removeAttribute('draggable');
			});

R
RubaXa 已提交
1016 1017 1018 1019
			touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1);

			this._onDrop();

1020
			this.el = el = null;
R
RubaXa 已提交
1021 1022 1023
		}
	};

1024

R
RubaXa 已提交
1025 1026 1027 1028 1029 1030 1031 1032 1033
	function _cloneHide(state) {
		if (cloneEl && (cloneEl.state !== state)) {
			_css(cloneEl, 'display', state ? 'none' : '');
			!state && cloneEl.state && rootEl.insertBefore(cloneEl, dragEl);
			cloneEl.state = state;
		}
	}


R
RubaXa 已提交
1034
	function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) {
R
RubaXa 已提交
1035
		if (el) {
R
RubaXa 已提交
1036 1037 1038
			ctx = ctx || document;

			do {
R
RubaXa 已提交
1039
				if ((selector === '>*' && el.parentNode === ctx) || _matches(el, selector)) {
R
RubaXa 已提交
1040
					return el;
R
RubaXa 已提交
1041 1042
				}
			}
R
RubaXa 已提交
1043
			while (el !== ctx && (el = el.parentNode));
R
RubaXa 已提交
1044 1045
		}

R
RubaXa 已提交
1046
		return null;
R
RubaXa 已提交
1047 1048 1049
	}


1050
	function _globalDragOver(/**Event*/evt) {
1051 1052 1053
		if (evt.dataTransfer) {
			evt.dataTransfer.dropEffect = 'move';
		}
R
RubaXa 已提交
1054 1055 1056 1057
		evt.preventDefault();
	}


R
RubaXa 已提交
1058
	function _on(el, event, fn) {
R
RubaXa 已提交
1059 1060 1061 1062
		el.addEventListener(event, fn, false);
	}


R
RubaXa 已提交
1063
	function _off(el, event, fn) {
R
RubaXa 已提交
1064 1065 1066 1067
		el.removeEventListener(event, fn, false);
	}


R
RubaXa 已提交
1068 1069 1070
	function _toggleClass(el, name, state) {
		if (el) {
			if (el.classList) {
R
RubaXa 已提交
1071 1072 1073
				el.classList[state ? 'add' : 'remove'](name);
			}
			else {
B
Bogdan Mustiata 已提交
1074 1075
				var className = (' ' + el.className + ' ').replace(RSPACE, ' ').replace(' ' + name + ' ', ' ');
				el.className = (className + (state ? ' ' + name : '')).replace(RSPACE, ' ');
R
RubaXa 已提交
1076 1077 1078 1079 1080
			}
		}
	}


R
RubaXa 已提交
1081
	function _css(el, prop, val) {
R
RubaXa 已提交
1082 1083
		var style = el && el.style;

R
RubaXa 已提交
1084 1085 1086
		if (style) {
			if (val === void 0) {
				if (document.defaultView && document.defaultView.getComputedStyle) {
R
RubaXa 已提交
1087 1088
					val = document.defaultView.getComputedStyle(el, '');
				}
R
RubaXa 已提交
1089 1090
				else if (el.currentStyle) {
					val = el.currentStyle;
R
RubaXa 已提交
1091
				}
R
RubaXa 已提交
1092 1093 1094 1095 1096 1097 1098 1099 1100

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

				style[prop] = val + (typeof val === 'string' ? '' : 'px');
R
RubaXa 已提交
1101 1102 1103 1104 1105
			}
		}
	}


R
RubaXa 已提交
1106 1107
	function _find(ctx, tagName, iterator) {
		if (ctx) {
R
RubaXa 已提交
1108
			var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length;
R
RubaXa 已提交
1109

R
RubaXa 已提交
1110 1111
			if (iterator) {
				for (; i < n; i++) {
R
RubaXa 已提交
1112 1113 1114
					iterator(list[i], i);
				}
			}
R
RubaXa 已提交
1115

R
RubaXa 已提交
1116
			return list;
R
RubaXa 已提交
1117
		}
R
RubaXa 已提交
1118 1119

		return [];
R
RubaXa 已提交
1120 1121 1122
	}


R
RubaXa 已提交
1123 1124

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

R
RubaXa 已提交
1127
		var evt = document.createEvent('Event'),
1128
			options = sortable.options,
R
RubaXa 已提交
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140
			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 已提交
1141 1142
		rootEl.dispatchEvent(evt);

R
RubaXa 已提交
1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
		if (options[onName]) {
			options[onName].call(sortable, evt);
		}
	}


	function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect) {
		var evt,
			sortable = fromEl[expando],
			onMoveFn = sortable.options.onMove,
			retVal;

C
ChiefORZ 已提交
1155 1156
		evt = document.createEvent('Event');
		evt.initEvent('move', true, true);
R
RubaXa 已提交
1157

C
ChiefORZ 已提交
1158 1159 1160 1161 1162 1163
		evt.to = toEl;
		evt.from = fromEl;
		evt.dragged = dragEl;
		evt.draggedRect = dragRect;
		evt.related = targetEl || toEl;
		evt.relatedRect = targetRect || toEl.getBoundingClientRect();
R
RubaXa 已提交
1164

C
ChiefORZ 已提交
1165
		fromEl.dispatchEvent(evt);
R
RubaXa 已提交
1166

C
ChiefORZ 已提交
1167
		if (onMoveFn) {
R
RubaXa 已提交
1168 1169 1170
			retVal = onMoveFn.call(sortable, evt);
		}

R
RubaXa 已提交
1171
		return retVal;
R
RubaXa 已提交
1172 1173 1174
	}


R
RubaXa 已提交
1175
	function _disableDraggable(el) {
R
RubaXa 已提交
1176
		el.draggable = false;
R
RubaXa 已提交
1177 1178 1179
	}


R
RubaXa 已提交
1180
	function _unsilent() {
R
RubaXa 已提交
1181 1182 1183 1184
		_silent = false;
	}


R
RubaXa 已提交
1185
	/** @returns {HTMLElement|false} */
1186
	function _ghostIsLast(el, evt) {
R
RubaXa 已提交
1187
		var lastEl = el.lastElementChild,
1188
				rect = lastEl.getBoundingClientRect();
R
RubaXa 已提交
1189

1190
		return ((evt.clientY - (rect.top + rect.height) > 5) || (evt.clientX - (rect.right + rect.width) > 5)) && lastEl; // min delta
R
RubaXa 已提交
1191 1192 1193
	}


1194 1195 1196 1197 1198 1199 1200
	/**
	 * Generate id
	 * @param   {HTMLElement} el
	 * @returns {String}
	 * @private
	 */
	function _generateId(el) {
R
RubaXa 已提交
1201
		var str = el.tagName + el.className + el.src + el.href + el.textContent,
1202
			i = str.length,
R
RubaXa 已提交
1203
			sum = 0;
1204

1205 1206 1207
		while (i--) {
			sum += str.charCodeAt(i);
		}
1208

1209 1210 1211
		return sum.toString(36);
	}

1212
	/**
1213 1214
	 * Returns the index of an element within its parent for a selected set of
	 * elements
1215
	 * @param  {HTMLElement} el
1216
	 * @param  {selector} selector
1217
	 * @return {number}
1218
	 */
1219
	function _index(el, selector) {
1220 1221
		var index = 0;

1222 1223 1224
		if (!el || !el.parentNode) {
			return -1;
		}
1225

1226
		while (el && (el = el.previousElementSibling)) {
R
RubaXa 已提交
1227
			if ((el.nodeName.toUpperCase() !== 'TEMPLATE') && (selector === '>*' || _matches(el, selector))) {
1228 1229
				index++;
			}
1230
		}
1231

1232 1233
		return index;
	}
R
RubaXa 已提交
1234

1235
	function _matches(/**HTMLElement*/el, /**String*/selector) {
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
		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)
			);
1246
		}
1247 1248

		return false;
1249 1250
	}

R
RubaXa 已提交
1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271
	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);
			}
		};
	}

1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283
	function _extend(dst, src) {
		if (dst && src) {
			for (var key in src) {
				if (src.hasOwnProperty(key)) {
					dst[key] = src[key];
				}
			}
		}

		return dst;
	}

1284 1285 1286 1287 1288 1289 1290 1291 1292
	function _clone(el) {
		return $
			? $(el).clone(true)[0]
			: (Polymer && Polymer.dom
				? Polymer.dom(el).cloneNode(true)
				: el.cloneNode(true)
			);
	}

R
RubaXa 已提交
1293

R
RubaXa 已提交
1294 1295 1296 1297 1298 1299
	// Export utils
	Sortable.utils = {
		on: _on,
		off: _off,
		css: _css,
		find: _find,
R
RubaXa 已提交
1300 1301 1302
		is: function (el, selector) {
			return !!_closest(el, selector, el);
		},
1303
		extend: _extend,
R
RubaXa 已提交
1304
		throttle: _throttle,
R
RubaXa 已提交
1305
		closest: _closest,
1306
		toggleClass: _toggleClass,
1307
		clone: _clone,
1308
		index: _index
R
RubaXa 已提交
1309 1310 1311
	};


1312 1313 1314 1315 1316 1317
	/**
	 * Create sortable instance
	 * @param {HTMLElement}  el
	 * @param {Object}      [options]
	 */
	Sortable.create = function (el, options) {
R
RubaXa 已提交
1318
		return new Sortable(el, options);
1319
	};
R
RubaXa 已提交
1320

R
RubaXa 已提交
1321 1322

	// Export
R
RubaXa 已提交
1323
	Sortable.version = '1.4.2';
1324
	return Sortable;
R
RubaXa 已提交
1325
});