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.

1145 lines
25 KiB

11 years ago
/**!
* Sortable
* @author RubaXa <trash@rubaxa.org>
* @license MIT
*/
10 years ago
(function (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();
}
10 years ago
else if (typeof Package !== "undefined") {
Sortable = factory(); // export for Meteor.js
}
11 years ago
else {
10 years ago
/* jshint sub:true */
11 years ago
window["Sortable"] = factory();
}
10 years ago
})(function () {
11 years ago
"use strict";
10 years ago
var dragEl,
ghostEl,
cloneEl,
rootEl,
nextEl,
11 years ago
scrollEl,
scrollParentEl,
10 years ago
lastEl,
lastCSS,
11 years ago
10 years ago
oldIndex,
newIndex,
10 years ago
activeGroup,
autoScroll = {},
11 years ago
10 years ago
tapEvt,
touchEvt,
11 years ago
10 years ago
/** @const */
RSPACE = /\s+/g,
10 years ago
expando = 'Sortable' + (new Date).getTime(),
11 years ago
10 years ago
win = window,
document = win.document,
parseInt = win.parseInt,
10 years ago
supportDraggable = !!('draggable' in document.createElement('div')),
10 years ago
_silent = false,
abs = Math.abs,
10 years ago
slice = [].slice,
11 years ago
touchDragOverListeners = [],
_autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl) {
// Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521
if (rootEl && options.scroll) {
var el,
rect,
sens = options.scrollSensitivity,
speed = options.scrollSpeed,
x = evt.clientX,
y = evt.clientY,
winWidth = window.innerWidth,
winHeight = window.innerHeight,
vx,
vy
;
// Delect scrollEl
if (scrollParentEl !== rootEl) {
scrollEl = options.scroll;
scrollParentEl = rootEl;
if (scrollEl === true) {
scrollEl = rootEl;
do {
if ((scrollEl.offsetWidth < scrollEl.scrollWidth) ||
(scrollEl.offsetHeight < scrollEl.scrollHeight)
) {
break;
}
/* jshint boss:true */
} while (scrollEl = scrollEl.parentNode);
}
}
if (scrollEl) {
el = scrollEl;
rect = scrollEl.getBoundingClientRect();
vx = (abs(rect.right - x) <= sens) - (abs(rect.left - x) <= sens);
vy = (abs(rect.bottom - y) <= sens) - (abs(rect.top - y) <= sens);
}
if (!(vx || vy)) {
vx = (winWidth - x <= sens) - (x <= sens);
vy = (winHeight - y <= sens) - (y <= sens);
/* jshint expr:true */
(vx || vy) && (el = win);
}
if (autoScroll.vx !== vx || autoScroll.vy !== vy || autoScroll.el !== el) {
autoScroll.el = el;
autoScroll.vx = vx;
autoScroll.vy = vy;
clearInterval(autoScroll.pid);
if (el) {
autoScroll.pid = setInterval(function () {
if (el === win) {
win.scrollTo(win.pageXOffset + vx * speed, win.pageYOffset + vy * speed);
} else {
vy && (el.scrollTop += vy * speed);
vx && (el.scrollLeft += vx * speed);
}
}, 24);
}
}
}
}, 30)
11 years ago
;
11 years ago
/**
* @class Sortable
* @param {HTMLElement} el
* @param {Object} [options]
11 years ago
*/
10 years ago
function Sortable(el, options) {
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: Math.random(),
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',
ignore: 'a, img',
filter: null,
animation: 0,
setData: function (dataTransfer, dragEl) {
dataTransfer.setData('Text', dragEl.textContent);
},
dropBubble: false,
dragoverBubble: false,
dataIdAttr: 'data-id',
delay: 0
};
// Set default options
for (var name in defaults) {
!(name in options) && (options[name] = defaults[name]);
}
11 years ago
var group = options.group;
if (!group || typeof group != 'object') {
group = options.group = { name: group };
}
['pull', 'put'].forEach(function (key) {
if (!(key in group)) {
group[key] = true;
}
});
options.groups = ' ' + group.name + (group.put.join ? ' ' + group.put.join(' ') : '') + ' ';
11 years ago
11 years ago
// Bind all private methods
10 years ago
for (var fn in this) {
if (fn.charAt(0) === '_') {
11 years ago
this[fn] = _bind(this, this[fn]);
}
}
// Bind events
_on(el, 'mousedown', this._onTapStart);
_on(el, 'touchstart', this._onTapStart);
_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,
type = evt.type,
touch = evt.touches && evt.touches[0],
target = (touch || evt).target,
originalTarget = target,
filter = options.filter;
if (type === 'mousedown' && evt.button !== 0 || options.disabled) {
return; // only left button or enabled
}
11 years ago
target = _closest(target, options.draggable, el);
if (!target) {
return;
}
// get the index of the dragged element within its parent
oldIndex = _index(target);
// Check filter
if (typeof filter === 'function') {
if (filter.call(this, evt, target, this)) {
_dispatchEvent(_this, originalTarget, 'filter', target, el, oldIndex);
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, oldIndex);
return true;
}
});
if (filter) {
evt.preventDefault();
return; // cancel dnd
}
}
if (options.handle && !_closest(originalTarget, options.handle, el)) {
return;
}
// Prepare `dragstart`
this._prepareDragStart(evt, touch, target);
},
_prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target) {
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;
nextEl = dragEl.nextSibling;
activeGroup = options.group;
dragStartFn = function () {
// Delayed drag has been triggered
// we can re-enable the events: touchmove/mousemove
_this._disableDelayedDrag();
// Make the element draggable
dragEl.draggable = true;
// Disable "draggable"
options.ignore.split(',').forEach(function (criteria) {
_find(dragEl, criteria.trim(), _disableDraggable);
});
// Bind the events: dragstart/dragend
_this._triggerDragStart(touch);
};
_on(ownerDocument, 'mouseup', _this._onDrop);
_on(ownerDocument, 'touchend', _this._onDrop);
_on(ownerDocument, 'touchcancel', _this._onDrop);
if (options.delay) {
// If the user moves the pointer before the delay has been reached:
// disable the delayed drag
_on(ownerDocument, 'mousemove', _this._disableDelayedDrag);
_on(ownerDocument, 'touchmove', _this._disableDelayedDrag);
_this._dragStartTimer = setTimeout(dragStartFn, options.delay);
} else {
dragStartFn();
}
}
},
11 years ago
_disableDelayedDrag: function () {
var ownerDocument = this.el.ownerDocument;
clearTimeout(this._dragStartTimer);
11 years ago
_off(ownerDocument, 'mousemove', this._disableDelayedDrag);
_off(ownerDocument, 'touchmove', this._disableDelayedDrag);
},
_triggerDragStart: function (/** Touch */touch) {
if (touch) {
// Touch device support
tapEvt = {
target: dragEl,
clientX: touch.clientX,
clientY: touch.clientY
};
this._onDragStart(tapEvt, 'touch');
}
else if (!supportDraggable) {
this._onDragStart(tapEvt, true);
}
else {
_on(dragEl, 'dragend', this);
_on(rootEl, 'dragstart', this._onDragStart);
}
try {
if (document.selection) {
document.selection.empty();
} else {
window.getSelection().removeAllRanges();
}
} catch (err) {
}
},
_dragStarted: function () {
if (rootEl && dragEl) {
// Apply effect
_toggleClass(dragEl, this.options.ghostClass, true);
Sortable.active = this;
// Drag start event
_dispatchEvent(this, rootEl, 'start', dragEl, rootEl, oldIndex);
}
11 years ago
},
10 years ago
_emulateDragOver: function () {
if (touchEvt) {
11 years ago
_css(ghostEl, 'display', 'none');
10 years ago
var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY),
parent = target,
groupName = ' ' + this.options.group.name + '',
10 years ago
i = touchDragOverListeners.length;
11 years ago
if (parent) {
do {
if (parent[expando] && parent[expando].options.groups.indexOf(groupName) > -1) {
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
}
_css(ghostEl, 'display', '');
}
},
10 years ago
_onTouchMove: function (/**TouchEvent*/evt) {
if (tapEvt) {
10 years ago
var touch = evt.touches ? evt.touches[0] : evt,
10 years ago
dx = touch.clientX - tapEvt.clientX,
dy = touch.clientY - tapEvt.clientY,
10 years ago
translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';
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
}
},
10 years ago
_onDragStart: function (/**Event*/evt, /**boolean*/useFallback) {
var dataTransfer = evt.dataTransfer,
options = this.options;
11 years ago
this._offUpEvents();
11 years ago
if (activeGroup.pull == 'clone') {
cloneEl = dragEl.cloneNode(true);
_css(cloneEl, 'display', 'none');
rootEl.insertBefore(cloneEl, dragEl);
}
10 years ago
if (useFallback) {
10 years ago
var rect = dragEl.getBoundingClientRect(),
css = _css(dragEl),
ghostRect;
11 years ago
ghostEl = dragEl.cloneNode(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');
11 years ago
rootEl.appendChild(ghostEl);
// Fixing dimensions.
ghostRect = ghostEl.getBoundingClientRect();
10 years ago
_css(ghostEl, 'width', rect.width * 2 - ghostRect.width);
_css(ghostEl, 'height', rect.height * 2 - ghostRect.height);
11 years ago
10 years ago
if (useFallback === 'touch') {
// Bind touch events
_on(document, 'touchmove', this._onTouchMove);
_on(document, 'touchend', this._onDrop);
_on(document, 'touchcancel', this._onDrop);
} else {
// Old brwoser
_on(document, 'mousemove', this._onTouchMove);
_on(document, 'mouseup', this._onDrop);
}
11 years ago
this._loopId = setInterval(this._emulateDragOver, 150);
11 years ago
}
else {
if (dataTransfer) {
dataTransfer.effectAllowed = 'move';
options.setData && options.setData.call(this, dataTransfer, dragEl);
}
11 years ago
_on(document, 'drop', this);
11 years ago
}
setTimeout(this._dragStarted, 0);
11 years ago
},
10 years ago
_onDragOver: function (/**Event*/evt) {
var el = this.el,
target,
dragRect,
revert,
options = this.options,
group = options.group,
groupPut = group.put,
isOwner = (activeGroup === group),
canSort = options.sort;
if (evt.preventDefault !== void 0) {
evt.preventDefault();
!options.dragoverBubble && evt.stopPropagation();
}
10 years ago
if (activeGroup && !options.disabled &&
(isOwner
? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list
: activeGroup.pull && groupPut && (
(activeGroup.name === group.name) || // by Name
(groupPut.indexOf && ~groupPut.indexOf(activeGroup.name)) // by Array
)
) &&
(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 (revert) {
_cloneHide(true);
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) && (target = _ghostInBottom(el, evt))
10 years ago
) {
if (target) {
if (target.animated) {
return;
}
targetRect = target.getBoundingClientRect();
}
11 years ago
_cloneHide(isOwner);
if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect) !== false) {
el.appendChild(dragEl);
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);
11 years ago
}
10 years ago
var targetRect = target.getBoundingClientRect(),
width = targetRect.right - targetRect.left,
height = targetRect.bottom - targetRect.top,
floating = /left|right|inline/.test(lastCSS.cssFloat + lastCSS.display),
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,
moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect),
10 years ago
after
;
if (moveVector !== false) {
_silent = true;
setTimeout(_unsilent, 30);
_cloneHide(isOwner);
if (moveVector === 1 || moveVector === -1) {
after = (moveVector === 1);
}
else if (floating) {
after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide;
} else {
after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
}
if (after && !nextSibling) {
el.appendChild(dragEl);
} else {
target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
}
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();
_css(target, 'transition', 'none');
_css(target, 'transform', 'translate3d('
+ (prevRect.left - currentRect.left) + 'px,'
+ (prevRect.top - currentRect.top) + 'px,0)'
);
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(ownerDocument, 'mouseup', this._onDrop);
_off(ownerDocument, 'touchend', this._onDrop);
_off(ownerDocument, 'touchcancel', this._onDrop);
},
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);
11 years ago
// Unbind events
_off(document, 'drop', this);
_off(document, 'mousemove', this._onTouchMove);
_off(el, 'dragstart', this._onDragStart);
11 years ago
this._offUpEvents();
11 years ago
10 years ago
if (evt) {
11 years ago
evt.preventDefault();
!options.dropBubble && evt.stopPropagation();
11 years ago
ghostEl && ghostEl.parentNode.removeChild(ghostEl);
11 years ago
10 years ago
if (dragEl) {
_off(dragEl, 'dragend', this);
_disableDraggable(dragEl);
11 years ago
_toggleClass(dragEl, this.options.ghostClass, false);
if (rootEl !== dragEl.parentNode) {
10 years ago
newIndex = _index(dragEl);
// drag from one list and drop into another
_dispatchEvent(null, dragEl.parentNode, 'sort', dragEl, rootEl, oldIndex, newIndex);
_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
11 years ago
// Add event
_dispatchEvent(null, dragEl.parentNode, 'add', dragEl, rootEl, oldIndex, newIndex);
// Remove event
_dispatchEvent(this, rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex);
11 years ago
}
else {
// Remove clone
cloneEl && cloneEl.parentNode.removeChild(cloneEl);
10 years ago
if (dragEl.nextSibling !== nextEl) {
// Get the index of the dragged element within its parent
newIndex = _index(dragEl);
10 years ago
// drag & drop within the same list
_dispatchEvent(this, rootEl, 'update', dragEl, rootEl, oldIndex, newIndex);
_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
}
11 years ago
}
if (Sortable.active) {
// Drag end event
_dispatchEvent(this, rootEl, 'end', dragEl, rootEl, oldIndex, newIndex);
// Save sorting
this.save();
}
11 years ago
}
// Nulling
11 years ago
rootEl =
dragEl =
ghostEl =
nextEl =
cloneEl =
11 years ago
scrollEl =
scrollParentEl =
11 years ago
tapEvt =
touchEvt =
lastEl =
lastCSS =
activeGroup =
Sortable.active = null;
11 years ago
}
},
handleEvent: function (/**Event*/evt) {
var type = evt.type;
if (type === 'dragover' || type === 'dragenter') {
if (dragEl) {
this._onDragOver(evt);
_globalDragOver(evt);
}
}
else if (type === 'drop' || type === 'dragend') {
this._onDrop(evt);
}
},
/**
* 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;
}
},
/**
* 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, '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(state) {
if (cloneEl && (cloneEl.state !== state)) {
_css(cloneEl, 'display', state ? 'none' : '');
!state && cloneEl.state && rootEl.insertBefore(cloneEl, dragEl);
cloneEl.state = state;
}
}
10 years ago
function _bind(ctx, fn) {
11 years ago
var args = slice.call(arguments, 2);
10 years ago
return fn.bind ? fn.bind.apply(fn, [ctx].concat(args)) : function () {
11 years ago
return fn.apply(ctx, args.concat(slice.call(arguments)));
};
}
function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) {
if (el) {
11 years ago
ctx = ctx || document;
selector = selector.split('.');
10 years ago
var tag = selector.shift().toUpperCase(),
re = new RegExp('\\s(' + selector.join('|') + ')(?=\\s)', 'g');
11 years ago
do {
10 years ago
if (
(tag === '>*' && el.parentNode === ctx) || (
(tag === '' || el.nodeName.toUpperCase() == tag) &&
(!selector.length || ((' ' + el.className + ' ').match(re) || []).length == selector.length)
)
10 years ago
) {
return el;
11 years ago
}
}
10 years ago
while (el !== ctx && (el = el.parentNode));
11 years ago
}
10 years ago
return null;
11 years ago
}
function _globalDragOver(/**Event*/evt) {
11 years ago
evt.dataTransfer.dropEffect = 'move';
evt.preventDefault();
}
10 years ago
function _on(el, event, fn) {
11 years ago
el.addEventListener(event, fn, false);
}
10 years ago
function _off(el, event, fn) {
11 years ago
el.removeEventListener(event, fn, false);
}
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(RSPACE, ' ').replace(' ' + name + ' ', ' ');
el.className = (className + (state ? ' ' + name : '')).replace(RSPACE, ' ');
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, fromEl, startIndex, newIndex) {
var evt = document.createEvent('Event'),
options = (sortable || rootEl[expando]).options,
onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1);
evt.initEvent(name, true, true);
evt.to = rootEl;
evt.from = fromEl || rootEl;
evt.item = targetEl || rootEl;
evt.clone = cloneEl;
evt.oldIndex = startIndex;
evt.newIndex = newIndex;
rootEl.dispatchEvent(evt);
if (options[onName]) {
options[onName].call(sortable, evt);
}
}
function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect) {
var evt,
sortable = fromEl[expando],
onMoveFn = sortable.options.onMove,
retVal;
if (onMoveFn) {
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();
retVal = onMoveFn.call(sortable, evt);
}
return retVal;
}
10 years ago
function _disableDraggable(el) {
el.draggable = false;
11 years ago
}
10 years ago
function _unsilent() {
_silent = false;
}
/** @returns {HTMLElement|false} */
10 years ago
function _ghostInBottom(el, evt) {
var lastEl = el.lastElementChild,
rect = lastEl.getBoundingClientRect();
return (evt.clientY - (rect.top + rect.height) > 5) && lastEl; // min delta
}
/**
* 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
* @param el
* @returns {number}
* @private
*/
function _index(/**HTMLElement*/el) {
var index = 0;
while (el && (el = el.previousElementSibling)) {
if (el.nodeName.toUpperCase() !== 'TEMPLATE') {
index++;
}
}
return index;
}
11 years ago
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;
}
11 years ago
// Export utils
Sortable.utils = {
on: _on,
off: _off,
css: _css,
find: _find,
bind: _bind,
is: function (el, selector) {
return !!_closest(el, selector, el);
},
extend: _extend,
throttle: _throttle,
11 years ago
closest: _closest,
toggleClass: _toggleClass,
index: _index
11 years ago
};
9 years ago
Sortable.version = '1.2.1';
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
return Sortable;
11 years ago
});