You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							2473 lines
						
					
					
						
							66 KiB
						
					
					
				
			
		
		
	
	
							2473 lines
						
					
					
						
							66 KiB
						
					
					
				/**! | 
						|
 * Sortable | 
						|
 * @author	RubaXa   <trash@rubaxa.org> | 
						|
 * @author	owenm    <owen23355@gmail.com> | 
						|
 * @license MIT | 
						|
 */ | 
						|
 | 
						|
(function sortableModule(factory) { | 
						|
	"use strict"; | 
						|
 | 
						|
	if (typeof define === "function" && define.amd) { | 
						|
		define(factory); | 
						|
	} | 
						|
	else if (typeof module != "undefined" && typeof module.exports != "undefined") { | 
						|
		module.exports = factory(); | 
						|
	} | 
						|
	else { | 
						|
		/* jshint sub:true */ | 
						|
		window["Sortable"] = factory(); | 
						|
	} | 
						|
})(function sortableFactory() { | 
						|
	"use strict"; | 
						|
 | 
						|
	if (typeof window === "undefined" || !window.document) { | 
						|
		return function sortableError() { | 
						|
			throw new Error("Sortable.js requires a window with a document"); | 
						|
		}; | 
						|
	} | 
						|
 | 
						|
	var dragEl, | 
						|
		parentEl, | 
						|
		ghostEl, | 
						|
		cloneEl, | 
						|
		rootEl, | 
						|
		nextEl, | 
						|
		lastDownEl, | 
						|
 | 
						|
		scrollEl, | 
						|
		scrollParentEl, | 
						|
		scrollCustomFn, | 
						|
 | 
						|
		oldIndex, | 
						|
		newIndex, | 
						|
 | 
						|
		activeGroup, | 
						|
		putSortable, | 
						|
 | 
						|
		autoScrolls = [], | 
						|
		scrolling = false, | 
						|
 | 
						|
		awaitingDragStarted = false, | 
						|
		ignoreNextClick = false, | 
						|
		sortables = [], | 
						|
 | 
						|
		pointerElemChangedInterval, | 
						|
		lastPointerElemX, | 
						|
		lastPointerElemY, | 
						|
 | 
						|
		tapEvt, | 
						|
		touchEvt, | 
						|
 | 
						|
		moved, | 
						|
 | 
						|
 | 
						|
		lastTarget, | 
						|
		lastDirection, | 
						|
		pastFirstInvertThresh = false, | 
						|
		isCircumstantialInvert = false, | 
						|
		lastMode, // 'swap' or 'insert' | 
						|
 | 
						|
		targetMoveDistance, | 
						|
 | 
						|
		// For positioning ghost absolutely | 
						|
		ghostRelativeParent, | 
						|
		ghostRelativeParentInitialScroll = [], // (left, top) | 
						|
 | 
						|
 | 
						|
		forRepaintDummy, | 
						|
		realDragElRect, // dragEl rect after current animation | 
						|
 | 
						|
		/** @const */ | 
						|
		R_SPACE = /\s+/g, | 
						|
 | 
						|
		expando = 'Sortable' + (new Date).getTime(), | 
						|
 | 
						|
		win = window, | 
						|
		document = win.document, | 
						|
		parseInt = win.parseInt, | 
						|
		setTimeout = win.setTimeout, | 
						|
 | 
						|
		$ = win.jQuery || win.Zepto, | 
						|
		Polymer = win.Polymer, | 
						|
 | 
						|
		captureMode = { | 
						|
			capture: false, | 
						|
			passive: false | 
						|
		}, | 
						|
 | 
						|
		IE11OrLess = !!navigator.userAgent.match(/(?:Trident.*rv[ :]?11\.|msie|iemobile)/i), | 
						|
		Edge = !!navigator.userAgent.match(/Edge/i), | 
						|
		FireFox = !!navigator.userAgent.match(/firefox/i), | 
						|
		Safari = !!(navigator.userAgent.match(/safari/i) && !navigator.userAgent.match(/chrome/i) && !navigator.userAgent.match(/android/i)), | 
						|
		IOS = !!(navigator.userAgent.match(/iP(ad|od|hone)/i)), | 
						|
 | 
						|
		PositionGhostAbsolutely = IOS, | 
						|
 | 
						|
		CSSFloatProperty = Edge || IE11OrLess ? 'cssFloat' : 'float', | 
						|
 | 
						|
		// This will not pass for IE9, because IE9 DnD only works on anchors | 
						|
		supportDraggable = ('draggable' in document.createElement('div')), | 
						|
 | 
						|
		supportCssPointerEvents = (function() { | 
						|
			// false when <= IE11 | 
						|
			if (IE11OrLess) { | 
						|
				return false; | 
						|
			} | 
						|
			var el = document.createElement('x'); | 
						|
			el.style.cssText = 'pointer-events:auto'; | 
						|
			return el.style.pointerEvents === 'auto'; | 
						|
		})(), | 
						|
 | 
						|
		_silent = false, | 
						|
		_alignedSilent = false, | 
						|
 | 
						|
		abs = Math.abs, | 
						|
		min = Math.min, | 
						|
		max = Math.max, | 
						|
 | 
						|
		savedInputChecked = [], | 
						|
 | 
						|
		_detectDirection = function(el, options) { | 
						|
			var elCSS = _css(el), | 
						|
				elWidth = parseInt(elCSS.width) | 
						|
					- parseInt(elCSS.paddingLeft) | 
						|
					- parseInt(elCSS.paddingRight) | 
						|
					- parseInt(elCSS.borderLeftWidth) | 
						|
					- parseInt(elCSS.borderRightWidth), | 
						|
				child1 = _getChild(el, 0, options), | 
						|
				child2 = _getChild(el, 1, options), | 
						|
				firstChildCSS = child1 && _css(child1), | 
						|
				secondChildCSS = child2 && _css(child2), | 
						|
				firstChildWidth = firstChildCSS && parseInt(firstChildCSS.marginLeft) + parseInt(firstChildCSS.marginRight) + _getRect(child1).width, | 
						|
				secondChildWidth = secondChildCSS && parseInt(secondChildCSS.marginLeft) + parseInt(secondChildCSS.marginRight) + _getRect(child2).width; | 
						|
 | 
						|
			if (elCSS.display === 'flex') { | 
						|
				return elCSS.flexDirection === 'column' || elCSS.flexDirection === 'column-reverse' | 
						|
				? 'vertical' : 'horizontal'; | 
						|
			} | 
						|
 | 
						|
			if (elCSS.display === 'grid') { | 
						|
				return elCSS.gridTemplateColumns.split(' ').length <= 1 ? 'vertical' : 'horizontal'; | 
						|
			} | 
						|
 | 
						|
			if (child1 && firstChildCSS.float !== 'none') { | 
						|
				var touchingSideChild2 = firstChildCSS.float === 'left' ? 'left' : 'right'; | 
						|
 | 
						|
				return child2 && (secondChildCSS.clear === 'both' || secondChildCSS.clear === touchingSideChild2) ? | 
						|
					'vertical' : 'horizontal'; | 
						|
			} | 
						|
 | 
						|
			return (child1 && | 
						|
				( | 
						|
					firstChildCSS.display === 'block' || | 
						|
					firstChildCSS.display === 'flex' || | 
						|
					firstChildCSS.display === 'table' || | 
						|
					firstChildCSS.display === 'grid' || | 
						|
					firstChildWidth >= elWidth && | 
						|
					elCSS[CSSFloatProperty] === 'none' || | 
						|
					child2 && | 
						|
					elCSS[CSSFloatProperty] === 'none' && | 
						|
					firstChildWidth + secondChildWidth > elWidth | 
						|
				) ? | 
						|
				'vertical' : 'horizontal' | 
						|
			); | 
						|
		}, | 
						|
 | 
						|
		/** | 
						|
		 * Detects first nearest empty sortable to X and Y position using emptyInsertThreshold. | 
						|
		 * @param  {Number} x      X position | 
						|
		 * @param  {Number} y      Y position | 
						|
		 * @return {HTMLElement}   Element of the first found nearest Sortable | 
						|
		 */ | 
						|
		_detectNearestEmptySortable = function(x, y) { | 
						|
			for (var i = 0; i < sortables.length; i++) { | 
						|
				if (_lastChild(sortables[i])) continue; | 
						|
 | 
						|
				var rect = _getRect(sortables[i]), | 
						|
					threshold = sortables[i][expando].options.emptyInsertThreshold, | 
						|
					insideHorizontally = x >= (rect.left - threshold) && x <= (rect.right + threshold), | 
						|
					insideVertically = y >= (rect.top - threshold) && y <= (rect.bottom + threshold); | 
						|
 | 
						|
				if (insideHorizontally && insideVertically) { | 
						|
					return sortables[i]; | 
						|
				} | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
		_isClientInRowColumn = function(x, y, el, axis, options) { | 
						|
			var targetRect = _getRect(el), | 
						|
				targetS1Opp = axis === 'vertical' ? targetRect.left : targetRect.top, | 
						|
				targetS2Opp = axis === 'vertical' ? targetRect.right : targetRect.bottom, | 
						|
				mouseOnOppAxis = axis === 'vertical' ? x : y; | 
						|
 | 
						|
			return targetS1Opp < mouseOnOppAxis && mouseOnOppAxis < targetS2Opp; | 
						|
		}, | 
						|
 | 
						|
		_isElInRowColumn = function(el1, el2, axis) { | 
						|
			var el1Rect = el1 === dragEl && realDragElRect || _getRect(el1), | 
						|
				el2Rect = el2 === dragEl && realDragElRect || _getRect(el2), | 
						|
				el1S1Opp = axis === 'vertical' ? el1Rect.left : el1Rect.top, | 
						|
				el1S2Opp = axis === 'vertical' ? el1Rect.right : el1Rect.bottom, | 
						|
				el1OppLength = axis === 'vertical' ? el1Rect.width : el1Rect.height, | 
						|
				el2S1Opp = axis === 'vertical' ? el2Rect.left : el2Rect.top, | 
						|
				el2S2Opp = axis === 'vertical' ? el2Rect.right : el2Rect.bottom, | 
						|
				el2OppLength = axis === 'vertical' ? el2Rect.width : el2Rect.height; | 
						|
 | 
						|
			return ( | 
						|
				el1S1Opp === el2S1Opp || | 
						|
				el1S2Opp === el2S2Opp || | 
						|
				(el1S1Opp + el1OppLength / 2) === (el2S1Opp + el2OppLength / 2) | 
						|
			); | 
						|
		}, | 
						|
 | 
						|
		_getParentAutoScrollElement = function(el, includeSelf) { | 
						|
			// skip to window | 
						|
			if (!el || !el.getBoundingClientRect) return _getWindowScrollingElement(); | 
						|
 | 
						|
			var elem = el; | 
						|
			var gotSelf = false; | 
						|
			do { | 
						|
				// we don't need to get elem css if it isn't even overflowing in the first place (performance) | 
						|
				if (elem.clientWidth < elem.scrollWidth || elem.clientHeight < elem.scrollHeight) { | 
						|
					var elemCSS = _css(elem); | 
						|
					if ( | 
						|
						elem.clientWidth < elem.scrollWidth && (elemCSS.overflowX == 'auto' || elemCSS.overflowX == 'scroll') || | 
						|
						elem.clientHeight < elem.scrollHeight && (elemCSS.overflowY == 'auto' || elemCSS.overflowY == 'scroll') | 
						|
					) { | 
						|
						if (!elem || !elem.getBoundingClientRect || elem === document.body) return _getWindowScrollingElement(); | 
						|
 | 
						|
						if (gotSelf || includeSelf) return elem; | 
						|
						gotSelf = true; | 
						|
					} | 
						|
				} | 
						|
			/* jshint boss:true */ | 
						|
			} while (elem = elem.parentNode); | 
						|
 | 
						|
			return _getWindowScrollingElement(); | 
						|
		}, | 
						|
 | 
						|
		_getWindowScrollingElement = function() { | 
						|
			if (IE11OrLess) { | 
						|
				return document.documentElement; | 
						|
			} else { | 
						|
				return document.scrollingElement; | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
		_scrollBy = function(el, x, y) { | 
						|
			el.scrollLeft += x; | 
						|
			el.scrollTop += y; | 
						|
		}, | 
						|
 | 
						|
		_autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl, /**Boolean*/isFallback) { | 
						|
			// Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521 | 
						|
			if (options.scroll) { | 
						|
				var _this = rootEl ? rootEl[expando] : window, | 
						|
					sens = options.scrollSensitivity, | 
						|
					speed = options.scrollSpeed, | 
						|
 | 
						|
					x = evt.clientX, | 
						|
					y = evt.clientY, | 
						|
 | 
						|
					winScroller = _getWindowScrollingElement(), | 
						|
 | 
						|
					scrollThisInstance = false; | 
						|
 | 
						|
				// Detect scrollEl | 
						|
				if (scrollParentEl !== rootEl) { | 
						|
					_clearAutoScrolls(); | 
						|
 | 
						|
					scrollEl = options.scroll; | 
						|
					scrollCustomFn = options.scrollFn; | 
						|
 | 
						|
					if (scrollEl === true) { | 
						|
						scrollEl = _getParentAutoScrollElement(rootEl, true); | 
						|
						scrollParentEl = scrollEl; | 
						|
					} | 
						|
				} | 
						|
 | 
						|
 | 
						|
				var layersOut = 0; | 
						|
				var currentParent = scrollEl; | 
						|
				do { | 
						|
					var	el = currentParent, | 
						|
						rect = _getRect(el), | 
						|
 | 
						|
						top = rect.top, | 
						|
						bottom = rect.bottom, | 
						|
						left = rect.left, | 
						|
						right = rect.right, | 
						|
 | 
						|
						width = rect.width, | 
						|
						height = rect.height, | 
						|
 | 
						|
						scrollWidth, | 
						|
						scrollHeight, | 
						|
 | 
						|
						css, | 
						|
 | 
						|
						vx, | 
						|
						vy, | 
						|
 | 
						|
						canScrollX, | 
						|
						canScrollY, | 
						|
 | 
						|
						scrollPosX, | 
						|
						scrollPosY; | 
						|
 | 
						|
 | 
						|
					scrollWidth = el.scrollWidth; | 
						|
					scrollHeight = el.scrollHeight; | 
						|
 | 
						|
					css = _css(el); | 
						|
 | 
						|
					scrollPosX = el.scrollLeft; | 
						|
					scrollPosY = el.scrollTop; | 
						|
 | 
						|
					if (el === winScroller) { | 
						|
						canScrollX = width < scrollWidth && (css.overflowX === 'auto' || css.overflowX === 'scroll' || css.overflowX === 'visible'); | 
						|
						canScrollY = height < scrollHeight && (css.overflowY === 'auto' || css.overflowY === 'scroll' || css.overflowY === 'visible'); | 
						|
					} else { | 
						|
						canScrollX = width < scrollWidth && (css.overflowX === 'auto' || css.overflowX === 'scroll'); | 
						|
						canScrollY = height < scrollHeight && (css.overflowY === 'auto' || css.overflowY === 'scroll'); | 
						|
					} | 
						|
 | 
						|
					vx = canScrollX && (abs(right - x) <= sens && (scrollPosX + width) < scrollWidth) - (abs(left - x) <= sens && !!scrollPosX); | 
						|
 | 
						|
					vy = canScrollY && (abs(bottom - y) <= sens && (scrollPosY + height) < scrollHeight) - (abs(top - y) <= sens && !!scrollPosY); | 
						|
 | 
						|
 | 
						|
					if (!autoScrolls[layersOut]) { | 
						|
						for (var i = 0; i <= layersOut; i++) { | 
						|
							if (!autoScrolls[i]) { | 
						|
								autoScrolls[i] = {}; | 
						|
							} | 
						|
						} | 
						|
					} | 
						|
 | 
						|
					if (autoScrolls[layersOut].vx != vx || autoScrolls[layersOut].vy != vy || autoScrolls[layersOut].el !== el) { | 
						|
						autoScrolls[layersOut].el = el; | 
						|
						autoScrolls[layersOut].vx = vx; | 
						|
						autoScrolls[layersOut].vy = vy; | 
						|
 | 
						|
						clearInterval(autoScrolls[layersOut].pid); | 
						|
 | 
						|
						if (el && (vx != 0 || vy != 0)) { | 
						|
							scrollThisInstance = true; | 
						|
							/* jshint loopfunc:true */ | 
						|
							autoScrolls[layersOut].pid = setInterval((function () { | 
						|
								// emulate drag over during autoscroll (fallback), emulating native DnD behaviour | 
						|
								if (isFallback && this.layer === 0) { | 
						|
									Sortable.active._emulateDragOver(true); | 
						|
									Sortable.active._onTouchMove(touchEvt, true); | 
						|
								} | 
						|
								var scrollOffsetY = autoScrolls[this.layer].vy ? autoScrolls[this.layer].vy * speed : 0; | 
						|
								var scrollOffsetX = autoScrolls[this.layer].vx ? autoScrolls[this.layer].vx * speed : 0; | 
						|
 | 
						|
								if ('function' === typeof(scrollCustomFn)) { | 
						|
									if (scrollCustomFn.call(_this, scrollOffsetX, scrollOffsetY, evt, touchEvt, autoScrolls[this.layer].el) !== 'continue') { | 
						|
										return; | 
						|
									} | 
						|
								} | 
						|
 | 
						|
								_scrollBy(autoScrolls[this.layer].el, scrollOffsetX, scrollOffsetY); | 
						|
							}).bind({layer: layersOut}), 24); | 
						|
						} | 
						|
					} | 
						|
					layersOut++; | 
						|
				} while (options.bubbleScroll && currentParent !== winScroller && (currentParent = _getParentAutoScrollElement(currentParent, false))); | 
						|
				scrolling = scrollThisInstance; // in case another function catches scrolling as false in between when it is not | 
						|
			} | 
						|
		}, 30), | 
						|
 | 
						|
		_clearAutoScrolls = function () { | 
						|
			autoScrolls.forEach(function(autoScroll) { | 
						|
				clearInterval(autoScroll.pid); | 
						|
			}); | 
						|
			autoScrolls = []; | 
						|
		}, | 
						|
 | 
						|
		_prepareGroup = function (options) { | 
						|
			function toFn(value, pull) { | 
						|
				return function(to, from, dragEl, evt) { | 
						|
					var sameGroup = to.options.group.name && | 
						|
									from.options.group.name && | 
						|
									to.options.group.name === from.options.group.name; | 
						|
 | 
						|
					if (value == null && (pull || sameGroup)) { | 
						|
						// Default pull value | 
						|
						// Default pull and put value if same group | 
						|
						return true; | 
						|
					} else if (value == null || value === false) { | 
						|
						return false; | 
						|
					} else if (pull && value === 'clone') { | 
						|
						return value; | 
						|
					} else if (typeof value === 'function') { | 
						|
						return toFn(value(to, from, dragEl, evt), pull)(to, from, dragEl, evt); | 
						|
					} else { | 
						|
						var otherGroup = (pull ? to : from).options.group.name; | 
						|
 | 
						|
						return (value === true || | 
						|
						(typeof value === 'string' && value === otherGroup) || | 
						|
						(value.join && value.indexOf(otherGroup) > -1)); | 
						|
					} | 
						|
				}; | 
						|
			} | 
						|
 | 
						|
			var group = {}; | 
						|
			var originalGroup = options.group; | 
						|
 | 
						|
			if (!originalGroup || typeof originalGroup != 'object') { | 
						|
				originalGroup = {name: originalGroup}; | 
						|
			} | 
						|
 | 
						|
			group.name = originalGroup.name; | 
						|
			group.checkPull = toFn(originalGroup.pull, true); | 
						|
			group.checkPut = toFn(originalGroup.put); | 
						|
			group.revertClone = originalGroup.revertClone; | 
						|
 | 
						|
			options.group = group; | 
						|
		}, | 
						|
 | 
						|
		_checkAlignment = function(evt) { | 
						|
			if (!dragEl || !dragEl.parentNode) return; | 
						|
			dragEl.parentNode[expando] && dragEl.parentNode[expando]._computeIsAligned(evt); | 
						|
		}, | 
						|
 | 
						|
		_isTrueParentSortable = function(el, target) { | 
						|
			var trueParent = target; | 
						|
			while (!trueParent[expando]) { | 
						|
				trueParent = trueParent.parentNode; | 
						|
			} | 
						|
 | 
						|
			return el === trueParent; | 
						|
		}, | 
						|
 | 
						|
		_artificalBubble = function(sortable, originalEvt, method) { | 
						|
			// Artificial IE bubbling | 
						|
			var nextParent = sortable.parentNode; | 
						|
			while (nextParent && !nextParent[expando]) { | 
						|
				nextParent = nextParent.parentNode; | 
						|
			} | 
						|
 | 
						|
			if (nextParent) { | 
						|
				nextParent[expando][method](_extend(originalEvt, { | 
						|
					artificialBubble: true | 
						|
				})); | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
		_hideGhostForTarget = function() { | 
						|
			if (!supportCssPointerEvents && ghostEl) { | 
						|
				_css(ghostEl, 'display', 'none'); | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
		_unhideGhostForTarget = function() { | 
						|
			if (!supportCssPointerEvents && ghostEl) { | 
						|
				_css(ghostEl, 'display', ''); | 
						|
			} | 
						|
		}; | 
						|
 | 
						|
 | 
						|
	// #1184 fix - Prevent click event on fallback if dragged but item not changed position | 
						|
	document.addEventListener('click', function(evt) { | 
						|
		if (ignoreNextClick) { | 
						|
			evt.preventDefault(); | 
						|
			evt.stopPropagation && evt.stopPropagation(); | 
						|
			evt.stopImmediatePropagation && evt.stopImmediatePropagation(); | 
						|
			ignoreNextClick = false; | 
						|
			return false; | 
						|
		} | 
						|
	}, true); | 
						|
 | 
						|
	var nearestEmptyInsertDetectEvent = function(evt) { | 
						|
		evt = evt.touches ? evt.touches[0] : evt; | 
						|
		if (dragEl) { | 
						|
			var nearest = _detectNearestEmptySortable(evt.clientX, evt.clientY); | 
						|
 | 
						|
			if (nearest) { | 
						|
				nearest[expando]._onDragOver({ | 
						|
					clientX: evt.clientX, | 
						|
					clientY: evt.clientY, | 
						|
					target: nearest, | 
						|
					rootEl: nearest | 
						|
				}); | 
						|
			} | 
						|
		} | 
						|
	}; | 
						|
	// We do not want this to be triggered if completed (bubbling canceled), so only define it here | 
						|
	_on(document, 'dragover', nearestEmptyInsertDetectEvent); | 
						|
	_on(document, 'mousemove', nearestEmptyInsertDetectEvent); | 
						|
	_on(document, 'touchmove', nearestEmptyInsertDetectEvent); | 
						|
 | 
						|
	/** | 
						|
	 * @class  Sortable | 
						|
	 * @param  {HTMLElement}  el | 
						|
	 * @param  {Object}       [options] | 
						|
	 */ | 
						|
	function Sortable(el, options) { | 
						|
		if (!(el && el.nodeType && el.nodeType === 1)) { | 
						|
			throw 'Sortable: `el` must be HTMLElement, not ' + {}.toString.call(el); | 
						|
		} | 
						|
 | 
						|
		this.el = el; // root element | 
						|
		this.options = options = _extend({}, options); | 
						|
 | 
						|
 | 
						|
		// Export instance | 
						|
		el[expando] = this; | 
						|
 | 
						|
		// Default options | 
						|
		var defaults = { | 
						|
			group: null, | 
						|
			sort: true, | 
						|
			disabled: false, | 
						|
			store: null, | 
						|
			handle: null, | 
						|
			scroll: true, | 
						|
			scrollSensitivity: 30, | 
						|
			scrollSpeed: 10, | 
						|
			bubbleScroll: true, | 
						|
			draggable: /[uo]l/i.test(el.nodeName) ? '>li' : '>*', | 
						|
			swapThreshold: 1, // percentage; 0 <= x <= 1 | 
						|
			invertSwap: false, // invert always | 
						|
			invertedSwapThreshold: null, // will be set to same as swapThreshold if default | 
						|
			removeCloneOnHide: true, | 
						|
			direction: function() { | 
						|
				return _detectDirection(el, this.options); | 
						|
			}, | 
						|
			ghostClass: 'sortable-ghost', | 
						|
			chosenClass: 'sortable-chosen', | 
						|
			dragClass: 'sortable-drag', | 
						|
			ignore: 'a, img', | 
						|
			filter: null, | 
						|
			preventOnFilter: true, | 
						|
			animation: 0, | 
						|
			easing: null, | 
						|
			setData: function (dataTransfer, dragEl) { | 
						|
				dataTransfer.setData('Text', dragEl.textContent); | 
						|
			}, | 
						|
			dropBubble: false, | 
						|
			dragoverBubble: false, | 
						|
			dataIdAttr: 'data-id', | 
						|
			delay: 0, | 
						|
			touchStartThreshold: parseInt(window.devicePixelRatio, 10) || 1, | 
						|
			forceFallback: false, | 
						|
			fallbackClass: 'sortable-fallback', | 
						|
			fallbackOnBody: false, | 
						|
			fallbackTolerance: 0, | 
						|
			fallbackOffset: {x: 0, y: 0}, | 
						|
			supportPointer: Sortable.supportPointer !== false && ( | 
						|
				('PointerEvent' in window) || | 
						|
				window.navigator && ('msPointerEnabled' in window.navigator) // microsoft | 
						|
			), | 
						|
			emptyInsertThreshold: 5 | 
						|
		}; | 
						|
 | 
						|
 | 
						|
		// Set default options | 
						|
		for (var name in defaults) { | 
						|
			!(name in options) && (options[name] = defaults[name]); | 
						|
		} | 
						|
 | 
						|
		_prepareGroup(options); | 
						|
 | 
						|
		// Bind all private methods | 
						|
		for (var fn in this) { | 
						|
			if (fn.charAt(0) === '_' && typeof this[fn] === 'function') { | 
						|
				this[fn] = this[fn].bind(this); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		// Setup drag mode | 
						|
		this.nativeDraggable = options.forceFallback ? false : supportDraggable; | 
						|
 | 
						|
		if (this.nativeDraggable) { | 
						|
			// Touch start threshold cannot be greater than the native dragstart threshold | 
						|
			this.options.touchStartThreshold = 1; | 
						|
		} | 
						|
 | 
						|
		// Bind events | 
						|
		if (options.supportPointer) { | 
						|
			_on(el, 'pointerdown', this._onTapStart); | 
						|
		} else { | 
						|
			_on(el, 'mousedown', this._onTapStart); | 
						|
			_on(el, 'touchstart', this._onTapStart); | 
						|
		} | 
						|
 | 
						|
		if (this.nativeDraggable) { | 
						|
			_on(el, 'dragover', this); | 
						|
			_on(el, 'dragenter', this); | 
						|
		} | 
						|
 | 
						|
		sortables.push(this.el); | 
						|
 | 
						|
		// Restore sorting | 
						|
		options.store && options.store.get && this.sort(options.store.get(this) || []); | 
						|
	} | 
						|
 | 
						|
	Sortable.prototype = /** @lends Sortable.prototype */ { | 
						|
		constructor: Sortable, | 
						|
 | 
						|
		_computeIsAligned: function(evt) { | 
						|
			var target; | 
						|
 | 
						|
			if (ghostEl && !supportCssPointerEvents) { | 
						|
				_hideGhostForTarget(); | 
						|
				target = document.elementFromPoint(evt.clientX, evt.clientY); | 
						|
				_unhideGhostForTarget(); | 
						|
			} else { | 
						|
				target = evt.target; | 
						|
			} | 
						|
 | 
						|
			target = _closest(target, this.options.draggable, this.el, false); | 
						|
			if (_alignedSilent) return; | 
						|
			if (!dragEl || dragEl.parentNode !== this.el) return; | 
						|
 | 
						|
			var children = this.el.children; | 
						|
			for (var i = 0; i < children.length; i++) { | 
						|
				// Don't change for target in case it is changed to aligned before onDragOver is fired | 
						|
				if (_closest(children[i], this.options.draggable, this.el, false) && children[i] !== target) { | 
						|
					children[i].sortableMouseAligned = _isClientInRowColumn(evt.clientX, evt.clientY, children[i], this._getDirection(evt, null), this.options); | 
						|
				} | 
						|
			} | 
						|
			// Used for nulling last target when not in element, nothing to do with checking if aligned | 
						|
			if (!_closest(target, this.options.draggable, this.el, true)) { | 
						|
				lastTarget = null; | 
						|
			} | 
						|
 | 
						|
			_alignedSilent = true; | 
						|
			setTimeout(function() { | 
						|
				_alignedSilent = false; | 
						|
			}, 30); | 
						|
 | 
						|
		}, | 
						|
 | 
						|
		_getDirection: function(evt, target) { | 
						|
			return (typeof this.options.direction === 'function') ? this.options.direction.call(this, evt, target, dragEl) : this.options.direction; | 
						|
		}, | 
						|
 | 
						|
		_onTapStart: function (/** Event|TouchEvent */evt) { | 
						|
			if (!evt.cancelable) return; | 
						|
			var _this = this, | 
						|
				el = this.el, | 
						|
				options = this.options, | 
						|
				preventOnFilter = options.preventOnFilter, | 
						|
				type = evt.type, | 
						|
				touch = evt.touches && evt.touches[0], | 
						|
				target = (touch || evt).target, | 
						|
				originalTarget = evt.target.shadowRoot && ((evt.path && evt.path[0]) || (evt.composedPath && evt.composedPath()[0])) || target, | 
						|
				filter = options.filter, | 
						|
				startIndex; | 
						|
 | 
						|
			_saveInputCheckedState(el); | 
						|
 | 
						|
 | 
						|
			// IE: Calls events in capture mode if event element is nested. This ensures only correct element's _onTapStart goes through. | 
						|
			// This process is also done in _onDragOver | 
						|
			if (IE11OrLess && !evt.artificialBubble && !_isTrueParentSortable(el, target)) { | 
						|
				return; | 
						|
			} | 
						|
 | 
						|
			// Don't trigger start event when an element is been dragged, otherwise the evt.oldindex always wrong when set option.group. | 
						|
			if (dragEl) { | 
						|
				return; | 
						|
			} | 
						|
 | 
						|
			if (/mousedown|pointerdown/.test(type) && evt.button !== 0 || options.disabled) { | 
						|
				return; // only left button and enabled | 
						|
			} | 
						|
 | 
						|
			// cancel dnd if original target is content editable | 
						|
			if (originalTarget.isContentEditable) { | 
						|
				return; | 
						|
			} | 
						|
 | 
						|
			target = _closest(target, options.draggable, el, false); | 
						|
 | 
						|
			if (!target) { | 
						|
				if (IE11OrLess) { | 
						|
					_artificalBubble(el, evt, '_onTapStart'); | 
						|
				} | 
						|
				return; | 
						|
			} | 
						|
 | 
						|
			if (lastDownEl === target) { | 
						|
				// Ignoring duplicate `down` | 
						|
				return; | 
						|
			} | 
						|
 | 
						|
			// Get the index of the dragged element within its parent | 
						|
			startIndex = _index(target, options.draggable); | 
						|
 | 
						|
			// Check filter | 
						|
			if (typeof filter === 'function') { | 
						|
				if (filter.call(this, evt, target, this)) { | 
						|
					_dispatchEvent(_this, originalTarget, 'filter', target, el, el, startIndex); | 
						|
					preventOnFilter && evt.cancelable && evt.preventDefault(); | 
						|
					return; // cancel dnd | 
						|
				} | 
						|
			} | 
						|
			else if (filter) { | 
						|
				filter = filter.split(',').some(function (criteria) { | 
						|
					criteria = _closest(originalTarget, criteria.trim(), el, false); | 
						|
 | 
						|
					if (criteria) { | 
						|
						_dispatchEvent(_this, criteria, 'filter', target, el, el, startIndex); | 
						|
						return true; | 
						|
					} | 
						|
				}); | 
						|
 | 
						|
				if (filter) { | 
						|
					preventOnFilter && evt.cancelable && evt.preventDefault(); | 
						|
					return; // cancel dnd | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			if (options.handle && !_closest(originalTarget, options.handle, el, false)) { | 
						|
				return; | 
						|
			} | 
						|
 | 
						|
			// Prepare `dragstart` | 
						|
			this._prepareDragStart(evt, touch, target, startIndex); | 
						|
		}, | 
						|
 | 
						|
 | 
						|
		_handleAutoScroll: function(evt, fallback) { | 
						|
			if (!dragEl || !this.options.scroll) return; | 
						|
			var x = evt.clientX, | 
						|
				y = evt.clientY, | 
						|
 | 
						|
				elem = document.elementFromPoint(x, y), | 
						|
				_this = this; | 
						|
 | 
						|
			// IE does not seem to have native autoscroll, | 
						|
			// Edge's autoscroll seems too conditional, | 
						|
			// MACOS Safari does not have autoscroll, | 
						|
			// Firefox and Chrome are good | 
						|
			if (fallback || Edge || IE11OrLess || Safari) { | 
						|
				_autoScroll(evt, _this.options, elem, fallback); | 
						|
 | 
						|
				// Listener for pointer element change | 
						|
				var ogElemScroller = _getParentAutoScrollElement(elem, true); | 
						|
				if ( | 
						|
					scrolling && | 
						|
					( | 
						|
						!pointerElemChangedInterval || | 
						|
						x !== lastPointerElemX || | 
						|
						y !== lastPointerElemY | 
						|
					) | 
						|
				) { | 
						|
 | 
						|
					pointerElemChangedInterval && clearInterval(pointerElemChangedInterval); | 
						|
					// Detect for pointer elem change, emulating native DnD behaviour | 
						|
					pointerElemChangedInterval = setInterval(function() { | 
						|
						if (!dragEl) return; | 
						|
						// could also check if scroll direction on newElem changes due to parent autoscrolling | 
						|
						var newElem = _getParentAutoScrollElement(document.elementFromPoint(x, y), true); | 
						|
						if (newElem !== ogElemScroller) { | 
						|
							ogElemScroller = newElem; | 
						|
							_clearAutoScrolls(); | 
						|
							_autoScroll(evt, _this.options, ogElemScroller, fallback); | 
						|
						} | 
						|
					}, 10); | 
						|
					lastPointerElemX = x; | 
						|
					lastPointerElemY = y; | 
						|
				} | 
						|
 | 
						|
			} else { | 
						|
				// if DnD is enabled (and browser has good autoscrolling), first autoscroll will already scroll, so get parent autoscroll of first autoscroll | 
						|
				if (!_this.options.bubbleScroll || _getParentAutoScrollElement(elem, true) === _getWindowScrollingElement()) { | 
						|
					_clearAutoScrolls(); | 
						|
					return; | 
						|
				} | 
						|
				_autoScroll(evt, _this.options, _getParentAutoScrollElement(elem, false), false); | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
		_prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target, /** Number */startIndex) { | 
						|
			var _this = this, | 
						|
				el = _this.el, | 
						|
				options = _this.options, | 
						|
				ownerDocument = el.ownerDocument, | 
						|
				dragStartFn; | 
						|
 | 
						|
			if (target && !dragEl && (target.parentNode === el)) { | 
						|
				rootEl = el; | 
						|
				dragEl = target; | 
						|
				parentEl = dragEl.parentNode; | 
						|
				nextEl = dragEl.nextSibling; | 
						|
				lastDownEl = target; | 
						|
				activeGroup = options.group; | 
						|
				oldIndex = startIndex; | 
						|
 | 
						|
				tapEvt = { | 
						|
					target: dragEl, | 
						|
					clientX: (touch || evt).clientX, | 
						|
					clientY: (touch || evt).clientY | 
						|
				}; | 
						|
 | 
						|
				this._lastX = (touch || evt).clientX; | 
						|
				this._lastY = (touch || evt).clientY; | 
						|
 | 
						|
				dragEl.style['will-change'] = 'all'; | 
						|
				// undo animation if needed | 
						|
				dragEl.style.transition = ''; | 
						|
				dragEl.style.transform = ''; | 
						|
 | 
						|
				dragStartFn = function () { | 
						|
					// Delayed drag has been triggered | 
						|
					// we can re-enable the events: touchmove/mousemove | 
						|
					_this._disableDelayedDragEvents(); | 
						|
 | 
						|
					if (!FireFox && _this.nativeDraggable) { | 
						|
						dragEl.draggable = true; | 
						|
					} | 
						|
 | 
						|
					// Bind the events: dragstart/dragend | 
						|
					_this._triggerDragStart(evt, touch); | 
						|
 | 
						|
					// Drag start event | 
						|
					_dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, rootEl, oldIndex); | 
						|
 | 
						|
					// Chosen item | 
						|
					_toggleClass(dragEl, options.chosenClass, true); | 
						|
				}; | 
						|
 | 
						|
				// Disable "draggable" | 
						|
				options.ignore.split(',').forEach(function (criteria) { | 
						|
					_find(dragEl, criteria.trim(), _disableDraggable); | 
						|
				}); | 
						|
 | 
						|
				if (options.supportPointer) { | 
						|
					_on(ownerDocument, 'pointerup', _this._onDrop); | 
						|
				} else { | 
						|
					_on(ownerDocument, 'mouseup', _this._onDrop); | 
						|
					_on(ownerDocument, 'touchend', _this._onDrop); | 
						|
					_on(ownerDocument, 'touchcancel', _this._onDrop); | 
						|
				} | 
						|
 | 
						|
				// Make dragEl draggable (must be before delay for FireFox) | 
						|
				if (FireFox && this.nativeDraggable) { | 
						|
					this.options.touchStartThreshold = 4; | 
						|
					dragEl.draggable = true; | 
						|
				} | 
						|
 | 
						|
				// Delay is impossible for native DnD in Edge or IE | 
						|
				if (options.delay && (!this.nativeDraggable || !(Edge || IE11OrLess))) { | 
						|
					// If the user moves the pointer or let go the click or touch | 
						|
					// before the delay has been reached: | 
						|
					// disable the delayed drag | 
						|
					_on(ownerDocument, 'mouseup', _this._disableDelayedDrag); | 
						|
					_on(ownerDocument, 'touchend', _this._disableDelayedDrag); | 
						|
					_on(ownerDocument, 'touchcancel', _this._disableDelayedDrag); | 
						|
					_on(ownerDocument, 'mousemove', _this._delayedDragTouchMoveHandler); | 
						|
					_on(ownerDocument, 'touchmove', _this._delayedDragTouchMoveHandler); | 
						|
					options.supportPointer && _on(ownerDocument, 'pointermove', _this._delayedDragTouchMoveHandler); | 
						|
 | 
						|
					_this._dragStartTimer = setTimeout(dragStartFn, options.delay); | 
						|
				} else { | 
						|
					dragStartFn(); | 
						|
				} | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
		_delayedDragTouchMoveHandler: function (/** TouchEvent|PointerEvent **/e) { | 
						|
			var touch = e.touches ? e.touches[0] : e; | 
						|
			if (max(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY)) | 
						|
					>= Math.floor(this.options.touchStartThreshold / (this.nativeDraggable && window.devicePixelRatio || 1)) | 
						|
			) { | 
						|
				this._disableDelayedDrag(); | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
		_disableDelayedDrag: function () { | 
						|
			dragEl && _disableDraggable(dragEl); | 
						|
			clearTimeout(this._dragStartTimer); | 
						|
 | 
						|
			this._disableDelayedDragEvents(); | 
						|
		}, | 
						|
 | 
						|
		_disableDelayedDragEvents: function () { | 
						|
			var ownerDocument = this.el.ownerDocument; | 
						|
			_off(ownerDocument, 'mouseup', this._disableDelayedDrag); | 
						|
			_off(ownerDocument, 'touchend', this._disableDelayedDrag); | 
						|
			_off(ownerDocument, 'touchcancel', this._disableDelayedDrag); | 
						|
			_off(ownerDocument, 'mousemove', this._delayedDragTouchMoveHandler); | 
						|
			_off(ownerDocument, 'touchmove', this._delayedDragTouchMoveHandler); | 
						|
			_off(ownerDocument, 'pointermove', this._delayedDragTouchMoveHandler); | 
						|
		}, | 
						|
 | 
						|
		_triggerDragStart: function (/** Event */evt, /** Touch */touch) { | 
						|
			touch = touch || (evt.pointerType == 'touch' ? evt : null); | 
						|
 | 
						|
			if (!this.nativeDraggable || touch) { | 
						|
				if (this.options.supportPointer) { | 
						|
					_on(document, 'pointermove', this._onTouchMove); | 
						|
				} else if (touch) { | 
						|
					_on(document, 'touchmove', this._onTouchMove); | 
						|
				} else { | 
						|
					_on(document, 'mousemove', this._onTouchMove); | 
						|
				} | 
						|
			} else { | 
						|
				_on(dragEl, 'dragend', this); | 
						|
				_on(rootEl, 'dragstart', this._onDragStart); | 
						|
			} | 
						|
 | 
						|
			try { | 
						|
				if (document.selection) { | 
						|
					// Timeout neccessary for IE9 | 
						|
					_nextTick(function () { | 
						|
						document.selection.empty(); | 
						|
					}); | 
						|
				} else { | 
						|
					window.getSelection().removeAllRanges(); | 
						|
				} | 
						|
			} catch (err) { | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
		_dragStarted: function (fallback, evt) { | 
						|
			awaitingDragStarted = false; | 
						|
			if (rootEl && dragEl) { | 
						|
				if (this.nativeDraggable) { | 
						|
					_on(document, 'dragover', this._handleAutoScroll); | 
						|
					_on(document, 'dragover', _checkAlignment); | 
						|
				} | 
						|
				var options = this.options; | 
						|
 | 
						|
				// Apply effect | 
						|
				!fallback && _toggleClass(dragEl, options.dragClass, false); | 
						|
				_toggleClass(dragEl, options.ghostClass, true); | 
						|
 | 
						|
				// In case dragging an animated element | 
						|
				_css(dragEl, 'transform', ''); | 
						|
 | 
						|
				Sortable.active = this; | 
						|
 | 
						|
				fallback && this._appendGhost(); | 
						|
 | 
						|
				// Drag start event | 
						|
				_dispatchEvent(this, rootEl, 'start', dragEl, rootEl, rootEl, oldIndex, undefined, evt); | 
						|
			} else { | 
						|
				this._nulling(); | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
		_emulateDragOver: function (forAutoScroll) { | 
						|
			if (touchEvt) { | 
						|
				if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY && !forAutoScroll) { | 
						|
					return; | 
						|
				} | 
						|
				this._lastX = touchEvt.clientX; | 
						|
				this._lastY = touchEvt.clientY; | 
						|
 | 
						|
				_hideGhostForTarget(); | 
						|
 | 
						|
				var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY); | 
						|
				var parent = target; | 
						|
 | 
						|
				while (target && target.shadowRoot) { | 
						|
					target = target.shadowRoot.elementFromPoint(touchEvt.clientX, touchEvt.clientY); | 
						|
					parent = target; | 
						|
				} | 
						|
 | 
						|
				if (parent) { | 
						|
					do { | 
						|
						if (parent[expando]) { | 
						|
							var inserted; | 
						|
 | 
						|
							inserted = parent[expando]._onDragOver({ | 
						|
								clientX: touchEvt.clientX, | 
						|
								clientY: touchEvt.clientY, | 
						|
								target: target, | 
						|
								rootEl: parent | 
						|
							}); | 
						|
 | 
						|
							if (inserted && !this.options.dragoverBubble) { | 
						|
								break; | 
						|
							} | 
						|
						} | 
						|
 | 
						|
						target = parent; // store last element | 
						|
					} | 
						|
					/* jshint boss:true */ | 
						|
					while (parent = parent.parentNode); | 
						|
				} | 
						|
				dragEl.parentNode[expando]._computeIsAligned(touchEvt); | 
						|
 | 
						|
				_unhideGhostForTarget(); | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
 | 
						|
		_onTouchMove: function (/**TouchEvent*/evt, forAutoScroll) { | 
						|
			if (tapEvt) { | 
						|
				var	options = this.options, | 
						|
					fallbackTolerance = options.fallbackTolerance, | 
						|
					fallbackOffset = options.fallbackOffset, | 
						|
					touch = evt.touches ? evt.touches[0] : evt, | 
						|
					matrix = ghostEl && _matrix(ghostEl), | 
						|
					scaleX = ghostEl && matrix && matrix.a, | 
						|
					scaleY = ghostEl && matrix && matrix.d, | 
						|
					relativeScrollOffset = PositionGhostAbsolutely && ghostRelativeParent && _getRelativeScrollOffset(ghostRelativeParent), | 
						|
					dx = ((touch.clientX - tapEvt.clientX) | 
						|
							+ fallbackOffset.x) / (scaleX || 1) | 
						|
							+ (relativeScrollOffset ? (relativeScrollOffset[0] - ghostRelativeParentInitialScroll[0]) : 0) / (scaleX || 1), | 
						|
					dy = ((touch.clientY - tapEvt.clientY) | 
						|
							+ fallbackOffset.y) / (scaleY || 1) | 
						|
							+ (relativeScrollOffset ? (relativeScrollOffset[1] - ghostRelativeParentInitialScroll[1]) : 0) / (scaleY || 1), | 
						|
					translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)'; | 
						|
 | 
						|
				// only set the status to dragging, when we are actually dragging | 
						|
				if (!Sortable.active && !awaitingDragStarted) { | 
						|
					if (fallbackTolerance && | 
						|
						min(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY)) < fallbackTolerance | 
						|
					) { | 
						|
						return; | 
						|
					} | 
						|
					this._onDragStart(evt, true); | 
						|
				} | 
						|
 | 
						|
				!forAutoScroll && this._handleAutoScroll(touch, true); | 
						|
 | 
						|
				moved = true; | 
						|
				touchEvt = touch; | 
						|
 | 
						|
				_css(ghostEl, 'webkitTransform', translate3d); | 
						|
				_css(ghostEl, 'mozTransform', translate3d); | 
						|
				_css(ghostEl, 'msTransform', translate3d); | 
						|
				_css(ghostEl, 'transform', translate3d); | 
						|
 | 
						|
				evt.cancelable && evt.preventDefault(); | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
		_appendGhost: function () { | 
						|
			// Bug if using scale(): https://stackoverflow.com/questions/2637058 | 
						|
			// Not being adjusted for | 
						|
			if (!ghostEl) { | 
						|
				var container = this.options.fallbackOnBody ? document.body : rootEl, | 
						|
					rect = _getRect(dragEl, true, container, !PositionGhostAbsolutely), | 
						|
					css = _css(dragEl), | 
						|
					options = this.options; | 
						|
 | 
						|
				// Position absolutely | 
						|
				if (PositionGhostAbsolutely) { | 
						|
					// Get relatively positioned parent | 
						|
					ghostRelativeParent = container; | 
						|
 | 
						|
					while ( | 
						|
						_css(ghostRelativeParent, 'position') === 'static' && | 
						|
						_css(ghostRelativeParent, 'transform') === 'none' && | 
						|
						ghostRelativeParent !== document | 
						|
					) { | 
						|
						ghostRelativeParent = ghostRelativeParent.parentNode; | 
						|
					} | 
						|
 | 
						|
					if (ghostRelativeParent !== document) { | 
						|
						var ghostRelativeParentRect = _getRect(ghostRelativeParent, true); | 
						|
 | 
						|
						rect.top -= ghostRelativeParentRect.top; | 
						|
						rect.left -= ghostRelativeParentRect.left; | 
						|
					} | 
						|
 | 
						|
					if (ghostRelativeParent !== document.body && ghostRelativeParent !== document.documentElement) { | 
						|
						if (ghostRelativeParent === document) ghostRelativeParent = _getWindowScrollingElement(); | 
						|
 | 
						|
						rect.top += ghostRelativeParent.scrollTop; | 
						|
						rect.left += ghostRelativeParent.scrollLeft; | 
						|
					} else { | 
						|
						ghostRelativeParent = _getWindowScrollingElement(); | 
						|
					} | 
						|
					ghostRelativeParentInitialScroll = _getRelativeScrollOffset(ghostRelativeParent); | 
						|
				} | 
						|
 | 
						|
 | 
						|
				ghostEl = dragEl.cloneNode(true); | 
						|
 | 
						|
				_toggleClass(ghostEl, options.ghostClass, false); | 
						|
				_toggleClass(ghostEl, options.fallbackClass, true); | 
						|
				_toggleClass(ghostEl, options.dragClass, true); | 
						|
 | 
						|
				_css(ghostEl, 'box-sizing', 'border-box'); | 
						|
				_css(ghostEl, 'margin', 0); | 
						|
				_css(ghostEl, 'top', rect.top); | 
						|
				_css(ghostEl, 'left', rect.left); | 
						|
				_css(ghostEl, 'width', rect.width); | 
						|
				_css(ghostEl, 'height', rect.height); | 
						|
				_css(ghostEl, 'opacity', '0.8'); | 
						|
				_css(ghostEl, 'position', (PositionGhostAbsolutely ? 'absolute' : 'fixed')); | 
						|
				_css(ghostEl, 'zIndex', '100000'); | 
						|
				_css(ghostEl, 'pointerEvents', 'none'); | 
						|
 | 
						|
				container.appendChild(ghostEl); | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
		_onDragStart: function (/**Event*/evt, /**boolean*/fallback) { | 
						|
			var _this = this; | 
						|
			var dataTransfer = evt.dataTransfer; | 
						|
			var options = _this.options; | 
						|
 | 
						|
			// Setup clone | 
						|
			cloneEl = _clone(dragEl); | 
						|
 | 
						|
			cloneEl.draggable = false; | 
						|
			cloneEl.style['will-change'] = ''; | 
						|
 | 
						|
			this._hideClone(); | 
						|
 | 
						|
			_toggleClass(cloneEl, _this.options.chosenClass, false); | 
						|
 | 
						|
 | 
						|
			// #1143: IFrame support workaround | 
						|
			_this._cloneId = _nextTick(function () { | 
						|
				if (!_this.options.removeCloneOnHide) { | 
						|
					rootEl.insertBefore(cloneEl, dragEl); | 
						|
				} | 
						|
				_dispatchEvent(_this, rootEl, 'clone', dragEl); | 
						|
			}); | 
						|
 | 
						|
 | 
						|
			!fallback && _toggleClass(dragEl, options.dragClass, true); | 
						|
 | 
						|
			// Set proper drop events | 
						|
			if (fallback) { | 
						|
				ignoreNextClick = true; | 
						|
				_this._loopId = setInterval(_this._emulateDragOver, 50); | 
						|
			} else { | 
						|
				// Undo what was set in _prepareDragStart before drag started | 
						|
				_off(document, 'mouseup', _this._onDrop); | 
						|
				_off(document, 'touchend', _this._onDrop); | 
						|
				_off(document, 'touchcancel', _this._onDrop); | 
						|
 | 
						|
				if (dataTransfer) { | 
						|
					dataTransfer.effectAllowed = 'move'; | 
						|
					options.setData && options.setData.call(_this, dataTransfer, dragEl); | 
						|
				} | 
						|
 | 
						|
				_on(document, 'drop', _this); | 
						|
 | 
						|
				// #1276 fix: | 
						|
				_css(dragEl, 'transform', 'translateZ(0)'); | 
						|
			} | 
						|
 | 
						|
			awaitingDragStarted = true; | 
						|
 | 
						|
			_this._dragStartId = _nextTick(_this._dragStarted.bind(_this, fallback, evt)); | 
						|
			_on(document, 'selectstart', _this); | 
						|
			if (Safari) { | 
						|
				_css(document.body, 'user-select', 'none'); | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
 | 
						|
		// Returns true - if no further action is needed (either inserted or another condition) | 
						|
		_onDragOver: function (/**Event*/evt) { | 
						|
			var el = this.el, | 
						|
				target = evt.target, | 
						|
				dragRect, | 
						|
				targetRect, | 
						|
				revert, | 
						|
				options = this.options, | 
						|
				group = options.group, | 
						|
				activeSortable = Sortable.active, | 
						|
				isOwner = (activeGroup === group), | 
						|
				canSort = options.sort, | 
						|
				_this = this; | 
						|
 | 
						|
			if (_silent) return; | 
						|
 | 
						|
			// IE event order fix | 
						|
			if (IE11OrLess && !evt.rootEl && !evt.artificialBubble && !_isTrueParentSortable(el, target)) { | 
						|
				return; | 
						|
			} | 
						|
 | 
						|
			// Return invocation when dragEl is inserted (or completed) | 
						|
			function completed(insertion) { | 
						|
				if (insertion) { | 
						|
					if (isOwner) { | 
						|
						activeSortable._hideClone(); | 
						|
					} else { | 
						|
						activeSortable._showClone(_this); | 
						|
					} | 
						|
 | 
						|
					if (activeSortable) { | 
						|
						// Set ghost class to new sortable's ghost class | 
						|
						_toggleClass(dragEl, putSortable ? putSortable.options.ghostClass : activeSortable.options.ghostClass, false); | 
						|
						_toggleClass(dragEl, options.ghostClass, true); | 
						|
					} | 
						|
 | 
						|
					if (putSortable !== _this && _this !== Sortable.active) { | 
						|
						putSortable = _this; | 
						|
					} else if (_this === Sortable.active) { | 
						|
						putSortable = null; | 
						|
					} | 
						|
 | 
						|
					// Animation | 
						|
					dragRect && _this._animate(dragRect, dragEl); | 
						|
					target && targetRect && _this._animate(targetRect, target); | 
						|
				} | 
						|
 | 
						|
 | 
						|
				// Null lastTarget if it is not inside a previously swapped element | 
						|
				if ((target === dragEl && !dragEl.animated) || (target === el && !target.animated)) { | 
						|
					lastTarget = null; | 
						|
				} | 
						|
				// no bubbling and not fallback | 
						|
				if (!options.dragoverBubble && !evt.rootEl && target !== document) { | 
						|
					_this._handleAutoScroll(evt); | 
						|
					dragEl.parentNode[expando]._computeIsAligned(evt); | 
						|
				} | 
						|
 | 
						|
				!options.dragoverBubble && evt.stopPropagation && evt.stopPropagation(); | 
						|
 | 
						|
				return true; | 
						|
			} | 
						|
 | 
						|
			// Call when dragEl has been inserted | 
						|
			function changed() { | 
						|
				_dispatchEvent(_this, rootEl, 'change', target, el, rootEl, oldIndex, _index(dragEl, options.draggable), evt); | 
						|
			} | 
						|
 | 
						|
 | 
						|
			if (evt.preventDefault !== void 0) { | 
						|
				evt.cancelable && evt.preventDefault(); | 
						|
			} | 
						|
 | 
						|
 | 
						|
			moved = true; | 
						|
 | 
						|
			target = _closest(target, options.draggable, el, true); | 
						|
 | 
						|
			// target is dragEl or target is animated | 
						|
			if (!!_closest(evt.target, null, dragEl, true) || target.animated) { | 
						|
				return completed(false); | 
						|
			} | 
						|
 | 
						|
			if (target !== dragEl) { | 
						|
				ignoreNextClick = false; | 
						|
			} | 
						|
 | 
						|
			if (activeSortable && !options.disabled && | 
						|
				(isOwner | 
						|
					? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list | 
						|
					: ( | 
						|
						putSortable === this || | 
						|
						( | 
						|
							(this.lastPutMode = activeGroup.checkPull(this, activeSortable, dragEl, evt)) && | 
						|
							group.checkPut(this, activeSortable, dragEl, evt) | 
						|
						) | 
						|
					) | 
						|
				) | 
						|
			) { | 
						|
				var axis = this._getDirection(evt, target); | 
						|
 | 
						|
				dragRect = _getRect(dragEl); | 
						|
 | 
						|
				if (revert) { | 
						|
					this._hideClone(); | 
						|
					parentEl = rootEl; // actualization | 
						|
 | 
						|
					if (nextEl) { | 
						|
						rootEl.insertBefore(dragEl, nextEl); | 
						|
					} else { | 
						|
						rootEl.appendChild(dragEl); | 
						|
					} | 
						|
 | 
						|
					return completed(true); | 
						|
				} | 
						|
 | 
						|
				var elLastChild = _lastChild(el); | 
						|
 | 
						|
				if (!elLastChild || _ghostIsLast(evt, axis, el) && !elLastChild.animated) { | 
						|
					// assign target only if condition is true | 
						|
					if (elLastChild && el === evt.target) { | 
						|
						target = elLastChild; | 
						|
					} | 
						|
 | 
						|
					if (target) { | 
						|
						targetRect = _getRect(target); | 
						|
					} | 
						|
 | 
						|
					if (isOwner) { | 
						|
						activeSortable._hideClone(); | 
						|
					} else { | 
						|
						activeSortable._showClone(this); | 
						|
					} | 
						|
 | 
						|
					if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, !!target) !== false) { | 
						|
						el.appendChild(dragEl); | 
						|
						parentEl = el; // actualization | 
						|
						realDragElRect = null; | 
						|
 | 
						|
						changed(); | 
						|
						return completed(true); | 
						|
					} | 
						|
				} | 
						|
				else if (target && target !== dragEl && target.parentNode === el) { | 
						|
					var direction = 0, | 
						|
						targetBeforeFirstSwap, | 
						|
						aligned = target.sortableMouseAligned, | 
						|
						differentLevel = dragEl.parentNode !== el, | 
						|
						side1 = axis === 'vertical' ? 'top' : 'left', | 
						|
						scrolledPastTop = _isScrolledPast(target, 'top') || _isScrolledPast(dragEl, 'top'), | 
						|
						scrollBefore = scrolledPastTop ? scrolledPastTop.scrollTop : void 0; | 
						|
 | 
						|
 | 
						|
					if (lastTarget !== target) { | 
						|
						lastMode = null; | 
						|
						targetBeforeFirstSwap = _getRect(target)[side1]; | 
						|
						pastFirstInvertThresh = false; | 
						|
					} | 
						|
 | 
						|
					// Reference: https://www.lucidchart.com/documents/view/10fa0e93-e362-4126-aca2-b709ee56bd8b/0 | 
						|
					if ( | 
						|
						_isElInRowColumn(dragEl, target, axis) && aligned || | 
						|
						differentLevel || | 
						|
						scrolledPastTop || | 
						|
						options.invertSwap || | 
						|
						lastMode === 'insert' || | 
						|
						// Needed, in the case that we are inside target and inserted because not aligned... aligned will stay false while inside | 
						|
						// and lastMode will change to 'insert', but we must swap | 
						|
						lastMode === 'swap' | 
						|
					) { | 
						|
						// New target that we will be inside | 
						|
						if (lastMode !== 'swap') { | 
						|
							isCircumstantialInvert = options.invertSwap || differentLevel; | 
						|
						} | 
						|
 | 
						|
						direction = _getSwapDirection(evt, target, axis, | 
						|
							options.swapThreshold, options.invertedSwapThreshold == null ? options.swapThreshold : options.invertedSwapThreshold, | 
						|
							isCircumstantialInvert, | 
						|
							lastTarget === target); | 
						|
						lastMode = 'swap'; | 
						|
					} else { | 
						|
						// Insert at position | 
						|
						direction = _getInsertDirection(target); | 
						|
						lastMode = 'insert'; | 
						|
					} | 
						|
					if (direction === 0) return completed(false); | 
						|
 | 
						|
					realDragElRect = null; | 
						|
					lastTarget = target; | 
						|
 | 
						|
					lastDirection = direction; | 
						|
 | 
						|
					targetRect = _getRect(target); | 
						|
 | 
						|
					var nextSibling = target.nextElementSibling, | 
						|
						after = false; | 
						|
 | 
						|
					after = direction === 1; | 
						|
 | 
						|
					var moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, after); | 
						|
 | 
						|
					if (moveVector !== false) { | 
						|
						if (moveVector === 1 || moveVector === -1) { | 
						|
							after = (moveVector === 1); | 
						|
						} | 
						|
 | 
						|
						_silent = true; | 
						|
						setTimeout(_unsilent, 30); | 
						|
 | 
						|
						if (isOwner) { | 
						|
							activeSortable._hideClone(); | 
						|
						} else { | 
						|
							activeSortable._showClone(this); | 
						|
						} | 
						|
 | 
						|
						if (after && !nextSibling) { | 
						|
							el.appendChild(dragEl); | 
						|
						} else { | 
						|
							target.parentNode.insertBefore(dragEl, after ? nextSibling : target); | 
						|
						} | 
						|
 | 
						|
						// Undo chrome's scroll adjustment | 
						|
						if (scrolledPastTop) { | 
						|
							_scrollBy(scrolledPastTop, 0, scrollBefore - scrolledPastTop.scrollTop); | 
						|
						} | 
						|
 | 
						|
						parentEl = dragEl.parentNode; // actualization | 
						|
 | 
						|
						// must be done before animation | 
						|
						if (targetBeforeFirstSwap !== undefined && !isCircumstantialInvert) { | 
						|
							targetMoveDistance = abs(targetBeforeFirstSwap - _getRect(target)[side1]); | 
						|
						} | 
						|
						changed(); | 
						|
 | 
						|
						return completed(true); | 
						|
					} | 
						|
				} | 
						|
 | 
						|
				if (el.contains(dragEl)) { | 
						|
					return completed(false); | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			if (IE11OrLess && !evt.rootEl) { | 
						|
				_artificalBubble(el, evt, '_onDragOver'); | 
						|
			} | 
						|
 | 
						|
			return false; | 
						|
		}, | 
						|
 | 
						|
		_animate: function (prevRect, target) { | 
						|
			var ms = this.options.animation; | 
						|
 | 
						|
			if (ms) { | 
						|
				var currentRect = _getRect(target); | 
						|
 | 
						|
				if (target === dragEl) { | 
						|
					realDragElRect = currentRect; | 
						|
				} | 
						|
 | 
						|
				if (prevRect.nodeType === 1) { | 
						|
					prevRect = _getRect(prevRect); | 
						|
				} | 
						|
 | 
						|
				// Check if actually moving position | 
						|
				if ((prevRect.left + prevRect.width / 2) !== (currentRect.left + currentRect.width / 2) | 
						|
					|| (prevRect.top + prevRect.height / 2) !== (currentRect.top + currentRect.height / 2) | 
						|
				) { | 
						|
					var matrix = _matrix(this.el), | 
						|
						scaleX = matrix && matrix.a, | 
						|
						scaleY = matrix && matrix.d; | 
						|
 | 
						|
					_css(target, 'transition', 'none'); | 
						|
					_css(target, 'transform', 'translate3d(' | 
						|
						+ (prevRect.left - currentRect.left) / (scaleX ? scaleX : 1) + 'px,' | 
						|
						+ (prevRect.top - currentRect.top) / (scaleY ? scaleY : 1) + 'px,0)' | 
						|
					); | 
						|
 | 
						|
					forRepaintDummy = target.offsetWidth; // repaint | 
						|
					_css(target, 'transition', 'transform ' + ms + 'ms' + (this.options.easing ? ' ' + this.options.easing : '')); | 
						|
					_css(target, 'transform', 'translate3d(0,0,0)'); | 
						|
				} | 
						|
 | 
						|
				(typeof target.animated === 'number') && clearTimeout(target.animated); | 
						|
				target.animated = setTimeout(function () { | 
						|
					_css(target, 'transition', ''); | 
						|
					_css(target, 'transform', ''); | 
						|
					target.animated = false; | 
						|
				}, ms); | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
		_offUpEvents: function () { | 
						|
			var ownerDocument = this.el.ownerDocument; | 
						|
 | 
						|
			_off(document, 'touchmove', this._onTouchMove); | 
						|
			_off(document, 'pointermove', this._onTouchMove); | 
						|
			_off(ownerDocument, 'mouseup', this._onDrop); | 
						|
			_off(ownerDocument, 'touchend', this._onDrop); | 
						|
			_off(ownerDocument, 'pointerup', this._onDrop); | 
						|
			_off(ownerDocument, 'touchcancel', this._onDrop); | 
						|
			_off(document, 'selectstart', this); | 
						|
		}, | 
						|
 | 
						|
		_onDrop: function (/**Event*/evt) { | 
						|
			var el = this.el, | 
						|
				options = this.options; | 
						|
			awaitingDragStarted = false; | 
						|
			scrolling = false; | 
						|
			isCircumstantialInvert = false; | 
						|
			pastFirstInvertThresh = false; | 
						|
 | 
						|
			clearInterval(this._loopId); | 
						|
 | 
						|
			clearInterval(pointerElemChangedInterval); | 
						|
			_clearAutoScrolls(); | 
						|
			_cancelThrottle(); | 
						|
 | 
						|
			clearTimeout(this._dragStartTimer); | 
						|
 | 
						|
			_cancelNextTick(this._cloneId); | 
						|
			_cancelNextTick(this._dragStartId); | 
						|
 | 
						|
			// Unbind events | 
						|
			_off(document, 'mousemove', this._onTouchMove); | 
						|
 | 
						|
 | 
						|
			if (this.nativeDraggable) { | 
						|
				_off(document, 'drop', this); | 
						|
				_off(el, 'dragstart', this._onDragStart); | 
						|
				_off(document, 'dragover', this._handleAutoScroll); | 
						|
				_off(document, 'dragover', _checkAlignment); | 
						|
			} | 
						|
 | 
						|
			if (Safari) { | 
						|
				_css(document.body, 'user-select', ''); | 
						|
			} | 
						|
 | 
						|
			this._offUpEvents(); | 
						|
 | 
						|
			if (evt) { | 
						|
				if (moved) { | 
						|
					evt.cancelable && evt.preventDefault(); | 
						|
					!options.dropBubble && evt.stopPropagation(); | 
						|
				} | 
						|
 | 
						|
				ghostEl && ghostEl.parentNode && ghostEl.parentNode.removeChild(ghostEl); | 
						|
 | 
						|
				if (rootEl === parentEl || (putSortable && putSortable.lastPutMode !== 'clone')) { | 
						|
					// Remove clone | 
						|
					cloneEl && cloneEl.parentNode && cloneEl.parentNode.removeChild(cloneEl); | 
						|
				} | 
						|
 | 
						|
				if (dragEl) { | 
						|
					if (this.nativeDraggable) { | 
						|
						_off(dragEl, 'dragend', this); | 
						|
					} | 
						|
 | 
						|
					_disableDraggable(dragEl); | 
						|
					dragEl.style['will-change'] = ''; | 
						|
 | 
						|
					// Remove class's | 
						|
					_toggleClass(dragEl, putSortable ? putSortable.options.ghostClass : this.options.ghostClass, false); | 
						|
					_toggleClass(dragEl, this.options.chosenClass, false); | 
						|
 | 
						|
					// Drag stop event | 
						|
					_dispatchEvent(this, rootEl, 'unchoose', dragEl, parentEl, rootEl, oldIndex, null, evt); | 
						|
 | 
						|
					if (rootEl !== parentEl) { | 
						|
						newIndex = _index(dragEl, options.draggable); | 
						|
 | 
						|
						if (newIndex >= 0) { | 
						|
							// Add event | 
						|
							_dispatchEvent(null, parentEl, 'add', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); | 
						|
 | 
						|
							// Remove event | 
						|
							_dispatchEvent(this, rootEl, 'remove', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); | 
						|
 | 
						|
							// drag from one list and drop into another | 
						|
							_dispatchEvent(null, parentEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); | 
						|
							_dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); | 
						|
						} | 
						|
 | 
						|
						putSortable && putSortable.save(); | 
						|
					} | 
						|
					else { | 
						|
						if (dragEl.nextSibling !== nextEl) { | 
						|
							// Get the index of the dragged element within its parent | 
						|
							newIndex = _index(dragEl, options.draggable); | 
						|
 | 
						|
							if (newIndex >= 0) { | 
						|
								// drag & drop within the same list | 
						|
								_dispatchEvent(this, rootEl, 'update', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); | 
						|
								_dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); | 
						|
							} | 
						|
						} | 
						|
					} | 
						|
 | 
						|
					if (Sortable.active) { | 
						|
						/* jshint eqnull:true */ | 
						|
						if (newIndex == null || newIndex === -1) { | 
						|
							newIndex = oldIndex; | 
						|
						} | 
						|
						_dispatchEvent(this, rootEl, 'end', dragEl, parentEl, rootEl, oldIndex, newIndex, evt); | 
						|
 | 
						|
						// Save sorting | 
						|
						this.save(); | 
						|
					} | 
						|
				} | 
						|
 | 
						|
			} | 
						|
			this._nulling(); | 
						|
		}, | 
						|
 | 
						|
		_nulling: function() { | 
						|
			rootEl = | 
						|
			dragEl = | 
						|
			parentEl = | 
						|
			ghostEl = | 
						|
			nextEl = | 
						|
			cloneEl = | 
						|
			lastDownEl = | 
						|
 | 
						|
			scrollEl = | 
						|
			scrollParentEl = | 
						|
			autoScrolls.length = | 
						|
 | 
						|
			pointerElemChangedInterval = | 
						|
			lastPointerElemX = | 
						|
			lastPointerElemY = | 
						|
 | 
						|
			tapEvt = | 
						|
			touchEvt = | 
						|
 | 
						|
			moved = | 
						|
			newIndex = | 
						|
			oldIndex = | 
						|
 | 
						|
			lastTarget = | 
						|
			lastDirection = | 
						|
 | 
						|
			forRepaintDummy = | 
						|
			realDragElRect = | 
						|
 | 
						|
			putSortable = | 
						|
			activeGroup = | 
						|
			Sortable.active = null; | 
						|
 | 
						|
			savedInputChecked.forEach(function (el) { | 
						|
				el.checked = true; | 
						|
			}); | 
						|
 | 
						|
			savedInputChecked.length = 0; | 
						|
		}, | 
						|
 | 
						|
		handleEvent: function (/**Event*/evt) { | 
						|
			switch (evt.type) { | 
						|
				case 'drop': | 
						|
				case 'dragend': | 
						|
					this._onDrop(evt); | 
						|
					break; | 
						|
 | 
						|
				case 'dragenter': | 
						|
				case 'dragover': | 
						|
					if (dragEl) { | 
						|
						this._onDragOver(evt); | 
						|
						_globalDragOver(evt); | 
						|
					} | 
						|
					break; | 
						|
 | 
						|
				case 'selectstart': | 
						|
					evt.preventDefault(); | 
						|
					break; | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
 | 
						|
		/** | 
						|
		 * Serializes the item into an array of string. | 
						|
		 * @returns {String[]} | 
						|
		 */ | 
						|
		toArray: function () { | 
						|
			var order = [], | 
						|
				el, | 
						|
				children = this.el.children, | 
						|
				i = 0, | 
						|
				n = children.length, | 
						|
				options = this.options; | 
						|
 | 
						|
			for (; i < n; i++) { | 
						|
				el = children[i]; | 
						|
				if (_closest(el, options.draggable, this.el, false)) { | 
						|
					order.push(el.getAttribute(options.dataIdAttr) || _generateId(el)); | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			return order; | 
						|
		}, | 
						|
 | 
						|
 | 
						|
		/** | 
						|
		 * Sorts the elements according to the array. | 
						|
		 * @param  {String[]}  order  order of the items | 
						|
		 */ | 
						|
		sort: function (order) { | 
						|
			var items = {}, rootEl = this.el; | 
						|
 | 
						|
			this.toArray().forEach(function (id, i) { | 
						|
				var el = rootEl.children[i]; | 
						|
 | 
						|
				if (_closest(el, this.options.draggable, rootEl, false)) { | 
						|
					items[id] = el; | 
						|
				} | 
						|
			}, this); | 
						|
 | 
						|
			order.forEach(function (id) { | 
						|
				if (items[id]) { | 
						|
					rootEl.removeChild(items[id]); | 
						|
					rootEl.appendChild(items[id]); | 
						|
				} | 
						|
			}); | 
						|
		}, | 
						|
 | 
						|
 | 
						|
		/** | 
						|
		 * Save the current sorting | 
						|
		 */ | 
						|
		save: function () { | 
						|
			var store = this.options.store; | 
						|
			store && store.set && store.set(this); | 
						|
		}, | 
						|
 | 
						|
 | 
						|
		/** | 
						|
		 * 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, false); | 
						|
		}, | 
						|
 | 
						|
 | 
						|
		/** | 
						|
		 * 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; | 
						|
 | 
						|
				if (name === 'group') { | 
						|
					_prepareGroup(options); | 
						|
				} | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
 | 
						|
		/** | 
						|
		 * Destroy | 
						|
		 */ | 
						|
		destroy: function () { | 
						|
			var el = this.el; | 
						|
 | 
						|
			el[expando] = null; | 
						|
 | 
						|
			_off(el, 'mousedown', this._onTapStart); | 
						|
			_off(el, 'touchstart', this._onTapStart); | 
						|
			_off(el, 'pointerdown', this._onTapStart); | 
						|
 | 
						|
			if (this.nativeDraggable) { | 
						|
				_off(el, 'dragover', this); | 
						|
				_off(el, 'dragenter', this); | 
						|
			} | 
						|
			// Remove draggable attributes | 
						|
			Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) { | 
						|
				el.removeAttribute('draggable'); | 
						|
			}); | 
						|
 | 
						|
			this._onDrop(); | 
						|
 | 
						|
			sortables.splice(sortables.indexOf(this.el), 1); | 
						|
 | 
						|
			this.el = el = null; | 
						|
		}, | 
						|
 | 
						|
		_hideClone: function() { | 
						|
			if (!cloneEl.cloneHidden) { | 
						|
				_css(cloneEl, 'display', 'none'); | 
						|
				cloneEl.cloneHidden = true; | 
						|
				if (cloneEl.parentNode && this.options.removeCloneOnHide) { | 
						|
					cloneEl.parentNode.removeChild(cloneEl); | 
						|
				} | 
						|
			} | 
						|
		}, | 
						|
 | 
						|
		_showClone: function(putSortable) { | 
						|
			if (putSortable.lastPutMode !== 'clone') { | 
						|
				this._hideClone(); | 
						|
				return; | 
						|
			} | 
						|
 | 
						|
			if (cloneEl.cloneHidden) { | 
						|
				// show clone at dragEl or original position | 
						|
				if (rootEl.contains(dragEl) && !this.options.group.revertClone) { | 
						|
					rootEl.insertBefore(cloneEl, dragEl); | 
						|
				} else if (nextEl) { | 
						|
					rootEl.insertBefore(cloneEl, nextEl); | 
						|
				} else { | 
						|
					rootEl.appendChild(cloneEl); | 
						|
				} | 
						|
 | 
						|
				if (this.options.group.revertClone) { | 
						|
					this._animate(dragEl, cloneEl); | 
						|
				} | 
						|
				_css(cloneEl, 'display', ''); | 
						|
				cloneEl.cloneHidden = false; | 
						|
			} | 
						|
		} | 
						|
	}; | 
						|
 | 
						|
	function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx, includeCTX) { | 
						|
		if (el) { | 
						|
			ctx = ctx || document; | 
						|
 | 
						|
			do { | 
						|
				if ( | 
						|
					selector != null && | 
						|
					( | 
						|
						selector[0] === '>' && el.parentNode === ctx && _matches(el, selector.substring(1)) || | 
						|
						_matches(el, selector) | 
						|
					) || | 
						|
					includeCTX && el === ctx | 
						|
				) { | 
						|
					return el; | 
						|
				} | 
						|
 | 
						|
				if (el === ctx) break; | 
						|
				/* jshint boss:true */ | 
						|
			} while (el = _getParentOrHost(el)); | 
						|
		} | 
						|
 | 
						|
		return null; | 
						|
	} | 
						|
 | 
						|
 | 
						|
	function _getParentOrHost(el) { | 
						|
		return (el.host && el !== document && el.host.nodeType) | 
						|
			? el.host | 
						|
			: el.parentNode; | 
						|
	} | 
						|
 | 
						|
 | 
						|
	function _globalDragOver(/**Event*/evt) { | 
						|
		if (evt.dataTransfer) { | 
						|
			evt.dataTransfer.dropEffect = 'move'; | 
						|
		} | 
						|
		evt.cancelable && evt.preventDefault(); | 
						|
	} | 
						|
 | 
						|
 | 
						|
	function _on(el, event, fn) { | 
						|
		el.addEventListener(event, fn, captureMode); | 
						|
	} | 
						|
 | 
						|
 | 
						|
	function _off(el, event, fn) { | 
						|
		el.removeEventListener(event, fn, captureMode); | 
						|
	} | 
						|
 | 
						|
 | 
						|
	function _toggleClass(el, name, state) { | 
						|
		if (el && name) { | 
						|
			if (el.classList) { | 
						|
				el.classList[state ? 'add' : 'remove'](name); | 
						|
			} | 
						|
			else { | 
						|
				var className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' '); | 
						|
				el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' '); | 
						|
			} | 
						|
		} | 
						|
	} | 
						|
 | 
						|
 | 
						|
	function _css(el, prop, val) { | 
						|
		var style = el && el.style; | 
						|
 | 
						|
		if (style) { | 
						|
			if (val === void 0) { | 
						|
				if (document.defaultView && document.defaultView.getComputedStyle) { | 
						|
					val = document.defaultView.getComputedStyle(el, ''); | 
						|
				} | 
						|
				else if (el.currentStyle) { | 
						|
					val = el.currentStyle; | 
						|
				} | 
						|
 | 
						|
				return prop === void 0 ? val : val[prop]; | 
						|
			} | 
						|
			else { | 
						|
				if (!(prop in style) && prop.indexOf('webkit') === -1) { | 
						|
					prop = '-webkit-' + prop; | 
						|
				} | 
						|
 | 
						|
				style[prop] = val + (typeof val === 'string' ? '' : 'px'); | 
						|
			} | 
						|
		} | 
						|
	} | 
						|
 | 
						|
	function _matrix(el) { | 
						|
		var appliedTransforms = ''; | 
						|
		do { | 
						|
			var transform = _css(el, 'transform'); | 
						|
 | 
						|
			if (transform && transform !== 'none') { | 
						|
				appliedTransforms = transform + ' ' + appliedTransforms; | 
						|
			} | 
						|
			/* jshint boss:true */ | 
						|
		} while (el = el.parentNode); | 
						|
 | 
						|
		if (window.DOMMatrix) { | 
						|
			return new DOMMatrix(appliedTransforms); | 
						|
		} else if (window.WebKitCSSMatrix) { | 
						|
			return new WebKitCSSMatrix(appliedTransforms); | 
						|
		} else if (window.CSSMatrix) { | 
						|
			return new CSSMatrix(appliedTransforms); | 
						|
		} | 
						|
	} | 
						|
 | 
						|
 | 
						|
	function _find(ctx, tagName, iterator) { | 
						|
		if (ctx) { | 
						|
			var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length; | 
						|
 | 
						|
			if (iterator) { | 
						|
				for (; i < n; i++) { | 
						|
					iterator(list[i], i); | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			return list; | 
						|
		} | 
						|
 | 
						|
		return []; | 
						|
	} | 
						|
 | 
						|
 | 
						|
 | 
						|
	function _dispatchEvent(sortable, rootEl, name, targetEl, toEl, fromEl, startIndex, newIndex, originalEvt) { | 
						|
		sortable = (sortable || rootEl[expando]); | 
						|
		var evt, | 
						|
			options = sortable.options, | 
						|
			onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1); | 
						|
		// Support for new CustomEvent feature | 
						|
		if (window.CustomEvent && !IE11OrLess && !Edge) { | 
						|
			evt = new CustomEvent(name, { | 
						|
				bubbles: true, | 
						|
				cancelable: true | 
						|
			}); | 
						|
		} else { | 
						|
			evt = document.createEvent('Event'); | 
						|
			evt.initEvent(name, true, true); | 
						|
		} | 
						|
 | 
						|
		evt.to = toEl || rootEl; | 
						|
		evt.from = fromEl || rootEl; | 
						|
		evt.item = targetEl || rootEl; | 
						|
		evt.clone = cloneEl; | 
						|
 | 
						|
		evt.oldIndex = startIndex; | 
						|
		evt.newIndex = newIndex; | 
						|
 | 
						|
		evt.originalEvent = originalEvt; | 
						|
		evt.pullMode = putSortable ? putSortable.lastPutMode : undefined; | 
						|
 | 
						|
		if (rootEl) { | 
						|
			rootEl.dispatchEvent(evt); | 
						|
		} | 
						|
 | 
						|
		if (options[onName]) { | 
						|
			options[onName].call(sortable, evt); | 
						|
		} | 
						|
	} | 
						|
 | 
						|
 | 
						|
	function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvt, willInsertAfter) { | 
						|
		var evt, | 
						|
			sortable = fromEl[expando], | 
						|
			onMoveFn = sortable.options.onMove, | 
						|
			retVal; | 
						|
		// Support for new CustomEvent feature | 
						|
		if (window.CustomEvent && !IE11OrLess && !Edge) { | 
						|
			evt = new CustomEvent('move', { | 
						|
				bubbles: true, | 
						|
				cancelable: true | 
						|
			}); | 
						|
		} else { | 
						|
			evt = document.createEvent('Event'); | 
						|
			evt.initEvent('move', true, true); | 
						|
		} | 
						|
 | 
						|
		evt.to = toEl; | 
						|
		evt.from = fromEl; | 
						|
		evt.dragged = dragEl; | 
						|
		evt.draggedRect = dragRect; | 
						|
		evt.related = targetEl || toEl; | 
						|
		evt.relatedRect = targetRect || _getRect(toEl); | 
						|
		evt.willInsertAfter = willInsertAfter; | 
						|
 | 
						|
		evt.originalEvent = originalEvt; | 
						|
 | 
						|
		fromEl.dispatchEvent(evt); | 
						|
 | 
						|
		if (onMoveFn) { | 
						|
			retVal = onMoveFn.call(sortable, evt, originalEvt); | 
						|
		} | 
						|
 | 
						|
		return retVal; | 
						|
	} | 
						|
 | 
						|
	function _disableDraggable(el) { | 
						|
		el.draggable = false; | 
						|
	} | 
						|
 | 
						|
	function _unsilent() { | 
						|
		_silent = false; | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * Gets nth child of el, ignoring hidden children, sortable's elements (does not ignore clone if it's visible) | 
						|
	 * and non-draggable elements | 
						|
	 * @param  {HTMLElement} el       The parent element | 
						|
	 * @param  {Number} childNum      The index of the child | 
						|
	 * @param  {Object} options       Parent Sortable's options | 
						|
	 * @return {HTMLElement}          The child at index childNum, or null if not found | 
						|
	 */ | 
						|
	function _getChild(el, childNum, options) { | 
						|
		var currentChild = 0, | 
						|
			i = 0, | 
						|
			children = el.children; | 
						|
 | 
						|
		while (i < children.length) { | 
						|
			if ( | 
						|
				children[i].style.display !== 'none' && | 
						|
				children[i] !== ghostEl && | 
						|
				children[i] !== dragEl && | 
						|
				_closest(children[i], options.draggable, el, false) | 
						|
			) { | 
						|
				if (currentChild === childNum) { | 
						|
					return children[i]; | 
						|
				} | 
						|
				currentChild++; | 
						|
			} | 
						|
 | 
						|
			i++; | 
						|
		} | 
						|
		return null; | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * Gets the last child in the el, ignoring ghostEl or invisible elements (clones) | 
						|
	 * @param  {HTMLElement} el       Parent element | 
						|
	 * @return {HTMLElement}          The last child, ignoring ghostEl | 
						|
	 */ | 
						|
	function _lastChild(el) { | 
						|
		var last = el.lastElementChild; | 
						|
 | 
						|
		while (last && (last === ghostEl || last.style.display === 'none')) { | 
						|
			last = last.previousElementSibling; | 
						|
		} | 
						|
 | 
						|
		return last || null; | 
						|
	} | 
						|
 | 
						|
	function _ghostIsLast(evt, axis, el) { | 
						|
		var elRect = _getRect(_lastChild(el)), | 
						|
			mouseOnAxis = axis === 'vertical' ? evt.clientY : evt.clientX, | 
						|
			mouseOnOppAxis = axis === 'vertical' ? evt.clientX : evt.clientY, | 
						|
			targetS2 = axis === 'vertical' ? elRect.bottom : elRect.right, | 
						|
			targetS1Opp = axis === 'vertical' ? elRect.left : elRect.top, | 
						|
			targetS2Opp = axis === 'vertical' ? elRect.right : elRect.bottom, | 
						|
			spacer = 10; | 
						|
 | 
						|
		return ( | 
						|
			axis === 'vertical' ? | 
						|
				(mouseOnOppAxis > targetS2Opp + spacer || mouseOnOppAxis <= targetS2Opp && mouseOnAxis > targetS2 && mouseOnOppAxis >= targetS1Opp) : | 
						|
				(mouseOnAxis > targetS2 && mouseOnOppAxis > targetS1Opp || mouseOnAxis <= targetS2 && mouseOnOppAxis > targetS2Opp + spacer) | 
						|
		); | 
						|
	} | 
						|
 | 
						|
	function _getSwapDirection(evt, target, axis, swapThreshold, invertedSwapThreshold, invertSwap, isLastTarget) { | 
						|
		var targetRect = _getRect(target), | 
						|
			mouseOnAxis = axis === 'vertical' ? evt.clientY : evt.clientX, | 
						|
			targetLength = axis === 'vertical' ? targetRect.height : targetRect.width, | 
						|
			targetS1 = axis === 'vertical' ? targetRect.top : targetRect.left, | 
						|
			targetS2 = axis === 'vertical' ? targetRect.bottom : targetRect.right, | 
						|
			dragRect = _getRect(dragEl), | 
						|
			invert = false; | 
						|
 | 
						|
 | 
						|
		if (!invertSwap) { | 
						|
			// Never invert or create dragEl shadow when target movemenet causes mouse to move past the end of regular swapThreshold | 
						|
			if (isLastTarget && targetMoveDistance < targetLength * swapThreshold) { // multiplied only by swapThreshold because mouse will already be inside target by (1 - threshold) * targetLength / 2 | 
						|
				// check if past first invert threshold on side opposite of lastDirection | 
						|
				if (!pastFirstInvertThresh && | 
						|
					(lastDirection === 1 ? | 
						|
						( | 
						|
							mouseOnAxis > targetS1 + targetLength * invertedSwapThreshold / 2 | 
						|
						) : | 
						|
						( | 
						|
							mouseOnAxis < targetS2 - targetLength * invertedSwapThreshold / 2 | 
						|
						) | 
						|
					) | 
						|
				) | 
						|
				{ | 
						|
					// past first invert threshold, do not restrict inverted threshold to dragEl shadow | 
						|
					pastFirstInvertThresh = true; | 
						|
				} | 
						|
 | 
						|
				if (!pastFirstInvertThresh) { | 
						|
					var dragS1 = axis === 'vertical' ? dragRect.top : dragRect.left, | 
						|
						dragS2 = axis === 'vertical' ? dragRect.bottom : dragRect.right; | 
						|
					// dragEl shadow (target move distance shadow) | 
						|
					if ( | 
						|
						lastDirection === 1 ? | 
						|
						( | 
						|
							mouseOnAxis < targetS1 + targetMoveDistance // over dragEl shadow | 
						|
						) : | 
						|
						( | 
						|
							mouseOnAxis > targetS2 - targetMoveDistance | 
						|
						) | 
						|
					) | 
						|
					{ | 
						|
						return lastDirection * -1; | 
						|
					} | 
						|
				} else { | 
						|
					invert = true; | 
						|
				} | 
						|
			} else { | 
						|
				// Regular | 
						|
				if ( | 
						|
					mouseOnAxis > targetS1 + (targetLength * (1 - swapThreshold) / 2) && | 
						|
					mouseOnAxis < targetS2 - (targetLength * (1 - swapThreshold) / 2) | 
						|
				) { | 
						|
					return _getInsertDirection(target); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		invert = invert || invertSwap; | 
						|
 | 
						|
		if (invert) { | 
						|
			// Invert of regular | 
						|
			if ( | 
						|
				mouseOnAxis < targetS1 + (targetLength * invertedSwapThreshold / 2) || | 
						|
				mouseOnAxis > targetS2 - (targetLength * invertedSwapThreshold / 2) | 
						|
			) | 
						|
			{ | 
						|
				return ((mouseOnAxis > targetS1 + targetLength / 2) ? 1 : -1); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		return 0; | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * Gets the direction dragEl must be swapped relative to target in order to make it | 
						|
	 * seem that dragEl has been "inserted" into that element's position | 
						|
	 * @param  {HTMLElement} target       The target whose position dragEl is being inserted at | 
						|
	 * @return {Number}                   Direction dragEl must be swapped | 
						|
	 */ | 
						|
	function _getInsertDirection(target) { | 
						|
		var dragElIndex = _index(dragEl), | 
						|
			targetIndex = _index(target); | 
						|
 | 
						|
		if (dragElIndex < targetIndex) { | 
						|
			return 1; | 
						|
		} else { | 
						|
			return -1; | 
						|
		} | 
						|
	} | 
						|
 | 
						|
 | 
						|
	/** | 
						|
	 * Generate id | 
						|
	 * @param   {HTMLElement} el | 
						|
	 * @returns {String} | 
						|
	 * @private | 
						|
	 */ | 
						|
	function _generateId(el) { | 
						|
		var str = el.tagName + el.className + el.src + el.href + el.textContent, | 
						|
			i = str.length, | 
						|
			sum = 0; | 
						|
 | 
						|
		while (i--) { | 
						|
			sum += str.charCodeAt(i); | 
						|
		} | 
						|
 | 
						|
		return sum.toString(36); | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * Returns the index of an element within its parent for a selected set of | 
						|
	 * elements | 
						|
	 * @param  {HTMLElement} el | 
						|
	 * @param  {selector} selector | 
						|
	 * @return {number} | 
						|
	 */ | 
						|
	function _index(el, selector) { | 
						|
		var index = 0; | 
						|
 | 
						|
		if (!el || !el.parentNode) { | 
						|
			return -1; | 
						|
		} | 
						|
 | 
						|
		while (el && (el = el.previousElementSibling)) { | 
						|
			if ((el.nodeName.toUpperCase() !== 'TEMPLATE') && el !== cloneEl) { | 
						|
				index++; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		return index; | 
						|
	} | 
						|
 | 
						|
	function _matches(/**HTMLElement*/el, /**String*/selector) { | 
						|
		if (el) { | 
						|
			try { | 
						|
				if (el.matches) { | 
						|
					return el.matches(selector); | 
						|
				} else if (el.msMatchesSelector) { | 
						|
					return el.msMatchesSelector(selector); | 
						|
				} else if (el.webkitMatchesSelector) { | 
						|
					return el.webkitMatchesSelector(selector); | 
						|
				} | 
						|
			} catch(_) { | 
						|
				return false; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		return false; | 
						|
	} | 
						|
 | 
						|
	var _throttleTimeout; | 
						|
	function _throttle(callback, ms) { | 
						|
		return function () { | 
						|
			if (!_throttleTimeout) { | 
						|
				var args = arguments, | 
						|
					_this = this; | 
						|
 | 
						|
				_throttleTimeout = setTimeout(function () { | 
						|
					if (args.length === 1) { | 
						|
						callback.call(_this, args[0]); | 
						|
					} else { | 
						|
						callback.apply(_this, args); | 
						|
					} | 
						|
 | 
						|
					_throttleTimeout = void 0; | 
						|
				}, ms); | 
						|
			} | 
						|
		}; | 
						|
	} | 
						|
 | 
						|
	function _cancelThrottle() { | 
						|
		clearTimeout(_throttleTimeout); | 
						|
		_throttleTimeout = void 0; | 
						|
	} | 
						|
 | 
						|
	function _extend(dst, src) { | 
						|
		if (dst && src) { | 
						|
			for (var key in src) { | 
						|
				if (src.hasOwnProperty(key)) { | 
						|
					dst[key] = src[key]; | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		return dst; | 
						|
	} | 
						|
 | 
						|
	function _clone(el) { | 
						|
		if (Polymer && Polymer.dom) { | 
						|
			return Polymer.dom(el).cloneNode(true); | 
						|
		} | 
						|
		else if ($) { | 
						|
			return $(el).clone(true)[0]; | 
						|
		} | 
						|
		else { | 
						|
			return el.cloneNode(true); | 
						|
		} | 
						|
	} | 
						|
 | 
						|
	function _saveInputCheckedState(root) { | 
						|
		savedInputChecked.length = 0; | 
						|
 | 
						|
		var inputs = root.getElementsByTagName('input'); | 
						|
		var idx = inputs.length; | 
						|
 | 
						|
		while (idx--) { | 
						|
			var el = inputs[idx]; | 
						|
			el.checked && savedInputChecked.push(el); | 
						|
		} | 
						|
	} | 
						|
 | 
						|
	function _nextTick(fn) { | 
						|
		return setTimeout(fn, 0); | 
						|
	} | 
						|
 | 
						|
	function _cancelNextTick(id) { | 
						|
		return clearTimeout(id); | 
						|
	} | 
						|
 | 
						|
 | 
						|
	/** | 
						|
	 * Returns the "bounding client rect" of given element | 
						|
	 * @param  {HTMLElement} el                The element whose boundingClientRect is wanted | 
						|
	 * @param  {[HTMLElement]} container       the parent the element will be placed in | 
						|
	 * @param  {[Boolean]} adjustForTransform  Whether the rect should compensate for parent's transform | 
						|
	 * @return {Object}                        The boundingClientRect of el | 
						|
	 */ | 
						|
	function _getRect(el, adjustForTransform, container, adjustForFixed) { | 
						|
		if (!el.getBoundingClientRect && el !== win) return; | 
						|
 | 
						|
		var elRect, | 
						|
			top, | 
						|
			left, | 
						|
			bottom, | 
						|
			right, | 
						|
			height, | 
						|
			width; | 
						|
 | 
						|
		if (el !== win && el !== _getWindowScrollingElement()) { | 
						|
			elRect = el.getBoundingClientRect(); | 
						|
			top = elRect.top; | 
						|
			left = elRect.left; | 
						|
			bottom = elRect.bottom; | 
						|
			right = elRect.right; | 
						|
			height = elRect.height; | 
						|
			width = elRect.width; | 
						|
		} else { | 
						|
			top = 0; | 
						|
			left = 0; | 
						|
			bottom = window.innerHeight; | 
						|
			right = window.innerWidth; | 
						|
			height = window.innerHeight; | 
						|
			width = window.innerWidth; | 
						|
		} | 
						|
 | 
						|
		if (adjustForFixed && el !== win) { | 
						|
			// Adjust for translate() | 
						|
			container = container || el.parentNode; | 
						|
 | 
						|
			// solves #1123 (see: https://stackoverflow.com/a/37953806/6088312) | 
						|
			// Not needed on <= IE11 | 
						|
			if (!IE11OrLess) { | 
						|
				do { | 
						|
					if (container && container.getBoundingClientRect && _css(container, 'transform') !== 'none') { | 
						|
						var containerRect = container.getBoundingClientRect(); | 
						|
 | 
						|
						// Set relative to edges of padding box of container | 
						|
						top -= containerRect.top + parseInt(_css(container, 'border-top-width')); | 
						|
						left -= containerRect.left + parseInt(_css(container, 'border-left-width')); | 
						|
						bottom = top + elRect.height; | 
						|
						right = left + elRect.width; | 
						|
 | 
						|
						break; | 
						|
					} | 
						|
					/* jshint boss:true */ | 
						|
				} while (container = container.parentNode); | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		if (adjustForTransform && el !== win) { | 
						|
			// Adjust for scale() | 
						|
			var matrix = _matrix(container || el), | 
						|
				scaleX = matrix && matrix.a, | 
						|
				scaleY = matrix && matrix.d; | 
						|
 | 
						|
			if (matrix) { | 
						|
				top /= scaleY; | 
						|
				left /= scaleX; | 
						|
 | 
						|
				width /= scaleX; | 
						|
				height /= scaleY; | 
						|
 | 
						|
				bottom = top + height; | 
						|
				right = left + width; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		return { | 
						|
			top: top, | 
						|
			left: left, | 
						|
			bottom: bottom, | 
						|
			right: right, | 
						|
			width: width, | 
						|
			height: height | 
						|
		}; | 
						|
	} | 
						|
 | 
						|
 | 
						|
	/** | 
						|
	 * Checks if a side of an element is scrolled past a side of it's parents | 
						|
	 * @param  {HTMLElement}  el       The element who's side being scrolled out of view is in question | 
						|
	 * @param  {String}       side     Side of the element in question ('top', 'left', 'right', 'bottom') | 
						|
	 * @return {HTMLElement}           The parent scroll element that the el's side is scrolled past, or null if there is no such element | 
						|
	 */ | 
						|
	function _isScrolledPast(el, side) { | 
						|
		var parent = _getParentAutoScrollElement(el, true), | 
						|
			elSide = _getRect(el)[side]; | 
						|
 | 
						|
		/* jshint boss:true */ | 
						|
		while (parent) { | 
						|
			var parentSide = _getRect(parent)[side], | 
						|
				visible; | 
						|
 | 
						|
			if (side === 'top' || side === 'left') { | 
						|
				visible = elSide >= parentSide; | 
						|
			} else { | 
						|
				visible = elSide <= parentSide; | 
						|
			} | 
						|
 | 
						|
			if (!visible) return parent; | 
						|
 | 
						|
			if (parent === _getWindowScrollingElement()) break; | 
						|
 | 
						|
			parent = _getParentAutoScrollElement(parent, false); | 
						|
		} | 
						|
 | 
						|
		return false; | 
						|
	} | 
						|
 | 
						|
	/** | 
						|
	 * Returns the scroll offset of the given element, added with all the scroll offsets of parent elements. | 
						|
	 * The value is returned in real pixels. | 
						|
	 * @param  {HTMLElement} el | 
						|
	 * @return {Array}             Offsets in the format of [left, top] | 
						|
	 */ | 
						|
	function _getRelativeScrollOffset(el) { | 
						|
		var offsetLeft = 0, | 
						|
			offsetTop = 0, | 
						|
			winScroller = _getWindowScrollingElement(); | 
						|
 | 
						|
		if (el) { | 
						|
			do { | 
						|
				var matrix = _matrix(el), | 
						|
					scaleX = matrix.a, | 
						|
					scaleY = matrix.d; | 
						|
 | 
						|
				offsetLeft += el.scrollLeft * scaleX; | 
						|
				offsetTop += el.scrollTop * scaleY; | 
						|
			} while (el !== winScroller && (el = el.parentNode)); | 
						|
		} | 
						|
 | 
						|
		return [offsetLeft, offsetTop]; | 
						|
	} | 
						|
 | 
						|
	// Fixed #973: | 
						|
	_on(document, 'touchmove', function(evt) { | 
						|
		if ((Sortable.active || awaitingDragStarted) && evt.cancelable) { | 
						|
			evt.preventDefault(); | 
						|
		} | 
						|
	}); | 
						|
 | 
						|
 | 
						|
	// Export utils | 
						|
	Sortable.utils = { | 
						|
		on: _on, | 
						|
		off: _off, | 
						|
		css: _css, | 
						|
		find: _find, | 
						|
		is: function (el, selector) { | 
						|
			return !!_closest(el, selector, el, false); | 
						|
		}, | 
						|
		extend: _extend, | 
						|
		throttle: _throttle, | 
						|
		closest: _closest, | 
						|
		toggleClass: _toggleClass, | 
						|
		clone: _clone, | 
						|
		index: _index, | 
						|
		nextTick: _nextTick, | 
						|
		cancelNextTick: _cancelNextTick, | 
						|
		detectDirection: _detectDirection, | 
						|
		getChild: _getChild | 
						|
	}; | 
						|
 | 
						|
 | 
						|
	/** | 
						|
	 * Create sortable instance | 
						|
	 * @param {HTMLElement}  el | 
						|
	 * @param {Object}      [options] | 
						|
	 */ | 
						|
	Sortable.create = function (el, options) { | 
						|
		return new Sortable(el, options); | 
						|
	}; | 
						|
 | 
						|
 | 
						|
	// Export | 
						|
	Sortable.version = '1.8.4'; | 
						|
	return Sortable; | 
						|
});
 | 
						|
 |