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.

1558 lines
36 KiB

11 years ago
/**!
* Sortable
* @author RubaXa <trash@rubaxa.org>
* @license MIT
*/
8 years ago
(function sortableModule(factory) {
11 years ago
"use strict";
10 years ago
if (typeof define === "function" && define.amd) {
11 years ago
define(factory);
11 years ago
}
10 years ago
else if (typeof module != "undefined" && typeof module.exports != "undefined") {
module.exports = factory();
}
11 years ago
else {
10 years ago
/* jshint sub:true */
11 years ago
window["Sortable"] = factory();
}
8 years ago
})(function sortableFactory() {
11 years ago
"use strict";
if (typeof window === "undefined" || !window.document) {
8 years ago
return function sortableError() {
9 years ago
throw new Error("Sortable.js requires a window with a document");
};
}
11 years ago
10 years ago
var dragEl,
parentEl,
10 years ago
ghostEl,
cloneEl,
rootEl,
nextEl,
lastDownEl,
11 years ago
scrollEl,
scrollParentEl,
scrollCustomFn,
10 years ago
lastEl,
lastCSS,
lastParentCSS,
11 years ago
10 years ago
oldIndex,
newIndex,
10 years ago
activeGroup,
putSortable,
autoScroll = {},
11 years ago
10 years ago
tapEvt,
touchEvt,
11 years ago
moved,
forRepaintDummy,
10 years ago
/** @const */
R_SPACE = /\s+/g,
R_FLOAT = /left|right|inline/,
10 years ago
10 years ago
expando = 'Sortable' + (new Date).getTime(),
11 years ago
10 years ago
win = window,
document = win.document,
parseInt = win.parseInt,
setTimeout = win.setTimeout,
10 years ago
9 years ago
$ = win.jQuery || win.Zepto,
Polymer = win.Polymer,
8 years ago
captureMode = false,
passiveMode = false,
supportDraggable = ('draggable' in document.createElement('div')),
supportCssPointerEvents = (function (el) {
// false when IE11
if (!!navigator.userAgent.match(/(?:Trident.*rv[ :]?11\.|msie)/i)) {
return false;
}
el = document.createElement('x');
el.style.cssText = 'pointer-events:auto';
return el.style.pointerEvents === 'auto';
})(),
10 years ago
_silent = false,
abs = Math.abs,
min = Math.min,
11 years ago
savedInputChecked = [],
touchDragOverListeners = [],
alwaysFalse = function () { return false; },
_autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl) {
// Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521
if (rootEl && options.scroll) {
var _this = rootEl[expando],
el,
rect,
sens = options.scrollSensitivity,
speed = options.scrollSpeed,
x = evt.clientX,
y = evt.clientY,
winWidth = window.innerWidth,
winHeight = window.innerHeight,
vx,
vy,
scrollOffsetX,
scrollOffsetY
;
// Delect scrollEl
if (scrollParentEl !== rootEl) {
scrollEl = options.scroll;
scrollParentEl = rootEl;
scrollCustomFn = options.scrollFn;
if (scrollEl === true) {
scrollEl = rootEl;
do {
if ((scrollEl.offsetWidth < scrollEl.scrollWidth) ||
(scrollEl.offsetHeight < scrollEl.scrollHeight)
) {
break;
}
/* jshint boss:true */
} while (scrollEl = scrollEl.parentNode);
}
}
if (scrollEl) {
el = scrollEl;
rect = scrollEl.getBoundingClientRect();
vx = (abs(rect.right - x) <= sens) - (abs(rect.left - x) <= sens);
vy = (abs(rect.bottom - y) <= sens) - (abs(rect.top - y) <= sens);
}
if (!(vx || vy)) {
vx = (winWidth - x <= sens) - (x <= sens);
vy = (winHeight - y <= sens) - (y <= sens);
/* jshint expr:true */
(vx || vy) && (el = win);
}
if (autoScroll.vx !== vx || autoScroll.vy !== vy || autoScroll.el !== el) {
autoScroll.el = el;
autoScroll.vx = vx;
autoScroll.vy = vy;
clearInterval(autoScroll.pid);
if (el) {
autoScroll.pid = setInterval(function () {
scrollOffsetY = vy ? vy * speed : 0;
scrollOffsetX = vx ? vx * speed : 0;
if ('function' === typeof(scrollCustomFn)) {
if (scrollCustomFn.call(_this, scrollOffsetX, scrollOffsetY, evt, touchEvt, el) !== 'continue') {
return;
}
}
if (el === win) {
win.scrollTo(win.pageXOffset + scrollOffsetX, win.pageYOffset + scrollOffsetY);
} else {
el.scrollTop += scrollOffsetY;
el.scrollLeft += scrollOffsetX;
}
}, 24);
}
}
}
}, 30),
_prepareGroup = function (options) {
function toFn(value, pull) {
if (value == null || value === true) {
value = group.name;
if (value == null) {
return alwaysFalse;
}
}
if (typeof value === 'function') {
return value;
} else {
return function (to, from) {
var fromGroup = from.options.group.name;
return pull
? value
: value && (value.join
? value.indexOf(fromGroup) > -1
: (fromGroup == value)
);
};
}
}
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;
}
;
11 years ago
// Detect support a passive mode
try {
window.addEventListener('test', null, Object.defineProperty({}, 'passive', {
get: function () {
7 years ago
// `false`, because everything starts to work incorrectly and instead of d'n'd,
// begins the page has scrolled.
passiveMode = false;
captureMode = {
capture: false,
passive: passiveMode
};
}
}));
} catch (err) {}
11 years ago
/**
* @class Sortable
* @param {HTMLElement} el
* @param {Object} [options]
11 years ago
*/
10 years ago
function Sortable(el, options) {
if (!(el && el.nodeType && el.nodeType === 1)) {
throw 'Sortable: `el` must be HTMLElement, and not ' + {}.toString.call(el);
}
11 years ago
this.el = el; // root element
this.options = options = _extend({}, options);
11 years ago
// 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,
draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*',
ghostClass: 'sortable-ghost',
chosenClass: 'sortable-chosen',
9 years ago
dragClass: 'sortable-drag',
ignore: 'a, img',
filter: null,
preventOnFilter: true,
animation: 0,
setData: function (dataTransfer, dragEl) {
dataTransfer.setData('Text', dragEl.textContent);
},
dropBubble: false,
dragoverBubble: false,
dataIdAttr: 'data-id',
delay: 0,
forceFallback: false,
fallbackClass: 'sortable-fallback',
fallbackOnBody: false,
fallbackTolerance: 0,
fallbackOffset: {x: 0, y: 0},
supportPointer: Sortable.supportPointer !== false
};
// Set default options
for (var name in defaults) {
!(name in options) && (options[name] = defaults[name]);
}
11 years ago
_prepareGroup(options);
11 years ago
11 years ago
// Bind all private methods
10 years ago
for (var fn in this) {
if (fn.charAt(0) === '_' && typeof this[fn] === 'function') {
this[fn] = this[fn].bind(this);
11 years ago
}
}
// Setup drag mode
this.nativeDraggable = options.forceFallback ? false : supportDraggable;
11 years ago
// Bind events
_on(el, 'mousedown', this._onTapStart);
_on(el, 'touchstart', this._onTapStart);
options.supportPointer && _on(el, 'pointerdown', this._onTapStart);
11 years ago
if (this.nativeDraggable) {
_on(el, 'dragover', this);
_on(el, 'dragenter', this);
}
11 years ago
touchDragOverListeners.push(this._onDragOver);
// Restore sorting
options.store && this.sort(options.store.get(this));
11 years ago
}
Sortable.prototype = /** @lends Sortable.prototype */ {
11 years ago
constructor: Sortable,
_onTapStart: function (/** Event|TouchEvent */evt) {
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 = target,
filter = options.filter,
startIndex;
_saveInputCheckedState(el);
// Don't trigger start event when an element is been dragged, otherwise the evt.oldindex always wrong when set option.group.
9 years ago
if (dragEl) {
return;
}
if (/mousedown|pointerdown/.test(type) && evt.button !== 0 || options.disabled) {
return; // only left button or enabled
}
11 years ago
// cancel dnd if original target is content editable
if (originalTarget.isContentEditable) {
return;
}
target = _closest(target, options.draggable, el);
if (!target) {
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.preventDefault();
return; // cancel dnd
}
}
else if (filter) {
filter = filter.split(',').some(function (criteria) {
criteria = _closest(originalTarget, criteria.trim(), el);
if (criteria) {
_dispatchEvent(_this, criteria, 'filter', target, el, el, startIndex);
return true;
}
});
if (filter) {
preventOnFilter && evt.preventDefault();
return; // cancel dnd
}
}
if (options.handle && !_closest(originalTarget, options.handle, el)) {
return;
}
// Prepare `dragstart`
this._prepareDragStart(evt, touch, target, startIndex);
},
_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)) {
tapEvt = evt;
rootEl = el;
dragEl = target;
parentEl = dragEl.parentNode;
nextEl = dragEl.nextSibling;
lastDownEl = target;
activeGroup = options.group;
oldIndex = startIndex;
this._lastX = (touch || evt).clientX;
this._lastY = (touch || evt).clientY;
dragEl.style['will-change'] = 'all';
dragStartFn = function () {
// Delayed drag has been triggered
// we can re-enable the events: touchmove/mousemove
_this._disableDelayedDrag();
// Make the element draggable
dragEl.draggable = _this.nativeDraggable;
// Chosen item
9 years ago
_toggleClass(dragEl, options.chosenClass, true);
// Bind the events: dragstart/dragend
8 years ago
_this._triggerDragStart(evt, touch);
// Drag start event
_dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, rootEl, oldIndex);
};
// Disable "draggable"
options.ignore.split(',').forEach(function (criteria) {
_find(dragEl, criteria.trim(), _disableDraggable);
});
_on(ownerDocument, 'mouseup', _this._onDrop);
_on(ownerDocument, 'touchend', _this._onDrop);
_on(ownerDocument, 'touchcancel', _this._onDrop);
_on(ownerDocument, 'selectstart', _this);
options.supportPointer && _on(ownerDocument, 'pointercancel', _this._onDrop);
if (options.delay) {
// 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._disableDelayedDrag);
_on(ownerDocument, 'touchmove', _this._disableDelayedDrag);
options.supportPointer && _on(ownerDocument, 'pointermove', _this._disableDelayedDrag);
_this._dragStartTimer = setTimeout(dragStartFn, options.delay);
} else {
dragStartFn();
}
8 years ago
}
},
11 years ago
_disableDelayedDrag: function () {
var ownerDocument = this.el.ownerDocument;
clearTimeout(this._dragStartTimer);
_off(ownerDocument, 'mouseup', this._disableDelayedDrag);
_off(ownerDocument, 'touchend', this._disableDelayedDrag);
_off(ownerDocument, 'touchcancel', this._disableDelayedDrag);
_off(ownerDocument, 'mousemove', this._disableDelayedDrag);
_off(ownerDocument, 'touchmove', this._disableDelayedDrag);
_off(ownerDocument, 'pointermove', this._disableDelayedDrag);
},
_triggerDragStart: function (/** Event */evt, /** Touch */touch) {
touch = touch || (evt.pointerType == 'touch' ? evt : null);
if (touch) {
// Touch device support
tapEvt = {
target: dragEl,
clientX: touch.clientX,
clientY: touch.clientY
};
this._onDragStart(tapEvt, 'touch');
}
else if (!this.nativeDraggable) {
this._onDragStart(tapEvt, true);
}
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 () {
if (rootEl && dragEl) {
9 years ago
var options = this.options;
// Apply effect
9 years ago
_toggleClass(dragEl, options.ghostClass, true);
_toggleClass(dragEl, options.dragClass, false);
Sortable.active = this;
// Drag start event
_dispatchEvent(this, rootEl, 'start', dragEl, rootEl, rootEl, oldIndex);
8 years ago
} else {
this._nulling();
}
11 years ago
},
10 years ago
_emulateDragOver: function () {
if (touchEvt) {
if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY) {
return;
}
this._lastX = touchEvt.clientX;
this._lastY = touchEvt.clientY;
if (!supportCssPointerEvents) {
_css(ghostEl, 'display', 'none');
}
11 years ago
var shadowRootOrDocument = document;
// Check for Shadow DOM
for (var node = this.el; node; node = node.parentNode) {
if (node.toString() === "[object ShadowRoot]") {
shadowRootOrDocument = node;
}
}
var target = shadowRootOrDocument.elementFromPoint(touchEvt.clientX, touchEvt.clientY),
parent = target,
i = touchDragOverListeners.length;
if (parent) {
do {
if (parent[expando]) {
while (i--) {
touchDragOverListeners[i]({
clientX: touchEvt.clientX,
clientY: touchEvt.clientY,
target: target,
rootEl: parent
});
}
break;
}
target = parent; // store last element
11 years ago
}
/* jshint boss:true */
while (parent = parent.parentNode);
11 years ago
}
if (!supportCssPointerEvents) {
_css(ghostEl, 'display', '');
}
11 years ago
}
},
10 years ago
_onTouchMove: function (/**TouchEvent*/evt) {
if (tapEvt) {
var options = this.options,
fallbackTolerance = options.fallbackTolerance,
fallbackOffset = options.fallbackOffset,
touch = evt.touches ? evt.touches[0] : evt,
dx = (touch.clientX - tapEvt.clientX) + fallbackOffset.x,
dy = (touch.clientY - tapEvt.clientY) + fallbackOffset.y,
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
9 years ago
if (!Sortable.active) {
if (fallbackTolerance &&
min(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY)) < fallbackTolerance
) {
return;
}
this._dragStarted();
}
9 years ago
// as well as creating the ghost element on the document body
this._appendGhost();
9 years ago
moved = true;
11 years ago
touchEvt = touch;
10 years ago
_css(ghostEl, 'webkitTransform', translate3d);
_css(ghostEl, 'mozTransform', translate3d);
_css(ghostEl, 'msTransform', translate3d);
_css(ghostEl, 'transform', translate3d);
11 years ago
evt.preventDefault();
11 years ago
}
},
9 years ago
_appendGhost: function () {
if (!ghostEl) {
10 years ago
var rect = dragEl.getBoundingClientRect(),
css = _css(dragEl),
9 years ago
options = this.options,
10 years ago
ghostRect;
11 years ago
ghostEl = dragEl.cloneNode(true);
11 years ago
9 years ago
_toggleClass(ghostEl, options.ghostClass, false);
_toggleClass(ghostEl, options.fallbackClass, true);
9 years ago
_toggleClass(ghostEl, options.dragClass, true);
11 years ago
_css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10));
_css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10));
11 years ago
_css(ghostEl, 'width', rect.width);
_css(ghostEl, 'height', rect.height);
11 years ago
_css(ghostEl, 'opacity', '0.8');
_css(ghostEl, 'position', 'fixed');
_css(ghostEl, 'zIndex', '100000');
_css(ghostEl, 'pointerEvents', 'none');
11 years ago
9 years ago
options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
11 years ago
// Fixing dimensions.
ghostRect = ghostEl.getBoundingClientRect();
10 years ago
_css(ghostEl, 'width', rect.width * 2 - ghostRect.width);
_css(ghostEl, 'height', rect.height * 2 - ghostRect.height);
}
},
_onDragStart: function (/**Event*/evt, /**boolean*/useFallback) {
var _this = this;
var dataTransfer = evt.dataTransfer;
var options = _this.options;
_this._offUpEvents();
if (activeGroup.checkPull(_this, _this, dragEl, evt)) {
cloneEl = _clone(dragEl);
cloneEl.draggable = false;
cloneEl.style['will-change'] = '';
_css(cloneEl, 'display', 'none');
_toggleClass(cloneEl, _this.options.chosenClass, false);
// #1143: IFrame support workaround
_this._cloneId = _nextTick(function () {
rootEl.insertBefore(cloneEl, dragEl);
_dispatchEvent(_this, rootEl, 'clone', dragEl);
});
}
9 years ago
_toggleClass(dragEl, options.dragClass, true);
if (useFallback) {
10 years ago
if (useFallback === 'touch') {
// Bind touch events
_on(document, 'touchmove', _this._onTouchMove);
_on(document, 'touchend', _this._onDrop);
_on(document, 'touchcancel', _this._onDrop);
if (options.supportPointer) {
_on(document, 'pointermove', _this._onTouchMove);
_on(document, 'pointerup', _this._onDrop);
}
10 years ago
} else {
// Old brwoser
_on(document, 'mousemove', _this._onTouchMove);
_on(document, 'mouseup', _this._onDrop);
10 years ago
}
11 years ago
_this._loopId = setInterval(_this._emulateDragOver, 50);
11 years ago
}
else {
if (dataTransfer) {
dataTransfer.effectAllowed = 'move';
options.setData && options.setData.call(_this, dataTransfer, dragEl);
}
11 years ago
_on(document, 'drop', _this);
7 years ago
// #1143: Бывает элемент с IFrame внутри блокирует `drop`,
7 years ago
// поэтому если вызвался `mouseover`, значит надо отменять весь d'n'd.
// Breaking Chrome 62+
// _on(document, 'mouseover', _this);
7 years ago
_this._dragStartId = _nextTick(_this._dragStarted);
11 years ago
}
},
10 years ago
_onDragOver: function (/**Event*/evt) {
var el = this.el,
target,
dragRect,
8 years ago
targetRect,
revert,
options = this.options,
group = options.group,
activeSortable = Sortable.active,
isOwner = (activeGroup === group),
isMovingBetweenSortable = false,
canSort = options.sort;
if (evt.preventDefault !== void 0) {
7 years ago
evt.preventDefault();
!options.dragoverBubble && evt.stopPropagation();
}
if (dragEl.animated) {
return;
}
moved = true;
8 years ago
if (activeSortable && !options.disabled &&
(isOwner
? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list
: (
putSortable === this ||
(
(activeSortable.lastPullMode = activeGroup.checkPull(this, activeSortable, dragEl, evt)) &&
group.checkPut(this, activeSortable, dragEl, evt)
)
)
) &&
(evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback
10 years ago
) {
10 years ago
// Smart auto-scrolling
_autoScroll(evt, options, this.el);
if (_silent) {
return;
}
target = _closest(evt.target, options.draggable, el);
dragRect = dragEl.getBoundingClientRect();
if (putSortable !== this) {
8 years ago
putSortable = this;
isMovingBetweenSortable = true;
}
if (revert) {
_cloneHide(activeSortable, true);
parentEl = rootEl; // actualization
if (cloneEl || nextEl) {
rootEl.insertBefore(dragEl, cloneEl || nextEl);
}
else if (!canSort) {
rootEl.appendChild(dragEl);
}
return;
}
11 years ago
10 years ago
if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
(el === evt.target) && (_ghostIsLast(el, evt))
10 years ago
) {
//assign target only if condition is true
if (el.children.length !== 0 && el.children[0] !== ghostEl && el === evt.target) {
target = el.lastElementChild;
}
if (target) {
if (target.animated) {
return;
}
targetRect = target.getBoundingClientRect();
}
11 years ago
_cloneHide(activeSortable, isOwner);
if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt) !== false) {
if (!dragEl.contains(el)) {
el.appendChild(dragEl);
parentEl = el; // actualization
}
this._animate(dragRect, dragEl);
target && this._animate(targetRect, target);
}
}
10 years ago
else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) {
if (lastEl !== target) {
lastEl = target;
lastCSS = _css(target);
lastParentCSS = _css(target.parentNode);
11 years ago
}
8 years ago
targetRect = target.getBoundingClientRect();
11 years ago
8 years ago
var width = targetRect.right - targetRect.left,
10 years ago
height = targetRect.bottom - targetRect.top,
floating = R_FLOAT.test(lastCSS.cssFloat + lastCSS.display)
|| (lastParentCSS.display == 'flex' && lastParentCSS['flex-direction'].indexOf('row') === 0),
10 years ago
isWide = (target.offsetWidth > dragEl.offsetWidth),
isLong = (target.offsetHeight > dragEl.offsetHeight),
halfway = (floating ? (evt.clientX - targetRect.left) / width : (evt.clientY - targetRect.top) / height) > 0.5,
nextSibling = target.nextElementSibling,
after = false
;
if (floating) {
var elTop = dragEl.offsetTop,
tgTop = target.offsetTop;
if (elTop === tgTop) {
after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide;
}
else if (target.previousElementSibling === dragEl || dragEl.previousElementSibling === target) {
after = (evt.clientY - targetRect.top) / height > 0.5;
} else {
after = tgTop > elTop;
}
} else if (!isMovingBetweenSortable) {
after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
}
var moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, after);
if (moveVector !== false) {
if (moveVector === 1 || moveVector === -1) {
after = (moveVector === 1);
}
9 years ago
_silent = true;
setTimeout(_unsilent, 30);
_cloneHide(activeSortable, isOwner);
if (!dragEl.contains(el)) {
if (after && !nextSibling) {
el.appendChild(dragEl);
} else {
target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
}
}
parentEl = dragEl.parentNode; // actualization
this._animate(dragRect, dragEl);
this._animate(targetRect, target);
}
11 years ago
}
}
},
_animate: function (prevRect, target) {
var ms = this.options.animation;
if (ms) {
var currentRect = target.getBoundingClientRect();
if (prevRect.nodeType === 1) {
prevRect = prevRect.getBoundingClientRect();
}
_css(target, 'transition', 'none');
_css(target, 'transform', 'translate3d('
+ (prevRect.left - currentRect.left) + 'px,'
+ (prevRect.top - currentRect.top) + 'px,0)'
);
forRepaintDummy = target.offsetWidth; // repaint
_css(target, 'transition', 'all ' + ms + 'ms');
_css(target, 'transform', 'translate3d(0,0,0)');
10 years ago
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(ownerDocument, 'pointercancel', this._onDrop);
_off(ownerDocument, 'selectstart', this);
},
11 years ago
10 years ago
_onDrop: function (/**Event*/evt) {
var el = this.el,
options = this.options;
11 years ago
clearInterval(this._loopId);
clearInterval(autoScroll.pid);
clearTimeout(this._dragStartTimer);
_cancelNextTick(this._cloneId);
_cancelNextTick(this._dragStartId);
11 years ago
// Unbind events
7 years ago
_off(document, 'mouseover', this);
_off(document, 'mousemove', this._onTouchMove);
if (this.nativeDraggable) {
_off(document, 'drop', this);
_off(el, 'dragstart', this._onDragStart);
}
11 years ago
this._offUpEvents();
11 years ago
10 years ago
if (evt) {
9 years ago
if (moved) {
7 years ago
evt.preventDefault();
!options.dropBubble && evt.stopPropagation();
}
11 years ago
ghostEl && ghostEl.parentNode && ghostEl.parentNode.removeChild(ghostEl);
11 years ago
if (rootEl === parentEl || Sortable.active.lastPullMode !== 'clone') {
// Remove clone
cloneEl && cloneEl.parentNode && cloneEl.parentNode.removeChild(cloneEl);
}
10 years ago
if (dragEl) {
if (this.nativeDraggable) {
_off(dragEl, 'dragend', this);
}
_disableDraggable(dragEl);
dragEl.style['will-change'] = '';
// Remove class's
11 years ago
_toggleClass(dragEl, this.options.ghostClass, false);
_toggleClass(dragEl, this.options.chosenClass, false);
11 years ago
// 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);
}
11 years ago
}
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);
}
}
11 years ago
}
if (Sortable.active) {
/* jshint eqnull:true */
9 years ago
if (newIndex == null || newIndex === -1) {
newIndex = oldIndex;
}
_dispatchEvent(this, rootEl, 'end', dragEl, parentEl, rootEl, oldIndex, newIndex, evt);
// Save sorting
this.save();
}
11 years ago
}
}
this._nulling();
},
11 years ago
_nulling: function() {
8 years ago
rootEl =
dragEl =
parentEl =
ghostEl =
nextEl =
cloneEl =
lastDownEl =
11 years ago
scrollEl =
scrollParentEl =
tapEvt =
touchEvt =
11 years ago
moved =
newIndex =
11 years ago
lastEl =
lastCSS =
putSortable =
activeGroup =
Sortable.active = null;
savedInputChecked.forEach(function (el) {
el.checked = true;
});
savedInputChecked.length = 0;
},
11 years ago
handleEvent: function (/**Event*/evt) {
switch (evt.type) {
case 'drop':
case 'dragend':
this._onDrop(evt);
break;
case 'dragover':
case 'dragenter':
8 years ago
if (dragEl) {
this._onDragOver(evt);
_globalDragOver(evt);
}
break;
7 years ago
case 'mouseover':
this._onDrop(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)) {
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)) {
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(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);
},
/**
* 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;
11 years ago
el[expando] = null;
11 years ago
_off(el, 'mousedown', this._onTapStart);
_off(el, 'touchstart', this._onTapStart);
_off(el, 'pointerdown', this._onTapStart);
11 years ago
if (this.nativeDraggable) {
_off(el, 'dragover', this);
_off(el, 'dragenter', this);
}
11 years ago
// Remove draggable attributes
10 years ago
Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) {
el.removeAttribute('draggable');
});
11 years ago
touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1);
this._onDrop();
this.el = el = null;
11 years ago
}
};
function _cloneHide(sortable, state) {
if (sortable.lastPullMode !== 'clone') {
state = true;
}
if (cloneEl && (cloneEl.state !== state)) {
_css(cloneEl, 'display', state ? 'none' : '');
if (!state) {
if (cloneEl.state) {
if (sortable.options.group.revertClone) {
rootEl.insertBefore(cloneEl, nextEl);
sortable._animate(dragEl, cloneEl);
} else {
rootEl.insertBefore(cloneEl, dragEl);
}
}
}
cloneEl.state = state;
}
}
function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) {
if (el) {
11 years ago
ctx = ctx || document;
do {
if ((selector === '>*' && el.parentNode === ctx) || _matches(el, selector)) {
10 years ago
return el;
11 years ago
}
8 years ago
/* jshint boss:true */
} while (el = _getParentOrHost(el));
11 years ago
}
10 years ago
return null;
11 years ago
}
function _getParentOrHost(el) {
var parent = el.host;
return (parent && parent.nodeType) ? parent : el.parentNode;
}
function _globalDragOver(/**Event*/evt) {
if (evt.dataTransfer) {
evt.dataTransfer.dropEffect = 'move';
}
7 years ago
evt.preventDefault();
11 years ago
}
10 years ago
function _on(el, event, fn) {
el.addEventListener(event, fn, captureMode);
11 years ago
}
10 years ago
function _off(el, event, fn) {
el.removeEventListener(event, fn, captureMode);
11 years ago
}
10 years ago
function _toggleClass(el, name, state) {
if (el) {
if (el.classList) {
11 years ago
el.classList[state ? 'add' : 'remove'](name);
}
else {
var className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' ');
el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' ');
11 years ago
}
}
}
10 years ago
function _css(el, prop, val) {
var style = el && el.style;
10 years ago
if (style) {
if (val === void 0) {
if (document.defaultView && document.defaultView.getComputedStyle) {
11 years ago
val = document.defaultView.getComputedStyle(el, '');
}
10 years ago
else if (el.currentStyle) {
val = el.currentStyle;
11 years ago
}
return prop === void 0 ? val : val[prop];
}
else {
if (!(prop in style)) {
prop = '-webkit-' + prop;
}
style[prop] = val + (typeof val === 'string' ? '' : 'px');
11 years ago
}
}
}
10 years ago
function _find(ctx, tagName, iterator) {
if (ctx) {
11 years ago
var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length;
10 years ago
if (iterator) {
for (; i < n; i++) {
11 years ago
iterator(list[i], i);
}
}
10 years ago
return list;
11 years ago
}
10 years ago
return [];
11 years ago
}
function _dispatchEvent(sortable, rootEl, name, targetEl, toEl, fromEl, startIndex, newIndex, originalEvt) {
sortable = (sortable || rootEl[expando]);
var evt = document.createEvent('Event'),
options = sortable.options,
onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1);
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;
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;
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 || toEl.getBoundingClientRect();
evt.willInsertAfter = willInsertAfter;
evt.originalEvent = originalEvt;
fromEl.dispatchEvent(evt);
if (onMoveFn) {
retVal = onMoveFn.call(sortable, evt, originalEvt);
}
return retVal;
}
10 years ago
function _disableDraggable(el) {
el.draggable = false;
11 years ago
}
10 years ago
function _unsilent() {
_silent = false;
}
/** @returns {HTMLElement|false} */
function _ghostIsLast(el, evt) {
var lastEl = el.lastElementChild,
rect = lastEl.getBoundingClientRect();
// 5 — min delta
9 years ago
// abs — нельзя добавлять, а то глюки при наведении сверху
return (evt.clientY - (rect.top + rect.height) > 5) ||
(evt.clientX - (rect.left + rect.width) > 5);
}
/**
* 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,
10 years ago
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') && (selector === '>*' || _matches(el, selector))) {
index++;
}
}
return index;
}
11 years ago
function _matches(/**HTMLElement*/el, /**String*/selector) {
if (el) {
try {
if (el.matches) {
return el.matches(selector);
} else if (el.msMatchesSelector) {
return el.msMatchesSelector(selector);
}
} catch(_) {
return false;
}
}
return false;
}
function _throttle(callback, ms) {
var args, _this;
return function () {
if (args === void 0) {
args = arguments;
_this = this;
setTimeout(function () {
if (args.length === 1) {
callback.call(_this, args[0]);
} else {
callback.apply(_this, args);
}
args = void 0;
}, ms);
}
};
}
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) {
8 years ago
return Polymer.dom(el).cloneNode(true);
}
else if ($) {
8 years ago
return $(el).clone(true)[0];
}
else {
8 years ago
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);
}
// Fixed #973:
_on(document, 'touchmove', function (evt) {
if (Sortable.active) {
evt.preventDefault();
}
});
11 years ago
// Export utils
Sortable.utils = {
on: _on,
off: _off,
css: _css,
find: _find,
is: function (el, selector) {
return !!_closest(el, selector, el);
},
extend: _extend,
throttle: _throttle,
11 years ago
closest: _closest,
toggleClass: _toggleClass,
clone: _clone,
index: _index,
nextTick: _nextTick,
cancelNextTick: _cancelNextTick
11 years ago
};
/**
* Create sortable instance
* @param {HTMLElement} el
* @param {Object} [options]
*/
Sortable.create = function (el, options) {
10 years ago
return new Sortable(el, options);
};
11 years ago
// Export
Sortable.version = '1.7.0';
return Sortable;
11 years ago
});