Sortable.js 29.4 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
		autoScroll = {},
R
RubaXa 已提交
46

R
RubaXa 已提交
47 48
		tapEvt,
		touchEvt,
R
RubaXa 已提交
49

C
ChiefORZ 已提交
50 51
		moved,

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

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

R
RubaXa 已提交
57 58 59
		win = window,
		document = win.document,
		parseInt = win.parseInt,
R
RubaXa 已提交
60

R
RubaXa 已提交
61
		supportDraggable = !!('draggable' in document.createElement('div')),
62 63 64 65 66
		supportCssPointerEvents = (function (el) {
			el = document.createElement('x');
			el.style.cssText = 'pointer-events:auto';
			return el.style.pointerEvents === 'auto';
		})(),
R
RubaXa 已提交
67

R
RubaXa 已提交
68
		_silent = false,
R
RubaXa 已提交
69

R
RubaXa 已提交
70
		abs = Math.abs,
R
RubaXa 已提交
71
		min = Math.min,
R
RubaXa 已提交
72
		slice = [].slice,
R
RubaXa 已提交
73

74 75 76 77 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
		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) {
140
								win.scrollTo(win.pageXOffset + vx * speed, win.pageYOffset + vy * speed);
141 142 143 144 145 146 147 148
							} else {
								vy && (el.scrollTop += vy * speed);
								vx && (el.scrollLeft += vx * speed);
							}
						}, 24);
					}
				}
			}
R
RubaXa 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
		}, 30),

		_prepareGroup = function (options) {
			var group = options.group;

			if (!group || typeof group != 'object') {
				group = options.group = {name: group};
			}

			['pull', 'put'].forEach(function (key) {
				if (!(key in group)) {
					group[key] = true;
				}
			});

			options.groups = ' ' + group.name + (group.put.join ? ' ' + group.put.join(' ') : '') + ' ';
		}
R
RubaXa 已提交
166 167 168
	;


169

R
RubaXa 已提交
170 171 172
	/**
	 * @class  Sortable
	 * @param  {HTMLElement}  el
173
	 * @param  {Object}       [options]
R
RubaXa 已提交
174
	 */
R
RubaXa 已提交
175
	function Sortable(el, options) {
R
RubaXa 已提交
176 177 178 179
		if (!(el && el.nodeType && el.nodeType === 1)) {
			throw 'Sortable: `el` must be HTMLElement, and not ' + {}.toString.call(el);
		}

R
RubaXa 已提交
180
		this.el = el; // root element
181
		this.options = options = _extend({}, options);
R
RubaXa 已提交
182 183


184 185 186 187
		// Export instance
		el[expando] = this;


R
RubaXa 已提交
188
		// Default options
189 190
		var defaults = {
			group: Math.random(),
R
RubaXa 已提交
191
			sort: true,
R
RubaXa 已提交
192
			disabled: false,
193 194
			store: null,
			handle: null,
R
RubaXa 已提交
195 196 197
			scroll: true,
			scrollSensitivity: 30,
			scrollSpeed: 10,
R
RubaXa 已提交
198
			draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*',
199
			ghostClass: 'sortable-ghost',
R
RubaXa 已提交
200
			chosenClass: 'sortable-chosen',
201
			ignore: 'a, img',
202
			filter: null,
R
RubaXa 已提交
203 204 205
			animation: 0,
			setData: function (dataTransfer, dragEl) {
				dataTransfer.setData('Text', dragEl.textContent);
206 207
			},
			dropBubble: false,
R
RubaXa 已提交
208
			dragoverBubble: false,
209
			dataIdAttr: 'data-id',
210
			delay: 0,
C
ChiefORZ 已提交
211 212
			forceFallback: false,
			fallbackClass: 'sortable-fallback',
213 214
			fallbackOnBody: false,
			fallbackTolerance: 0
R
RubaXa 已提交
215
		};
216

R
RubaXa 已提交
217

218 219
		// Set default options
		for (var name in defaults) {
R
RubaXa 已提交
220
			!(name in options) && (options[name] = defaults[name]);
221
		}
R
RubaXa 已提交
222

R
RubaXa 已提交
223
		_prepareGroup(options);
R
RubaXa 已提交
224

R
* JSDoc  
RubaXa 已提交
225
		// Bind all private methods
R
RubaXa 已提交
226 227
		for (var fn in this) {
			if (fn.charAt(0) === '_') {
228
				this[fn] = this[fn].bind(this);
R
RubaXa 已提交
229 230 231
			}
		}

R
RubaXa 已提交
232 233
		// Setup drag mode
		this.nativeDraggable = options.forceFallback ? false : supportDraggable;
R
RubaXa 已提交
234 235 236 237 238

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

R
RubaXa 已提交
239
		if (this.nativeDraggable) {
240 241 242
			_on(el, 'dragover', this);
			_on(el, 'dragenter', this);
		}
R
RubaXa 已提交
243 244

		touchDragOverListeners.push(this._onDragOver);
245 246 247

		// Restore sorting
		options.store && this.sort(options.store.get(this));
R
RubaXa 已提交
248 249 250
	}


