PageSourceSearch

https://ehall.boustead.edu.cn/new/resources/js/plugins/sortable/Sortable-1.3.0.js

js boustead.edu.cn collected 2026-10-05 01:53:55 UTC 29,642 bytes, 1,254 lines download raw bytes

1/**!
2 * Sortable
3 * @author	RubaXa   <[email protected]>
4 * @license MIT
5 */
6
7
8(function(factory) {
9	"use strict";
10
11	if (typeof define === "function" && define.amd) {
12		define(factory);
13	} else if (typeof module != "undefined" && typeof module.exports != "undefined") {
14		module.exports = factory();
15	} else if (typeof Package !== "undefined") {
16		Sortable = factory(); // export for Meteor.js
17	} else {
18		/* jshint sub:true */
19		window["Sortable"] = factory();
20	}
21})(function() {
22	"use strict";
23
24	var dragEl,
25		parentEl,
26		ghostEl,
27		cloneEl,
28		rootEl,
29		nextEl,
30
31		scrollEl,
32		scrollParentEl,
33
34		lastEl,
35		lastCSS,
36		lastParentCSS,
37
38		oldIndex,
39		newIndex,
40
41		activeGroup,
42		autoScroll = {},
43
44		tapEvt,
45		touchEvt,
46
47		moved,
48
49		/** @const */
50		RSPACE = /\s+/g,
51
52		expando = 'Sortable' + (new Date).getTime(),
53
54		win = window,
55		document = win.document,
56		parseInt = win.parseInt,
57
58		supportDraggable = !!('draggable' in document.createElement('div')),
59		supportCssPointerEvents = (function(el) {
60			el = document.createElement('x');
61			el.style.cssText = 'pointer-events:auto';
62			return el.style.pointerEvents === 'auto';
63		})(),
64
65		_silent = false,
66
67		abs = Math.abs,
68		slice = [].slice,
69
70		touchDragOverListeners = [],
71
72		_autoScroll = _throttle(function( /**Event*/ evt, /**Object*/ options, /**HTMLElement*/ rootEl) {
73			// Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521
74			if (rootEl && options.scroll) {
75				var el,
76					rect,
77					sens = options.scrollSensitivity,
78					speed = options.scrollSpeed,
79
80					x = evt.clientX,
81					y = evt.clientY,
82
83					winWidth = window.innerWidth,
84					winHeight = window.innerHeight,
85
86					vx,
87					vy;
88
89				// Delect scrollEl
90				if (scrollParentEl !== rootEl) {
91					scrollEl = options.scroll;
92					scrollParentEl = rootEl;
93
94					if (scrollEl === true) {
95						scrollEl = rootEl;
96
97						do {
98							if ((scrollEl.offsetWidth < scrollEl.scrollWidth) ||
99								(scrollEl.offsetHeight < scrollEl.scrollHeight)
100							) {
101								break;
102							}
103							/* jshint boss:true */
104						} while (scrollEl = scrollEl.parentNode);
105					}
106				}
107
108				if (scrollEl) {
109					el = scrollEl;
110					rect = scrollEl.getBoundingClientRect();
111					vx = (abs(rect.right - x) <= sens) - (abs(rect.left - x) <= sens);
112					vy = (abs(rect.bottom - y) <= sens) - (abs(rect.top - y) <= sens);
113				}
114
115
116				if (!(vx || vy)) {
117					vx = (winWidth - x <= sens) - (x <= sens);
118					vy = (winHeight - y <= sens) - (y <= sens);
119
120					/* jshint expr:true */
121					(vx || vy) && (el = win);
122				}
123
124
125				if (autoScroll.vx !== vx || autoScroll.vy !== vy || autoScroll.el !== el) {
126					autoScroll.el = el;
127					autoScroll.vx = vx;
128					autoScroll.vy = vy;
129
130					clearInterval(autoScroll.pid);
131
132					if (el) {
133						autoScroll.pid = setInterval(function() {
134							if (el === win) {
135								win.scrollTo(win.pageXOffset + vx * speed, win.pageYOffset + vy * speed);
136							} else {
137								vy && (el.scrollTop += vy * speed);
138								vx && (el.scrollLeft += vx * speed);
139							}
140						}, 24);
141					}
142				}
143			}
144		}, 30),
145
146		_prepareGroup = function(options) {
147			var group = options.group;
148
149			if (!group || typeof group != 'object') {
150				group = options.group = {
151					name: group
152				};
153			}
154
155			['pull', 'put'].forEach(function(key) {
156				if (!(key in group)) {
157					group[key] = true;
158				}
159			});
160
161			options.groups = ' ' + group.name + (group.put.join ? ' ' + group.put.join(' ') : '') + ' ';
162		};
163
164
165
166	/**
167	 * @class  Sortable
168	 * @param  {HTMLElement}  el
169	 * @param  {Object}       [options]
170	 */
171	function Sortable(el, options) {
172		if (!(el && el.nodeType && el.nodeType === 1)) {
173			throw 'Sortable: `el` must be HTMLElement, and not ' + {}.toString.call(el);
174		}
175
176		this.el = el; // root element
177		this.options = options = _extend({}, options);
178
179
180		// Export instance
181		el[expando] = this;
182
183
184		// Default options
185		var defaults = {
186			group: Math.random(),
187			sort: true,
188			disabled: false,
189			store: null,
190			handle: null,
191			scroll: true,
192			scrollSensitivity: 30,
193			scrollSpeed: 10,
194			draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*',
195			ghostClass: 'sortable-ghost',
196			chosenClass: 'sortable-chosen',
197			dragClass: 'sortable-drag',
198			ignore: 'a, img',
199			filter: null,
200			animation: 0,
201			setData: function(dataTransfer, dragEl) {
202				dataTransfer.setData('Text', dragEl.textContent);
203			},
204			dropBubble: false,
205			dragoverBubble: false,
206			dataIdAttr: 'data-id',
207			delay: 0,
208			forceFallback: false,
209			fallbackClass: 'sortable-fallback',
210			fallbackOnBody: false
211		};
212
213
214		// Set default options
215		for (var name in defaults) {
216			!(name in options) && (options[name] = defaults[name]);
217		}
218
219		_prepareGroup(options);
220
221		// Bind all private methods
222		for (var fn in this) {
223			if (fn.charAt(0) === '_') {
224				this[fn] = this[fn].bind(this);
225			}
226		}
227
228		// Setup drag mode
229		this.nativeDraggable = options.forceFallback ? false : supportDraggable;
230
231		// Bind events
232		_on(el, 'mousedown', this._onTapStart);
233		_on(el, 'touchstart', this._onTapStart);
234
235		if (this.nativeDraggable) {
236			_on(el, 'dragover', this);
237			_on(el, 'dragenter', this);
238		}
239
240		touchDragOverListeners.push(this._onDragOver);
241
242		// Restore sorting
243		options.store && this.sort(options.store.get(this));
244	}
245
246
247	Sortable.prototype = /** @lends Sortable.prototype */ {
248		constructor: Sortable,
249
250		_onTapStart: function( /** Event|TouchEvent */ evt) {
251			var _this = this,
252				el = this.el,
253				options = this.options,
254				type = evt.type,
255				touch = evt.touches && evt.touches[0],
256				target = (touch || evt).target,
257				originalTarget = target,
258				filter = options.filter;
259
260
261			if (type === 'mousedown' && evt.button !== 0 || options.disabled) {
262				return; // only left button or enabled
263			}
264
265			target = _closest(target, options.draggable, el);
266
267			if (!target) {
268				return;
269			}
270
271			// get the index of the dragged element within its parent
272			oldIndex = _index(target);
273
274			// Check filter
275			if (typeof filter === 'function') {
276				if (filter.call(this, evt, target, this)) {
277					_dispatchEvent(_this, originalTarget, 'filter', target, el, oldIndex);
278					evt.preventDefault();
279					return; // cancel dnd
280				}
281			} else if (filter) {
282				filter = filter.split(',').some(function(criteria) {
283					criteria = _closest(originalTarget, criteria.trim(), el);
284
285					if (criteria) {
286						_dispatchEvent(_this, criteria, 'filter', target, el, oldIndex);
287						return true;
288					}
289				});
290
291				if (filter) {
292					evt.preventDefault();
293					return; // cancel dnd
294				}
295			}
296
297
298			if (options.handle && !_closest(originalTarget, options.handle, el)) {
299				return;
300			}
301
302
303			// Prepare `dragstart`
304			this._prepareDragStart(evt, touch, target);
305		},
306
307		_prepareDragStart: function( /** Event */ evt, /** Touch */ touch, /** HTMLElement */ target) {
308			var _this = this,
309				el = _this.el,
310				options = _this.options,
311				ownerDocument = el.ownerDocument,
312				dragStartFn;
313
314			if (target && !dragEl && (target.parentNode === el)) {
315				tapEvt = evt;
316
317				rootEl = el;
318				dragEl = target;
319				parentEl = dragEl.parentNode;
320				nextEl = dragEl.nextSibling;
321				activeGroup = options.group;
322
323				dragStartFn = function() {
324					// Delayed drag has been triggered
325					// we can re-enable the events: touchmove/mousemove
326					_this._disableDelayedDrag();
327
328					// Make the element draggable
329					dragEl.draggable = true;
330
331					// Chosen item
332					_toggleClass(dragEl, _this.options.chosenClass, true);
333
334					// Bind the events: dragstart/dragend
335					_this._triggerDragStart(touch);
336				};
337
338				// Disable "draggable"
339				options.ignore.split(',').forEach(function(criteria) {
340					_find(dragEl, criteria.trim(), _disableDraggable);
341				});
342
343				_on(ownerDocument, 'mouseup', _this._onDrop);
344				_on(ownerDocument, 'touchend', _this._onDrop);
345				_on(ownerDocument, 'touchcancel', _this._onDrop);
346
347				if (options.delay) {
348					// If the user moves the pointer or let go the click or touch
349					// before the delay has been reached:
350					// disable the delayed drag
351					_on(ownerDocument, 'mouseup', _this._disableDelayedDrag);
352					_on(ownerDocument, 'touchend', _this._disableDelayedDrag);
353					_on(ownerDocument, 'touchcancel', _this._disableDelayedDrag);
354					_on(ownerDocument, 'mousemove', _this._disableDelayedDrag);
355					_on(ownerDocument, 'touchmove', _this._disableDelayedDrag);
356
357					_this._dragStartTimer = setTimeout(dragStartFn, options.delay);
358				} else {
359					dragStartFn();
360				}
361			}
362		},
363
364		_disableDelayedDrag: function() {
365			var ownerDocument = this.el.ownerDocument;
366
367			clearTimeout(this._dragStartTimer);
368			_off(ownerDocument, 'mouseup', this._disableDelayedDrag);
369			_off(ownerDocument, 'touchend', this._disableDelayedDrag);
370			_off(ownerDocument, 'touchcancel', this._disableDelayedDrag);
371			_off(ownerDocument, 'mousemove', this._disableDelayedDrag);
372			_off(ownerDocument, 'touchmove', this._disableDelayedDrag);
373		},
374
375		_triggerDragStart: function( /** Touch */ touch) {
376			if (touch) {
377				// Touch device support
378				tapEvt = {
379					target: dragEl,
380					clientX: touch.clientX,
381					clientY: touch.clientY
382				};
383
384				this._onDragStart(tapEvt, 'touch');
385			} else if (!this.nativeDraggable) {
386				this._onDragStart(tapEvt, true);
387			} else {
388				_on(dragEl, 'dragend', this);
389				_on(rootEl, 'dragstart', this._onDragStart);
390			}
391
392			try {
393				if (document.selection) {
394					document.selection.empty();
395				} else {
396					window.getSelection().removeAllRanges();
397				}
398			} catch (err) {}
399		},
400
401		_dragStarted: function() {
402			if (rootEl && dragEl) {
403				// Apply effect
404				_toggleClass(dragEl, this.options.ghostClass, true);
405				_toggleClass(dragEl, this.options.dragClass, false);
406
407				Sortable.active = this;
408
409				// Drag start event
410				_dispatchEvent(this, rootEl, 'start', dragEl, rootEl, oldIndex);
411			}
412		},
413
414		_emulateDragOver: function() {
415			if (touchEvt) {
416				if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY) {
417					return;
418				}
419
420				this._lastX = touchEvt.clientX;
421				this._lastY = touchEvt.clientY;
422
423				if (!supportCssPointerEvents) {
424					_css(ghostEl, 'display', 'none');
425				}
426
427				var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY),
428					parent = target,
429					groupName = ' ' + this.options.group.name + '',
430					i = touchDragOverListeners.length;
431
432				if (parent) {
433					do {
434						if (parent[expando] && parent[expando].options.groups.indexOf(groupName) > -1) {
435							while (i--) {
436								touchDragOverListeners[i]({
437									clientX: touchEvt.clientX,
438									clientY: touchEvt.clientY,
439									target: target,
440									rootEl: parent
441								});
442							}
443
444							break;
445						}
446
447						target = parent; // store last element
448					}
449					/* jshint boss:true */
450					while (parent = parent.parentNode);
451				}
452
453				if (!supportCssPointerEvents) {
454					_css(ghostEl, 'display', '');
455				}
456			}
457		},
458
459
460		_onTouchMove: function( /**TouchEvent*/ evt) {
461			if (tapEvt) {
462				// only set the status to dragging, when we are actually dragging
463				if (!Sortable.active) {
464					this._dragStarted();
465				}
466
467				// as well as creating the ghost element on the document body
468				this._appendGhost();
469
470				var touch = evt.touches ? evt.touches[0] : evt,
471					dx = touch.clientX - tapEvt.clientX,
472					dy = touch.clientY - tapEvt.clientY,
473					translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';
474
475				moved = true;
476				touchEvt = touch;
477
478				_css(ghostEl, 'webkitTransform', translate3d);
479				_css(ghostEl, 'mozTransform', translate3d);
480				_css(ghostEl, 'msTransform', translate3d);
481				_css(ghostEl, 'transform', translate3d);
482
483				evt.preventDefault();
484			}
485		},
486
487		_appendGhost: function() {
488			if (!ghostEl) {
489				var rect = dragEl.getBoundingClientRect(),
490					css = _css(dragEl),
491					ghostRect;
492
493				ghostEl = dragEl.cloneNode(true);
494
495				_toggleClass(ghostEl, this.options.ghostClass, false);
496				_toggleClass(ghostEl, this.options.fallbackClass, true);
497				_toggleClass(ghostEl, this.options.dragClass, true);
498
499				_css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10));
500				_css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10));
501				_css(ghostEl, 'width', rect.width);
502				_css(ghostEl, 'height', rect.height);
503				_css(ghostEl, 'opacity', '0.8');
504				_css(ghostEl, 'position', 'fixed');
505				_css(ghostEl, 'zIndex', '100000');
506				_css(ghostEl, 'pointerEvents', 'none');
507
508				this.options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
509
510				// Fixing dimensions.
511				ghostRect = ghostEl.getBoundingClientRect();
512				_css(ghostEl, 'width', rect.width * 2 - ghostRect.width);
513				_css(ghostEl, 'height', rect.height * 2 - ghostRect.height);
514			}
515		},
516
517		_onDragStart: function( /**Event*/ evt, /**boolean*/ useFallback) {
518			var dataTransfer = evt.dataTransfer,
519				options = this.options;
520
521			this._offUpEvents();
522
523			if (activeGroup.pull == 'clone') {
524				cloneEl = dragEl.cloneNode(true);
525				_css(cloneEl, 'display', 'none');
526				rootEl.insertBefore(cloneEl, dragEl);
527			}
528
529			_toggleClass(dragEl, options.dragClass, true);
530
531			if (useFallback) {
532
533				if (useFallback === 'touch') {
534					// Bind touch events
535					_on(document, 'touchmove', this._onTouchMove);
536					_on(document, 'touchend', this._onDrop);
537					_on(document, 'touchcancel', this._onDrop);
538				} else {
539					// Old brwoser
540					_on(document, 'mousemove', this._onTouchMove);
541					_on(document, 'mouseup', this._onDrop);
542				}
543
544				this._loopId = setInterval(this._emulateDragOver, 50);
545			} else {
546				if (dataTransfer) {
547					dataTransfer.effectAllowed = 'move';
548					options.setData && options.setData.call(this, dataTransfer, dragEl);
549				}
550
551				_on(document, 'drop', this);
552				setTimeout(this._dragStarted, 0);
553			}
554		},
555
556		_onDragOver: function( /**Event*/ evt) {
557			var el = this.el,
558				target,
559				dragRect,
560				revert,
561				options = this.options,
562				group = options.group,
563				groupPut = group.put,
564				isOwner = (activeGroup === group),
565				canSort = options.sort;
566
567			if (evt.preventDefault !== void 0) {
568				evt.preventDefault();
569				!options.dragoverBubble && evt.stopPropagation();
570			}
571
572			moved = true;
573
574			if (activeGroup && !options.disabled &&
575				(isOwner ? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list
576					: activeGroup.pull && groupPut && (
577						(activeGroup.name === group.name) || // by Name
578						(groupPut.indexOf && ~groupPut.indexOf(activeGroup.name)) // by Array
579					)
580				) &&
581				(evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback
582			) {
583				// Smart auto-scrolling
584				_autoScroll(evt, options, this.el);
585
586				if (_silent) {
587					return;
588				}
589
590				target = _closest(evt.target, options.draggable, el);
591				dragRect = dragEl.getBoundingClientRect();
592
593				if (revert) {
594					_cloneHide(true);
595
596					if (cloneEl || nextEl) {
597						rootEl.insertBefore(dragEl, cloneEl || nextEl);
598					} else if (!canSort) {
599						rootEl.appendChild(dragEl);
600					}
601
602					return;
603				}
604
605
606				if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
607					(el === evt.target) && (target = _ghostIsLast(el, evt))
608				) {
609
610					if (target) {
611						if (target.animated) {
612							return;
613						}
614
615						targetRect = target.getBoundingClientRect();
616					}
617
618					_cloneHide(isOwner);
619
620					if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect) !== false) {
621						if (!dragEl.contains(el)) {
622							el.appendChild(dragEl);
623							parentEl = el; // actualization
624						}
625
626						this._animate(dragRect, dragEl);
627						target && this._animate(targetRect, target);
628					}
629				} else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) {
630					if (lastEl !== target) {
631						lastEl = target;
632						lastCSS = _css(target);
633						lastParentCSS = _css(target.parentNode);
634					}
635
636
637					var targetRect = target.getBoundingClientRect(),
638						width = targetRect.right - targetRect.left,
639						height = targetRect.bottom - targetRect.top,
640						floating = /left|right|inline/.test(lastCSS.cssFloat + lastCSS.display) || (lastParentCSS.display == 'flex' && lastParentCSS['flex-direction'].indexOf('row') === 0),
641						isWide = (target.offsetWidth > dragEl.offsetWidth),
642						isLong = (target.offsetHeight > dragEl.offsetHeight),
643						halfway = (floating ? (evt.clientX - targetRect.left) / width : (evt.clientY - targetRect.top) / height) > 0.5,
644						nextSibling = target.nextElementSibling,
645						moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect),
646						after;
647
648					if (moveVector !== false) {
649						_silent = true;
650						setTimeout(_unsilent, 30);
651
652						_cloneHide(isOwner);
653
654						if (moveVector === 1 || moveVector === -1) {
655							after = (moveVector === 1);
656						} else if (floating) {
657							var elTop = dragEl.offsetTop,
658								tgTop = target.offsetTop;
659
660							if (elTop === tgTop) {
661								after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide;
662							} else {
663								after = tgTop > elTop;
664							}
665						} else {
666							after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
667						}
668
669						if (!dragEl.contains(el)) {
670							if (after && !nextSibling) {
671								el.appendChild(dragEl);
672							} else {
673								target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
674							}
675						}
676
677						parentEl = dragEl.parentNode; // actualization
678
679						this._animate(dragRect, dragEl);
680						this._animate(targetRect, target);
681					}
682				}
683			} else {
684				isOwner ? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list
685					: activeGroup.pull && groupPut && (
686						(activeGroup.name === group.name) || // by Name
687						(groupPut.indexOf && ~groupPut.indexOf(activeGroup.name)) // by Array
688					)
689				if (isOwner) {
690					_onMoveToSelf(rootEl, dragEl);
691				}
692			}
693		},
694
695		_animate: function(prevRect, target) {
696			var ms = this.options.animation;
697
698			if (ms) {
699				var currentRect = target.getBoundingClientRect();
700
701				_css(target, 'transition', 'none');
702				_css(target, 'transform', 'translate3d(' + (prevRect.left - currentRect.left) + 'px,' + (prevRect.top - currentRect.top) + 'px,0)');
703
704				target.offsetWidth; // repaint
705
706				_css(target, 'transition', 'all ' + ms + 'ms');
707				_css(target, 'transform', 'translate3d(0,0,0)');
708
709				clearTimeout(target.animated);
710				target.animated = setTimeout(function() {
711					_css(target, 'transition', '');
712					_css(target, 'transform', '');
713					target.animated = false;
714				}, ms);
715			}
716		},
717
718		_offUpEvents: function() {
719			var ownerDocument = this.el.ownerDocument;
720
721			_off(document, 'touchmove', this._onTouchMove);
722			_off(ownerDocument, 'mouseup', this._onDrop);
723			_off(ownerDocument, 'touchend', this._onDrop);
724			_off(ownerDocument, 'touchcancel', this._onDrop);
725		},
726
727		_onDrop: function( /**Event*/ evt) {
728			var el = this.el,
729				options = this.options;
730
731			clearInterval(this._loopId);
732			clearInterval(autoScroll.pid);
733			clearTimeout(this._dragStartTimer);
734
735			// Unbind events
736			_off(document, 'mousemove', this._onTouchMove);
737
738			if (this.nativeDraggable) {
739				_off(document, 'drop', this);
740				_off(el, 'dragstart', this._onDragStart);
741			}
742
743			this._offUpEvents();
744
745			if (evt) {
746				if (moved) {
747					evt.preventDefault();
748					!options.dropBubble && evt.stopPropagation();
749				}
750
751				ghostEl && ghostEl.parentNode.removeChild(ghostEl);
752
753				if (dragEl) {
754					if (this.nativeDraggable) {
755						_off(dragEl, 'dragend', this);
756					}
757
758					_disableDraggable(dragEl);
759
760					// Remove class's
761					_toggleClass(dragEl, this.options.ghostClass, false);
762					_toggleClass(dragEl, this.options.chosenClass, false);
763
764					if (rootEl !== parentEl) {
765						newIndex = _index(dragEl);
766
767						if (newIndex >= 0) {
768							// drag from one list and drop into another
769							_dispatchEvent(null, parentEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
770							_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
771
772							// Add event
773							_dispatchEvent(null, parentEl, 'add', dragEl, rootEl, oldIndex, newIndex);
774
775							// Remove event
776							_dispatchEvent(this, rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex);
777						}
778					} else {
779						// Remove clone
780						cloneEl && cloneEl.parentNode.removeChild(cloneEl);
781
782						if (dragEl.nextSibling !== nextEl) {
783							// Get the index of the dragged element within its parent
784							newIndex = _index(dragEl);
785
786							if (newIndex >= 0) {
787								// drag & drop within the same list
788								_dispatchEvent(this, rootEl, 'update', dragEl, rootEl, oldIndex, newIndex);
789								_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
790							}
791						}
792					}
793
794					if (Sortable.active) {
795						if (newIndex === null || newIndex === -1) {
796							newIndex = oldIndex;
797						}
798
799						_dispatchEvent(this, rootEl, 'end', dragEl, rootEl, oldIndex, newIndex);
800
801						// Save sorting
802						this.save();
803					}
804				}
805
806				// Nulling
807				rootEl =
808					dragEl =
809					parentEl =
810					ghostEl =
811					nextEl =
812					cloneEl =
813
814					scrollEl =
815					scrollParentEl =
816
817					tapEvt =
818					touchEvt =
819
820					moved =
821					newIndex =
822
823					lastEl =
824					lastCSS =
825
826					activeGroup =
827					Sortable.active = null;
828			}
829		},
830
831
832		handleEvent: function( /**Event*/ evt) {
833			var type = evt.type;
834
835			if (type === 'dragover' || type === 'dragenter') {
836				if (dragEl) {
837					this._onDragOver(evt);
838					_globalDragOver(evt);
839				}
840			} else if (type === 'drop' || type === 'dragend') {
841				this._onDrop(evt);
842			}
843		},
844
845
846		/**
847		 * Serializes the item into an array of string.
848		 * @returns {String[]}
849		 */
850		toArray: function() {
851			var order = [],
852				el,
853				children = this.el.children,
854				i = 0,
855				n = children.length,
856				options = this.options;
857
858			for (; i < n; i++) {
859				el = children[i];
860				if (_closest(el, options.draggable, this.el)) {
861					order.push(el.getAttribute(options.dataIdAttr) || _generateId(el));
862				}
863			}
864
865			return order;
866		},
867
868
869		/**
870		 * Sorts the elements according to the array.
871		 * @param  {String[]}  order  order of the items
872		 */
873		sort: function(order) {
874			var items = {},
875				rootEl = this.el;
876
877			this.toArray().forEach(function(id, i) {
878				var el = rootEl.children[i];
879
880				if (_closest(el, this.options.draggable, rootEl)) {
881					items[id] = el;
882				}
883			}, this);
884
885			order.forEach(function(id) {
886				if (items[id]) {
887					rootEl.removeChild(items[id]);
888					rootEl.appendChild(items[id]);
889				}
890			});
891		},
892
893
894		/**
895		 * Save the current sorting
896		 */
897		save: function() {
898			var store = this.options.store;
899			store && store.set(this);
900		},
901
902
903		/**
904		 * 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.
905		 * @param   {HTMLElement}  el
906		 * @param   {String}       [selector]  default: `options.draggable`
907		 * @returns {HTMLElement|null}
908		 */
909		closest: function(el, selector) {
910			return _closest(el, selector || this.options.draggable, this.el);
911		},
912
913
914		/**
915		 * Set/get option
916		 * @param   {string} name
917		 * @param   {*}      [value]
918		 * @returns {*}
919		 */
920		option: function(name, value) {
921			var options = this.options;
922
923			if (value === void 0) {
924				return options[name];
925			} else {
926				options[name] = value;
927
928				if (name === 'group') {
929					_prepareGroup(options);
930				}
931			}
932		},
933
934
935		/**
936		 * Destroy
937		 */
938		destroy: function() {
939			var el = this.el;
940
941			el[expando] = null;
942
943			_off(el, 'mousedown', this._onTapStart);
944			_off(el, 'touchstart', this._onTapStart);
945
946			if (this.nativeDraggable) {
947				_off(el, 'dragover', this);
948				_off(el, 'dragenter', this);
949			}
950
951			// Remove draggable attributes
952			Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function(el) {
953				el.removeAttribute('draggable');
954			});
955
956			touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1);
957
958			this._onDrop();
959
960			this.el = el = null;
961		}
962	};
963
964
965	function _cloneHide(state) {
966		if (cloneEl && (cloneEl.state !== state)) {
967			_css(cloneEl, 'display', state ? 'none' : '');
968			!state && cloneEl.state && rootEl.insertBefore(cloneEl, dragEl);
969			cloneEl.state = state;
970		}
971	}
972
973
974	function _closest( /**HTMLElement*/ el, /**String*/ selector, /**HTMLElement*/ ctx) {
975		if (el) {
976			ctx = ctx || document;
977			selector = selector.split('.');
978
979			var tag = selector.shift().toUpperCase(),
980				re = new RegExp('\\s(' + selector.join('|') + ')(?=\\s)', 'g');
981
982			do {
983				if (
984					(tag === '>*' && el.parentNode === ctx) || (
985						(tag === '' || el.nodeName.toUpperCase() == tag) &&
986						(!selector.length || ((' ' + el.className + ' ').match(re) || []).length == selector.length)
987					)
988				) {
989					return el;
990				}
991			}
992			while (el !== ctx && (el = el.parentNode));
993		}
994
995		return null;
996	}
997
998
999	function _globalDragOver( /**Event*/ evt) {
1000		if (evt.dataTransfer) {
1001			evt.dataTransfer.dropEffect = 'move';
1002		}
1003		evt.preventDefault();
1004	}
1005
1006
1007	function _on(el, event, fn) {
1008		el.addEventListener(event, fn, false);
1009	}
1010
1011
1012	function _off(el, event, fn) {
1013		el.removeEventListener(event, fn, false);
1014	}
1015
1016
1017	function _toggleClass(el, name, state) {
1018		if (el) {
1019			if (el.classList) {
1020				el.classList[state ? 'add' : 'remove'](name);
1021			} else {
1022				var className = (' ' + el.className + ' ').replace(RSPACE, ' ').replace(' ' + name + ' ', ' ');
1023				el.className = (className + (state ? ' ' + name : '')).replace(RSPACE, ' ');
1024			}
1025		}
1026	}
1027
1028
1029	function _css(el, prop, val) {
1030		var style = el && el.style;
1031
1032		if (style) {
1033			if (val === void 0) {
1034				if (document.defaultView && document.defaultView.getComputedStyle) {
1035					val = document.defaultView.getComputedStyle(el, '');
1036				} else if (el.currentStyle) {
1037					val = el.currentStyle;
1038				}
1039
1040				return prop === void 0 ? val : val[prop];
1041			} else {
1042				if (!(prop in style)) {
1043					prop = '-webkit-' + prop;
1044				}
1045
1046				style[prop] = val + (typeof val === 'string' ? '' : 'px');
1047			}
1048		}
1049	}
1050
1051
1052	function _find(ctx, tagName, iterator) {
1053		if (ctx) {
1054			var list = ctx.getElementsByTagName(tagName),
1055				i = 0,
1056				n = list.length;
1057
1058			if (iterator) {
1059				for (; i < n; i++) {
1060					iterator(list[i], i);
1061				}
1062			}
1063
1064			return list;
1065		}
1066
1067		return [];
1068	}
1069
1070
1071
1072	function _dispatchEvent(sortable, rootEl, name, targetEl, fromEl, startIndex, newIndex) {
1073		var evt = document.createEvent('Event'),
1074			options = (sortable || rootEl[expando]).options,
1075			onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1);
1076
1077		evt.initEvent(name, true, true);
1078
1079		evt.to = rootEl;
1080		evt.from = fromEl || rootEl;
1081		evt.item = targetEl || rootEl;
1082		evt.clone = cloneEl;
1083
1084		evt.oldIndex = startIndex;
1085		evt.newIndex = newIndex;
1086
1087		rootEl.dispatchEvent(evt);
1088
1089		if (options[onName]) {
1090			options[onName].call(sortable, evt);
1091		}
1092	}
1093
1094
1095	function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect) {
1096		var evt,
1097			sortable = fromEl[expando],
1098			onMoveFn = sortable.options.onMove,
1099			retVal;
1100
1101		evt = document.createEvent('Event');
1102		evt.initEvent('move', true, true);
1103
1104		evt.to = toEl;
1105		evt.from = fromEl;
1106		evt.dragged = dragEl;
1107		evt.draggedRect = dragRect;
1108		evt.related = targetEl || toEl;
1109		evt.relatedRect = targetRect || toEl.getBoundingClientRect();
1110
1111		fromEl.dispatchEvent(evt);
1112
1113		if (onMoveFn) {
1114			retVal = onMoveFn.call(sortable, evt);
1115		}
1116
1117		return retVal;
1118	}
1119
1120	function _onMoveToSelf(fromEl, _dragEl) {
1121		var sortable = fromEl[expando];
1122		var fun = sortable.options.onMoveToSelf;
1123		if (fun) {
1124			var evt = document.createEvent('Event');
1125			evt.initEvent('moveToSelf', true, true);
1126			evt.dragged = _dragEl;
1127			fun.call(sortable, evt);
1128		}
1129	}
1130
1131
1132	function _disableDraggable(el) {
1133		el.draggable = false;
1134	}
1135
1136
1137	function _unsilent() {
1138		_silent = false;
1139	}
1140
1141
1142	/** @returns {HTMLElement|false} */
1143	function _ghostIsLast(el, evt) {
1144		var lastEl = el.lastElementChild,
1145			rect = lastEl.getBoundingClientRect();
1146
1147		return ((evt.clientY - (rect.top + rect.height) > 5) || (evt.clientX - (rect.right + rect.width) > 5)) && lastEl; // min delta
1148	}
1149
1150
1151	/**
1152	 * Generate id
1153	 * @param   {HTMLElement} el
1154	 * @returns {String}
1155	 * @private
1156	 */
1157	function _generateId(el) {
1158		var str = el.tagName + el.className + el.src + el.href + el.textContent,
1159			i = str.length,
1160			sum = 0;
1161
1162		while (i--) {
1163			sum += str.charCodeAt(i);
1164		}
1165
1166		return sum.toString(36);
1167	}
1168
1169	/**
1170	 * Returns the index of an element within its parent
1171	 * @param  {HTMLElement} el
1172	 * @return {number}
1173	 */
1174	function _index(el) {
1175		var index = 0;
1176
1177		if (!el || !el.parentNode) {
1178			return -1;
1179		}
1180
1181		while (el && (el = el.previousElementSibling)) {
1182			if (el.nodeName.toUpperCase() !== 'TEMPLATE') {
1183				index++;
1184			}
1185		}
1186
1187		return index;
1188	}
1189
1190	function _throttle(callback, ms) {
1191		var args, _this;
1192
1193		return function() {
1194			if (args === void 0) {
1195				args = arguments;
1196				_this = this;
1197
1198				setTimeout(function() {
1199					if (args.length === 1) {
1200						callback.call(_this, args[0]);
1201					} else {
1202						callback.apply(_this, args);
1203					}
1204
1205					args = void 0;
1206				}, ms);
1207			}
1208		};
1209	}
1210
1211	function _extend(dst, src) {
1212		if (dst && src) {
1213			for (var key in src) {
1214				if (src.hasOwnProperty(key)) {
1215					dst[key] = src[key];
1216				}
1217			}
1218		}
1219
1220		return dst;
1221	}
1222
1223
1224	// Export utils
1225	Sortable.utils = {
1226		on: _on,
1227		off: _off,
1228		css: _css,
1229		find: _find,
1230		is: function(el, selector) {
1231			return !!_closest(el, selector, el);
1232		},
1233		extend: _extend,
1234		throttle: _throttle,
1235		closest: _closest,
1236		toggleClass: _toggleClass,
1237		index: _index
1238	};
1239
1240
1241	/**
1242	 * Create sortable instance
1243	 * @param {HTMLElement}  el
1244	 * @param {Object}      [options]
1245	 */
1246	Sortable.create = function(el, options) {
1247		return new Sortable(el, options);
1248	};
1249
1250
1251	// Export
1252	Sortable.version = '1.3.0';
1253	return Sortable;
1254});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.