251
	Sortable.prototype = /** @lends Sortable.prototype */ {
R
RubaXa 已提交
252 253
		constructor: Sortable,

254
		_onTapStart: function (/** Event|TouchEvent */evt) {
255 256
			var _this = this,
				el = this.el,
257 258 259 260 261 262
				options = this.options,
				type = evt.type,
				touch = evt.touches && evt.touches[0],
				target = (touch || evt).target,
				originalTarget = target,
				filter = options.filter;
R
RubaXa 已提交
263 264

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

269 270
			if (type === 'mousedown' && evt.button !== 0 || options.disabled) {
				return; // only left button or enabled
R
RubaXa 已提交
271
			}
R
RubaXa 已提交
272

273
			target = _closest(target, options.draggable, el);
274

275 276
			if (!target) {
				return;
277
			}
278

279 280 281
			if (options.handle && !_closest(originalTarget, options.handle, el)) {
				return;
			}
282

R
RubaXa 已提交
283
			// Get the index of the dragged element within its parent
284
			oldIndex = _index(target, options.draggable);
285 286 287 288

			// Check filter
			if (typeof filter === 'function') {
				if (filter.call(this, evt, target, this)) {
289
					_dispatchEvent(_this, originalTarget, 'filter', target, el, oldIndex);
290 291 292
					evt.preventDefault();
					return; // cancel dnd
				}
293
			}
294 295 296
			else if (filter) {
				filter = filter.split(',').some(function (criteria) {
					criteria = _closest(originalTarget, criteria.trim(), el);
297

298
					if (criteria) {
299
						_dispatchEvent(_this, criteria, 'filter', target, el, oldIndex);
300 301 302 303 304 305 306
						return true;
					}
				});

				if (filter) {
					evt.preventDefault();
					return; // cancel dnd
307 308 309
				}
			}

310 311
			// Prepare `dragstart`
			this._prepareDragStart(evt, touch, target);
312 313
		},

314 315 316 317 318 319
		_prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target) {
			var _this = this,
				el = _this.el,
				options = _this.options,
				ownerDocument = el.ownerDocument,
				dragStartFn;
320 321 322 323

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

324
				rootEl = el;
325
				dragEl = target;
R
RubaXa 已提交
326
				parentEl = dragEl.parentNode;
327
				nextEl = dragEl.nextSibling;
328 329
				activeGroup = options.group;

330 331
				this._lastX = (touch || evt).clientX;
				this._lastY = (touch || evt).clientY;
S
sp-kilobug 已提交
332

333 334 335 336
				dragStartFn = function () {
					// Delayed drag has been triggered
					// we can re-enable the events: touchmove/mousemove
					_this._disableDelayedDrag();
337

338 339
					// Make the element draggable
					dragEl.draggable = true;
340

R
RubaXa 已提交
341 342
					// Chosen item
					_toggleClass(dragEl, _this.options.chosenClass, true);
343 344 345

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

					// Drag start event
348
					_dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, oldIndex);
349 350
				};

R
RubaXa 已提交
351 352 353 354 355
				// Disable "draggable"
				options.ignore.split(',').forEach(function (criteria) {
					_find(dragEl, criteria.trim(), _disableDraggable);
				});

356 357 358 359 360
				_on(ownerDocument, 'mouseup', _this._onDrop);
				_on(ownerDocument, 'touchend', _this._onDrop);
				_on(ownerDocument, 'touchcancel', _this._onDrop);

				if (options.delay) {
361 362
					// If the user moves the pointer or let go the click or touch
					// before the delay has been reached:
363
					// disable the delayed drag
364 365 366
					_on(ownerDocument, 'mouseup', _this._disableDelayedDrag);
					_on(ownerDocument, 'touchend', _this._disableDelayedDrag);
					_on(ownerDocument, 'touchcancel', _this._disableDelayedDrag);
367 368 369 370 371 372
					_on(ownerDocument, 'mousemove', _this._disableDelayedDrag);
					_on(ownerDocument, 'touchmove', _this._disableDelayedDrag);

					_this._dragStartTimer = setTimeout(dragStartFn, options.delay);
				} else {
					dragStartFn();
373 374 375
				}
			}
		},
R
RubaXa 已提交
376

377 378
		_disableDelayedDrag: function () {
			var ownerDocument = this.el.ownerDocument;
379

380
			clearTimeout(this._dragStartTimer);
381 382 383
			_off(ownerDocument, 'mouseup', this._disableDelayedDrag);
			_off(ownerDocument, 'touchend', this._disableDelayedDrag);
			_off(ownerDocument, 'touchcancel', this._disableDelayedDrag);
384 385 386
			_off(ownerDocument, 'mousemove', this._disableDelayedDrag);
			_off(ownerDocument, 'touchmove', this._disableDelayedDrag);
		},
R
RubaXa 已提交
387

388 389 390 391 392 393 394 395
		_triggerDragStart: function (/** Touch */touch) {
			if (touch) {
				// Touch device support
				tapEvt = {
					target: dragEl,
					clientX: touch.clientX,
					clientY: touch.clientY
				};
396

397
				this._onDragStart(tapEvt, 'touch');
R
RubaXa 已提交
398
			}
R
RubaXa 已提交
399
			else if (!this.nativeDraggable) {
400 401 402 403 404
				this._onDragStart(tapEvt, true);
			}
			else {
				_on(dragEl, 'dragend', this);
				_on(rootEl, 'dragstart', this._onDragStart);
405 406
			}

407 408 409 410 411
			try {
				if (document.selection) {
					document.selection.empty();
				} else {
					window.getSelection().removeAllRanges();
412
				}
413
			} catch (err) {
414
			}
415
		},
416

417 418 419 420
		_dragStarted: function () {
			if (rootEl && dragEl) {
				// Apply effect
				_toggleClass(dragEl, this.options.ghostClass, true);
R
RubaXa 已提交
421

422
				Sortable.active = this;
423

424
				// Drag start event
425
				_dispatchEvent(this, rootEl, 'start', dragEl, rootEl, oldIndex);
426
			}
R
RubaXa 已提交
427 428
		},

R
RubaXa 已提交
429 430
		_emulateDragOver: function () {
			if (touchEvt) {
R
RubaXa 已提交
431
				if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY) {
432 433
					return;
				}
R
RubaXa 已提交
434 435 436

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

438 439 440
				if (!supportCssPointerEvents) {
					_css(ghostEl, 'display', 'none');
				}
R
RubaXa 已提交
441

R
RubaXa 已提交
442
				var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY),
R
RubaXa 已提交
443
					parent = target,
444
					groupName = ' ' + this.options.group.name + '',
R
RubaXa 已提交
445
					i = touchDragOverListeners.length;
R
RubaXa 已提交
446

R
RubaXa 已提交
447 448
				if (parent) {
					do {
449
						if (parent[expando] && parent[expando].options.groups.indexOf(groupName) > -1) {
R
RubaXa 已提交
450 451 452 453 454 455 456 457 458 459 460 461 462
							while (i--) {
								touchDragOverListeners[i]({
									clientX: touchEvt.clientX,
									clientY: touchEvt.clientY,
									target: target,
									rootEl: parent
								});
							}

							break;
						}

						target = parent; // store last element
L
Larry Davis 已提交
463
					}
R
RubaXa 已提交
464 465
					/* jshint boss:true */
					while (parent = parent.parentNode);
R
RubaXa 已提交
466 467
				}

468 469 470
				if (!supportCssPointerEvents) {
					_css(ghostEl, 'display', '');
				}
R
RubaXa 已提交
471 472 473 474
			}
		},


R
RubaXa 已提交
475 476
		_onTouchMove: function (/**TouchEvent*/evt) {
			if (tapEvt) {
R
RubaXa 已提交
477 478 479 480 481 482
				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 已提交
483

484
				// only set the status to dragging, when we are actually dragging
R
RubaXa 已提交
485
				if (!Sortable.active) {
R
RubaXa 已提交
486 487 488 489
					if (fallbackTolerance &&
						min(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY)) < fallbackTolerance
					) {
						return;
S
sp-kilobug 已提交
490
					}
R
RubaXa 已提交
491

492 493
					this._dragStarted();
				}
R
RubaXa 已提交
494

495 496
				// as well as creating the ghost element on the document body
				this._appendGhost();
R
RubaXa 已提交
497

A
Alex Wild 已提交
498
				moved = true;
R
RubaXa 已提交
499
				touchEvt = touch;
R
RubaXa 已提交
500 501 502 503 504 505

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

M
Marius Petcu 已提交
506
				evt.preventDefault();
R
RubaXa 已提交
507 508 509
			}
		},

R
RubaXa 已提交
510 511
		_appendGhost: function () {
			if (!ghostEl) {
R
RubaXa 已提交
512 513
				var rect = dragEl.getBoundingClientRect(),
					css = _css(dragEl),
R
RubaXa 已提交
514
					options = this.options,
R
RubaXa 已提交
515
					ghostRect;
R
RubaXa 已提交
516

517
				ghostEl = dragEl.cloneNode(true);
R
RubaXa 已提交
518

R
RubaXa 已提交
519 520
				_toggleClass(ghostEl, options.ghostClass, false);
				_toggleClass(ghostEl, options.fallbackClass, true);
C
ChiefORZ 已提交
521

R
RubaXa 已提交
522 523
				_css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10));
				_css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10));
R
RubaXa 已提交
524 525
				_css(ghostEl, 'width', rect.width);
				_css(ghostEl, 'height', rect.height);
R
RubaXa 已提交
526 527 528
				_css(ghostEl, 'opacity', '0.8');
				_css(ghostEl, 'position', 'fixed');
				_css(ghostEl, 'zIndex', '100000');
529
				_css(ghostEl, 'pointerEvents', 'none');
R
RubaXa 已提交
530

R
RubaXa 已提交
531
				options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
R
RubaXa 已提交
532 533 534

				// Fixing dimensions.
				ghostRect = ghostEl.getBoundingClientRect();
R
RubaXa 已提交
535 536
				_css(ghostEl, 'width', rect.width * 2 - ghostRect.width);
				_css(ghostEl, 'height', rect.height * 2 - ghostRect.height);
537 538 539 540 541 542 543 544 545 546 547 548 549
			}
		},

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

			this._offUpEvents();

			if (activeGroup.pull == 'clone') {
				cloneEl = dragEl.cloneNode(true);
				_css(cloneEl, 'display', 'none');
				rootEl.insertBefore(cloneEl, dragEl);
R
RubaXa 已提交
550
				_dispatchEvent(this, rootEl, 'clone', dragEl);
551 552 553
			}

			if (useFallback) {
R
RubaXa 已提交
554 555 556 557 558 559 560 561 562 563
				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 已提交
564

565
				this._loopId = setInterval(this._emulateDragOver, 50);
R
RubaXa 已提交
566 567
			}
			else {
R
RubaXa 已提交
568 569 570 571
				if (dataTransfer) {
					dataTransfer.effectAllowed = 'move';
					options.setData && options.setData.call(this, dataTransfer, dragEl);
				}
R
RubaXa 已提交
572

R
RubaXa 已提交
573
				_on(document, 'drop', this);
574
				setTimeout(this._dragStarted, 0);
R
RubaXa 已提交
575 576 577
			}
		},

R
RubaXa 已提交
578
		_onDragOver: function (/**Event*/evt) {
R
RubaXa 已提交
579 580 581 582 583 584
			var el = this.el,
				target,
				dragRect,
				revert,
				options = this.options,
				group = options.group,
R
RubaXa 已提交
585
				groupPut = group.put,
586 587
				isOwner = (activeGroup === group),
				canSort = options.sort;
R
RubaXa 已提交
588

R
RubaXa 已提交
589 590
			if (evt.preventDefault !== void 0) {
				evt.preventDefault();
591
				!options.dragoverBubble && evt.stopPropagation();
R
RubaXa 已提交
592
			}
R
RubaXa 已提交
593

A
Alex Wild 已提交
594 595
			moved = true;

R
RubaXa 已提交
596
			if (activeGroup && !options.disabled &&
597
				(isOwner
R
RubaXa 已提交
598
					? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list
R
RubaXa 已提交
599 600 601 602
					: activeGroup.pull && groupPut && (
						(activeGroup.name === group.name) || // by Name
						(groupPut.indexOf && ~groupPut.indexOf(activeGroup.name)) // by Array
					)
603
				) &&
R
RubaXa 已提交
604
				(evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback
R
RubaXa 已提交
605
			) {
R
RubaXa 已提交
606 607 608 609 610 611 612
				// Smart auto-scrolling
				_autoScroll(evt, options, this.el);

				if (_silent) {
					return;
				}

R
RubaXa 已提交
613
				target = _closest(evt.target, options.draggable, el);
R
RubaXa 已提交
614 615
				dragRect = dragEl.getBoundingClientRect();

616
				if (revert) {
R
RubaXa 已提交
617
					_cloneHide(true);
R
RubaXa 已提交
618
					parentEl = rootEl; // actualization
R
RubaXa 已提交
619

620 621 622 623 624 625 626
					if (cloneEl || nextEl) {
						rootEl.insertBefore(dragEl, cloneEl || nextEl);
					}
					else if (!canSort) {
						rootEl.appendChild(dragEl);
					}

R
RubaXa 已提交
627 628
					return;
				}
R
RubaXa 已提交
629

R
RubaXa 已提交
630

R
RubaXa 已提交
631
				if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
632
					(el === evt.target) && (target = _ghostIsLast(el, evt))
R
RubaXa 已提交
633
				) {
R
RubaXa 已提交
634

R
RubaXa 已提交
635 636 637 638
					if (target) {
						if (target.animated) {
							return;
						}
R
RubaXa 已提交
639

R
RubaXa 已提交
640 641
						targetRect = target.getBoundingClientRect();
					}
R
RubaXa 已提交
642

R
RubaXa 已提交
643 644
					_cloneHide(isOwner);

R
RubaXa 已提交
645
					if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect) !== false) {
646 647
						if (!dragEl.contains(el)) {
							el.appendChild(dragEl);
R
RubaXa 已提交
648
							parentEl = el; // actualization
649
						}
R
RubaXa 已提交
650

R
RubaXa 已提交
651 652 653
						this._animate(dragRect, dragEl);
						target && this._animate(targetRect, target);
					}
R
RubaXa 已提交
654
				}
R
RubaXa 已提交
655 656
				else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) {
					if (lastEl !== target) {
R
RubaXa 已提交
657
						lastEl = target;
R
RubaXa 已提交
658
						lastCSS = _css(target);
S
sp-kilobug 已提交
659
						lastParentCSS = _css(target.parentNode);
R
RubaXa 已提交
660 661 662
					}


R
RubaXa 已提交
663 664 665
					var targetRect = target.getBoundingClientRect(),
						width = targetRect.right - targetRect.left,
						height = targetRect.bottom - targetRect.top,
S
sp-kilobug 已提交
666 667
						floating = /left|right|inline/.test(lastCSS.cssFloat + lastCSS.display)
							|| (lastParentCSS.display == 'flex' && lastParentCSS['flex-direction'].indexOf('row') === 0),
R
RubaXa 已提交
668 669 670 671
						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 已提交
672
						moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect),
R
RubaXa 已提交
673
						after
R
RubaXa 已提交
674
					;
R
RubaXa 已提交
675

R
RubaXa 已提交
676
					if (moveVector !== false) {
R
RubaXa 已提交
677 678
						_silent = true;
						setTimeout(_unsilent, 30);
R
RubaXa 已提交
679

R
RubaXa 已提交
680
						_cloneHide(isOwner);
R
RubaXa 已提交
681

R
RubaXa 已提交
682 683 684 685
						if (moveVector === 1 || moveVector === -1) {
							after = (moveVector === 1);
						}
						else if (floating) {
S
sp-kilobug 已提交
686 687
							var elTop = dragEl.offsetTop,
								tgTop = target.offsetTop;
R
RubaXa 已提交
688 689

							if (elTop === tgTop) {
S
sp-kilobug 已提交
690
								after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide;
691
							}
692
							else if (target.previousElementSibling === dragEl || dragEl.previousElementSibling === target) {
693
								after = (evt.clientY - targetRect.top) / height > 0.5;
S
sp-kilobug 已提交
694 695 696
							} else {
								after = tgTop > elTop;
							}
R
RubaXa 已提交
697 698 699
						} else {
							after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
						}
R
RubaXa 已提交
700

701 702 703 704 705 706
						if (!dragEl.contains(el)) {
							if (after && !nextSibling) {
								el.appendChild(dragEl);
							} else {
								target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
							}
R
RubaXa 已提交
707
						}
R
RubaXa 已提交
708

R
RubaXa 已提交
709 710
						parentEl = dragEl.parentNode; // actualization

R
RubaXa 已提交
711 712 713
						this._animate(dragRect, dragEl);
						this._animate(targetRect, target);
					}
R
RubaXa 已提交
714 715 716 717
				}
			}
		},

718 719 720 721 722 723
		_animate: function (prevRect, target) {
			var ms = this.options.animation;

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

R
RubaXa 已提交
724
				_css(target, 'transition', 'none');
725 726 727 728 729 730 731
				_css(target, 'transform', 'translate3d('
					+ (prevRect.left - currentRect.left) + 'px,'
					+ (prevRect.top - currentRect.top) + 'px,0)'
				);

				target.offsetWidth; // repaint

R
RubaXa 已提交
732
				_css(target, 'transition', 'all ' + ms + 'ms');
733 734
				_css(target, 'transform', 'translate3d(0,0,0)');

R
* anim  
RubaXa 已提交
735 736
				clearTimeout(target.animated);
				target.animated = setTimeout(function () {
737
					_css(target, 'transition', '');
738
					_css(target, 'transform', '');
739 740 741 742 743
					target.animated = false;
				}, ms);
			}
		},

744
		_offUpEvents: function () {
745 746
			var ownerDocument = this.el.ownerDocument;

747
			_off(document, 'touchmove', this._onTouchMove);
748 749 750
			_off(ownerDocument, 'mouseup', this._onDrop);
			_off(ownerDocument, 'touchend', this._onDrop);
			_off(ownerDocument, 'touchcancel', this._onDrop);
751
		},
R
RubaXa 已提交
752

R
RubaXa 已提交
753
		_onDrop: function (/**Event*/evt) {
754 755
			var el = this.el,
				options = this.options;
R
RubaXa 已提交
756

R
RubaXa 已提交
757
			clearInterval(this._loopId);
R
RubaXa 已提交
758
			clearInterval(autoScroll.pid);
R
RubaXa 已提交
759
			clearTimeout(this._dragStartTimer);
760

R
RubaXa 已提交
761
			// Unbind events
R
RubaXa 已提交
762
			_off(document, 'mousemove', this._onTouchMove);
763

R
RubaXa 已提交
764
			if (this.nativeDraggable) {
765 766 767
				_off(document, 'drop', this);
				_off(el, 'dragstart', this._onDragStart);
			}
R
RubaXa 已提交
768

769
			this._offUpEvents();
R
RubaXa 已提交
770

R
RubaXa 已提交
771
			if (evt) {
R
RubaXa 已提交
772
				if (moved) {
C
ChiefORZ 已提交
773 774 775
					evt.preventDefault();
					!options.dropBubble && evt.stopPropagation();
				}
R
RubaXa 已提交
776

R
RubaXa 已提交
777
				ghostEl && ghostEl.parentNode.removeChild(ghostEl);
R
RubaXa 已提交
778

R
RubaXa 已提交
779
				if (dragEl) {
R
RubaXa 已提交
780
					if (this.nativeDraggable) {
781 782
						_off(dragEl, 'dragend', this);
					}
R
RubaXa 已提交
783

784
					_disableDraggable(dragEl);
R
RubaXa 已提交
785 786

					// Remove class's
R
RubaXa 已提交
787
					_toggleClass(dragEl, this.options.ghostClass, false);
R
RubaXa 已提交
788
					_toggleClass(dragEl, this.options.chosenClass, false);
R
RubaXa 已提交
789

790
					if (rootEl !== parentEl) {
791
						newIndex = _index(dragEl, options.draggable);
R
RubaXa 已提交
792

793
						if (newIndex >= 0) {
794 795 796
							// 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 已提交
797

798 799
							// Add event
							_dispatchEvent(null, parentEl, 'add', dragEl, rootEl, oldIndex, newIndex);
800

801 802 803
							// Remove event
							_dispatchEvent(this, rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex);
						}
R
RubaXa 已提交
804
					}
R
RubaXa 已提交
805 806
					else {
						// Remove clone
R
RubaXa 已提交
807
						cloneEl && cloneEl.parentNode.removeChild(cloneEl);
R
RubaXa 已提交
808

R
RubaXa 已提交
809 810
						if (dragEl.nextSibling !== nextEl) {
							// Get the index of the dragged element within its parent
811
							newIndex = _index(dragEl, options.draggable);
812 813

							if (newIndex >= 0) {
814 815 816 817
								// drag & drop within the same list
								_dispatchEvent(this, rootEl, 'update', dragEl, rootEl, oldIndex, newIndex);
								_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
							}
R
RubaXa 已提交
818
						}
R
RubaXa 已提交
819
					}
820

R
RubaXa 已提交
821
					if (Sortable.active) {
R
RubaXa 已提交
822
						if (newIndex === null || newIndex === -1) {
R
RubaXa 已提交
823 824 825
							newIndex = oldIndex;
						}

R
RubaXa 已提交
826 827 828 829 830
						_dispatchEvent(this, rootEl, 'end', dragEl, rootEl, oldIndex, newIndex);

						// Save sorting
						this.save();
					}
R
RubaXa 已提交
831
				}
R
RubaXa 已提交
832

833 834 835
			}
			this._nulling();
		},
R
RubaXa 已提交
836

837 838 839 840 841 842 843 844
		_nulling: function() {
			// Nulling
			rootEl =
			dragEl =
			parentEl =
			ghostEl =
			nextEl =
			cloneEl =
845

846 847
			scrollEl =
			scrollParentEl =
R
RubaXa 已提交
848

849 850
			tapEvt =
			touchEvt =
C
ChiefORZ 已提交
851

852 853
			moved =
			newIndex =
R
RubaXa 已提交
854

855 856
			lastEl =
			lastCSS =
R
RubaXa 已提交
857

858 859 860
			activeGroup =
			Sortable.active = null;
		},
R
RubaXa 已提交
861

R
RubaXa 已提交
862 863 864
		handleEvent: function (/**Event*/evt) {
			var type = evt.type;

R
RubaXa 已提交
865
			if (type === 'dragover' || type === 'dragenter') {
R
RubaXa 已提交
866 867 868 869
				if (dragEl) {
					this._onDragOver(evt);
					_globalDragOver(evt);
				}
R
RubaXa 已提交
870
			}
R
RubaXa 已提交
871
			else if (type === 'drop' || type === 'dragend') {
R
RubaXa 已提交
872 873
				this._onDrop(evt);
			}
R
RubaXa 已提交
874 875 876
		},


877 878 879 880 881 882 883 884 885
		/**
		 * Serializes the item into an array of string.
		 * @returns {String[]}
		 */
		toArray: function () {
			var order = [],
				el,
				children = this.el.children,
				i = 0,
R
RubaXa 已提交
886 887
				n = children.length,
				options = this.options;
888 889 890

			for (; i < n; i++) {
				el = children[i];
R
RubaXa 已提交
891
				if (_closest(el, options.draggable, this.el)) {
R
RubaXa 已提交
892
					order.push(el.getAttribute(options.dataIdAttr) || _generateId(el));
R
RubaXa 已提交
893
				}
894 895 896 897 898 899 900 901 902 903 904
			}

			return order;
		},


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

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

R
RubaXa 已提交
910
				if (_closest(el, this.options.draggable, rootEl)) {
R
RubaXa 已提交
911 912 913
					items[id] = el;
				}
			}, this);
914 915 916

			order.forEach(function (id) {
				if (items[id]) {
R
RubaXa 已提交
917 918
					rootEl.removeChild(items[id]);
					rootEl.appendChild(items[id]);
919 920 921 922 923
				}
			});
		},


R
RubaXa 已提交
924 925 926 927 928 929 930 931 932
		/**
		 * Save the current sorting
		 */
		save: function () {
			var store = this.options.store;
			store && store.set(this);
		},


933 934 935 936 937 938 939 940 941 942 943
		/**
		 * 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);
		},


944 945 946 947 948 949 950 951 952 953 954 955 956
		/**
		 * 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 已提交
957 958 959 960

				if (name === 'group') {
					_prepareGroup(options);
				}
961 962 963 964
			}
		},


965 966 967 968
		/**
		 * Destroy
		 */
		destroy: function () {
969
			var el = this.el;
R
RubaXa 已提交
970

971
			el[expando] = null;
972

R
RubaXa 已提交
973 974 975
			_off(el, 'mousedown', this._onTapStart);
			_off(el, 'touchstart', this._onTapStart);

R
RubaXa 已提交
976
			if (this.nativeDraggable) {
977 978 979
				_off(el, 'dragover', this);
				_off(el, 'dragenter', this);
			}
R
RubaXa 已提交
980

981
			// Remove draggable attributes
R
RubaXa 已提交
982
			Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) {
983 984 985
				el.removeAttribute('draggable');
			});

R
RubaXa 已提交
986 987 988 989
			touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1);

			this._onDrop();

990
			this.el = el = null;
R
RubaXa 已提交
991 992 993
		}
	};

994

R
RubaXa 已提交
995 996 997 998 999 1000 1001 1002 1003
	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 已提交
1004
	function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) {
R
RubaXa 已提交
1005
		if (el) {
R
RubaXa 已提交
1006 1007 1008
			ctx = ctx || document;

			do {
R
RubaXa 已提交
1009
				if (
1010 1011
					(selector === '>*' && el.parentNode === ctx)
					|| _matches(el, selector)
R
RubaXa 已提交
1012 1013
				) {
					return el;
R
RubaXa 已提交
1014 1015
				}
			}
R
RubaXa 已提交
1016
			while (el !== ctx && (el = el.parentNode));
R
RubaXa 已提交
1017 1018
		}

R
RubaXa 已提交
1019
		return null;
R
RubaXa 已提交
1020 1021 1022
	}


1023
	function _globalDragOver(/**Event*/evt) {
1024 1025 1026
		if (evt.dataTransfer) {
			evt.dataTransfer.dropEffect = 'move';
		}
R
RubaXa 已提交
1027 1028 1029 1030
		evt.preventDefault();
	}


R
RubaXa 已提交
1031
	function _on(el, event, fn) {
R
RubaXa 已提交
1032 1033 1034 1035
		el.addEventListener(event, fn, false);
	}


R
RubaXa 已提交
1036
	function _off(el, event, fn) {
R
RubaXa 已提交
1037 1038 1039 1040
		el.removeEventListener(event, fn, false);
	}


R
RubaXa 已提交
1041 1042 1043
	function _toggleClass(el, name, state) {
		if (el) {
			if (el.classList) {
R
RubaXa 已提交
1044 1045 1046
				el.classList[state ? 'add' : 'remove'](name);
			}
			else {
B
Bogdan Mustiata 已提交
1047 1048
				var className = (' ' + el.className + ' ').replace(RSPACE, ' ').replace(' ' + name + ' ', ' ');
				el.className = (className + (state ? ' ' + name : '')).replace(RSPACE, ' ');
R
RubaXa 已提交
1049 1050 1051 1052 1053
			}
		}
	}


R
RubaXa 已提交
1054
	function _css(el, prop, val) {
R
RubaXa 已提交
1055 1056
		var style = el && el.style;

R
RubaXa 已提交
1057 1058 1059
		if (style) {
			if (val === void 0) {
				if (document.defaultView && document.defaultView.getComputedStyle) {
R
RubaXa 已提交
1060 1061
					val = document.defaultView.getComputedStyle(el, '');
				}
R
RubaXa 已提交
1062 1063
				else if (el.currentStyle) {
					val = el.currentStyle;
R
RubaXa 已提交
1064
				}
R
RubaXa 已提交
1065 1066 1067 1068 1069 1070 1071 1072 1073

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

				style[prop] = val + (typeof val === 'string' ? '' : 'px');
R
RubaXa 已提交
1074 1075 1076 1077 1078
			}
		}
	}


R
RubaXa 已提交
1079 1080
	function _find(ctx, tagName, iterator) {
		if (ctx) {
R
RubaXa 已提交
1081
			var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length;
R
RubaXa 已提交
1082

R
RubaXa 已提交
1083 1084
			if (iterator) {
				for (; i < n; i++) {
R
RubaXa 已提交
1085 1086 1087
					iterator(list[i], i);
				}
			}
R
RubaXa 已提交
1088

R
RubaXa 已提交
1089
			return list;
R
RubaXa 已提交
1090
		}
R
RubaXa 已提交
1091 1092

		return [];
R
RubaXa 已提交
1093 1094 1095
	}


R
RubaXa 已提交
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111

	function _dispatchEvent(sortable, rootEl, name, targetEl, fromEl, startIndex, newIndex) {
		var evt = document.createEvent('Event'),
			options = (sortable || rootEl[expando]).options,
			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 已提交
1112 1113
		rootEl.dispatchEvent(evt);

R
RubaXa 已提交
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
		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 已提交
1126 1127
		evt = document.createEvent('Event');
		evt.initEvent('move', true, true);
R
RubaXa 已提交
1128

C
ChiefORZ 已提交
1129 1130 1131 1132 1133 1134
		evt.to = toEl;
		evt.from = fromEl;
		evt.dragged = dragEl;
		evt.draggedRect = dragRect;
		evt.related = targetEl || toEl;
		evt.relatedRect = targetRect || toEl.getBoundingClientRect();
R
RubaXa 已提交
1135

C
ChiefORZ 已提交
1136
		fromEl.dispatchEvent(evt);
R
RubaXa 已提交
1137

C
ChiefORZ 已提交
1138
		if (onMoveFn) {
R
RubaXa 已提交
1139 1140 1141
			retVal = onMoveFn.call(sortable, evt);
		}

R
RubaXa 已提交
1142
		return retVal;
R
RubaXa 已提交
1143 1144 1145
	}


R
RubaXa 已提交
1146
	function _disableDraggable(el) {
R
RubaXa 已提交
1147
		el.draggable = false;
R
RubaXa 已提交
1148 1149 1150
	}


R
RubaXa 已提交
1151
	function _unsilent() {
R
RubaXa 已提交
1152 1153 1154 1155
		_silent = false;
	}


R
RubaXa 已提交
1156
	/** @returns {HTMLElement|false} */
1157
	function _ghostIsLast(el, evt) {
R
RubaXa 已提交
1158
		var lastEl = el.lastElementChild,
1159
				rect = lastEl.getBoundingClientRect();
R
RubaXa 已提交
1160

1161
		return ((evt.clientY - (rect.top + rect.height) > 5) || (evt.clientX - (rect.right + rect.width) > 5)) && lastEl; // min delta
R
RubaXa 已提交
1162 1163 1164
	}


1165 1166 1167 1168 1169 1170 1171
	/**
	 * Generate id
	 * @param   {HTMLElement} el
	 * @returns {String}
	 * @private
	 */
	function _generateId(el) {
R
RubaXa 已提交
1172
		var str = el.tagName + el.className + el.src + el.href + el.textContent,
1173
			i = str.length,
R
RubaXa 已提交
1174
			sum = 0;
1175

1176 1177 1178
		while (i--) {
			sum += str.charCodeAt(i);
		}
1179

1180 1181 1182
		return sum.toString(36);
	}

1183
	/**
1184 1185
	 * Returns the index of an element within its parent for a selected set of
	 * elements
1186
	 * @param  {HTMLElement} el
1187
	 * @param  {selector} selector
1188
	 * @return {number}
1189
	 */
1190
	function _index(el, selector) {
1191 1192
		var index = 0;

1193 1194 1195
		if (!el || !el.parentNode) {
			return -1;
		}
1196

1197
		while (el && (el = el.previousElementSibling)) {
1198 1199
			if (el.nodeName.toUpperCase() !== 'TEMPLATE'
					&& _matches(el, selector)) {
1200 1201
				index++;
			}
1202
		}
1203

1204 1205
		return index;
	}
R
RubaXa 已提交
1206

1207
	function _matches(/**HTMLElement*/el, /**String*/selector) {
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217
		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)
			);
1218
		}
1219 1220

		return false;
1221 1222
	}

R
RubaXa 已提交
1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243
	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);
			}
		};
	}

1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255
	function _extend(dst, src) {
		if (dst && src) {
			for (var key in src) {
				if (src.hasOwnProperty(key)) {
					dst[key] = src[key];
				}
			}
		}

		return dst;
	}

R
RubaXa 已提交
1256

R
RubaXa 已提交
1257 1258 1259 1260 1261 1262
	// Export utils
	Sortable.utils = {
		on: _on,
		off: _off,
		css: _css,
		find: _find,
R
RubaXa 已提交
1263 1264 1265
		is: function (el, selector) {
			return !!_closest(el, selector, el);
		},
1266
		extend: _extend,
R
RubaXa 已提交
1267
		throttle: _throttle,
R
RubaXa 已提交
1268
		closest: _closest,
1269
		toggleClass: _toggleClass,
1270
		index: _index
R
RubaXa 已提交
1271 1272 1273
	};


1274 1275 1276 1277 1278 1279
	/**
	 * Create sortable instance
	 * @param {HTMLElement}  el
	 * @param {Object}      [options]
	 */
	Sortable.create = function (el, options) {
R
RubaXa 已提交
1280
		return new Sortable(el, options);
1281
	};
R
RubaXa 已提交
1282

R
RubaXa 已提交
1283 1284

	// Export
R
RubaXa 已提交
1285
	Sortable.version = '1.4.2';
1286
	return Sortable;
R
RubaXa 已提交
1287
});