Skip to content

Instantly share code, notes, and snippets.

@chaoaretasty
Created March 16, 2015 18:24
Show Gist options
  • Save chaoaretasty/1838b20aef7d58de720a to your computer and use it in GitHub Desktop.
Save chaoaretasty/1838b20aef7d58de720a to your computer and use it in GitHub Desktop.
IE8 fixing for Sortable.js

Work in progress to allow IE8 functionality.

I've currently collected my fallback support into the ie8Fix method. This is a temporary situation while hacking to get the basic functionality working. The goal is not to support ie8 in Sortable.js but to allow a site to provide the basic fallbacks they need with the tools they have (for example jQuery is being used above but you could easily replace this with other event handling if you have it). This approach also put the responsibility of support and testing on the implementing site's side asthe functionality is not being provided by the script itself.

Functionality that needs chaning to allow sortable to work in IE8:

  • Events (on, off, dispatch and preventDefault)
  • Mouse button issues
  • nextElementSibling and lastElementChild DOM traversal
  • CSS for the ghost element

The changes that were needed in the main code itself are as follows and should not have any significant impact on code complexity:

  • Line 54, 274: Addition of a leftButton variable so it can be overridden (IE8 and below used a different mapping for mousebuttons)
  • Line 268: IE8 and below used a different property for an event's target
  • Line 562, 841: nextElementSibling is not available in IE8 and below, using a function that can be overriden
  • Line 291, 306, 342, 416, 495, 637, 884, , 837: preventDefault is not available in IE8 and below, using a function that can be overriden
  • Line 845, 964: lastElementChild is not available in IE8 and below, using a function that can be overriden
  • Line 915: IE8 was having issues with comparing void 0 and NaN
/**!
* Sortable
* @author RubaXa <[email protected]>
* @license MIT
*/
(function(factory) {
"use strict";
if (typeof define === "function" && define.amd) {
define(factory);
}
else if (typeof module != "undefined" && typeof module.exports != "undefined") {
module.exports = factory();
}
else if (typeof Package !== "undefined") {
Sortable = factory(); // export for Meteor.js
}
else {
/* jshint sub:true */
window["Sortable"] = factory();
}
})(function() {
"use strict";
var dragEl,
ghostEl,
cloneEl,
rootEl,
nextEl,
scrollEl,
scrollParentEl,
lastEl,
lastCSS,
oldIndex,
newIndex,
activeGroup,
autoScroll = {},
tapEvt,
touchEvt,
expando = 'Sortable' + (new Date).getTime(),
win = window,
document = win.document,
parseInt = win.parseInt,
leftButton = 0,
supportDraggable = !!('draggable' in document.createElement('div')),
_silent = false,
_dispatchEvent = function(rootEl, name, targetEl, fromEl, startIndex, newIndex) {
var evt = document.createEvent('Event');
evt.initEvent(name, true, true);
evt.item = targetEl || rootEl;
evt.from = fromEl || rootEl;
evt.clone = cloneEl;
evt.oldIndex = startIndex;
evt.newIndex = newIndex;
rootEl.dispatchEvent(evt);
},
_customEvents = 'onAdd onUpdate onRemove onStart onEnd onFilter onSort'.split(' '),
noop = function() { },
abs = Math.abs,
slice = [].slice,
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.scrollX + vx * speed, win.scrollY + vy * speed);
} else {
vy && (el.scrollTop += vy * speed);
vx && (el.scrollLeft += vx * speed);
}
}, 24);
}
}
}
}, 30)
;
/**
* @class Sortable
* @param {HTMLElement} el
* @param {Object} [options]
*/
function Sortable(el, options) {
this.el = el; // root element
this.options = options = (options || {});
// 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
};
// Set default options
for (var name in defaults) {
!(name in options) && (options[name] = defaults[name]);
}
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;
}
});
// Define events
_customEvents.forEach(function(name) {
options[name] = _bind(this, options[name] || noop);
_on(el, name.substr(2).toLowerCase(), options[name]);
}, this);
// Export options
options.groups = ' ' + group.name + (group.put.join ? ' ' + group.put.join(' ') : '') + ' ';
el[expando] = options;
// Bind all private methods
for (var fn in this) {
if (fn.charAt(0) === '_') {
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);
touchDragOverListeners.push(this._onDragOver);
// Restore sorting
options.store && this.sort(options.store.get(this));
}
Sortable.prototype = /** @lends Sortable.prototype */ {
constructor: Sortable,
_dragStarted: function() {
if (rootEl && dragEl) {
// Apply effect
_toggleClass(dragEl, this.options.ghostClass, true);
Sortable.active = this;
// Drag start event
_dispatchEvent(rootEl, 'start', dragEl, rootEl, oldIndex);
}
},
_onTapStart: function(/**Event|TouchEvent*/evt) {
var type = evt.type,
touch = evt.touches && evt.touches[0],
target = (touch || evt).target || evt.srcElement,
originalTarget = target,
options = this.options,
el = this.el,
filter = options.filter;
if (type === 'mousedown' && evt.button !== leftButton || options.disabled) {
return; // only left button or enabled
}
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(originalTarget, 'filter', target, el, oldIndex);
_preventDefault(evt);
return; // cancel dnd
}
}
else if (filter) {
filter = filter.split(',').some(function(criteria) {
criteria = _closest(originalTarget, criteria.trim(), el);
if (criteria) {
_dispatchEvent(criteria, 'filter', target, el, oldIndex);
return true;
}
});
if (filter) {
_preventDefault(evt);
return; // cancel dnd
}
}
if (options.handle && !_closest(originalTarget, options.handle, el)) {
return;
}
// Prepare `dragstart`
if (target && !dragEl && (target.parentNode === el)) {
tapEvt = evt;
rootEl = this.el;
dragEl = target;
nextEl = dragEl.nextSibling;
activeGroup = this.options.group;
dragEl.draggable = true;
// Disable "draggable"
options.ignore.split(',').forEach(function(criteria) {
_find(target, criteria.trim(), _disableDraggable);
});
if (touch) {
// Touch device support
tapEvt = {
target: target,
clientX: touch.clientX,
clientY: touch.clientY
};
this._onDragStart(tapEvt, 'touch');
_preventDefault(evt);
}
_on(document, 'mouseup', this._onDrop);
_on(document, 'touchend', this._onDrop);
_on(document, 'touchcancel', this._onDrop);
_on(dragEl, 'dragend', this);
_on(rootEl, 'dragstart', this._onDragStart);
if (!supportDraggable) {
this._onDragStart(tapEvt, true);
}
try {
if (document.selection) {
document.selection.empty();
} else {
window.getSelection().removeAllRanges();
}
} catch (err) {
}
}
},
_emulateDragOver: function() {
if (touchEvt) {
_css(ghostEl, 'display', 'none');
var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY),
parent = target,
groupName = ' ' + this.options.group.name + '',
i = touchDragOverListeners.length;
if (parent) {
do {
if (parent[expando] && parent[expando].groups.indexOf(groupName) > -1) {
while (i--) {
touchDragOverListeners[i]({
clientX: touchEvt.clientX,
clientY: touchEvt.clientY,
target: target,
rootEl: parent
});
}
break;
}
target = parent; // store last element
}
/* jshint boss:true */
while (parent = parent.parentNode);
}
_css(ghostEl, 'display', '');
}
},
_onTouchMove: function(/**TouchEvent*/evt) {
if (tapEvt) {
var touch = evt.touches ? evt.touches[0] : evt,
dx = touch.clientX - tapEvt.clientX,
dy = touch.clientY - tapEvt.clientY,
translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';
touchEvt = touch;
_css(ghostEl, 'webkitTransform', translate3d);
_css(ghostEl, 'mozTransform', translate3d);
_css(ghostEl, 'msTransform', translate3d);
_css(ghostEl, 'transform', translate3d);
_preventDefault(evt);
}
},
_onDragStart: function(/**Event*/evt, /**boolean*/useFallback) {
var dataTransfer = evt.dataTransfer,
options = this.options;
this._offUpEvents();
if (activeGroup.pull == 'clone') {
cloneEl = dragEl.cloneNode(true);
_css(cloneEl, 'display', 'none');
rootEl.insertBefore(cloneEl, dragEl);
}
if (useFallback) {
var rect = dragEl.getBoundingClientRect(),
css = _css(dragEl),
ghostRect;
ghostEl = dragEl.cloneNode(true);
_css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10));
_css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10));
_css(ghostEl, 'width', rect.width);
_css(ghostEl, 'height', rect.height);
_css(ghostEl, 'opacity', '0.8');
_css(ghostEl, 'position', 'fixed');
_css(ghostEl, 'zIndex', '100000');
rootEl.appendChild(ghostEl);
// Fixing dimensions.
ghostRect = ghostEl.getBoundingClientRect();
_css(ghostEl, 'width', rect.width * 2 - ghostRect.width);
_css(ghostEl, 'height', rect.height * 2 - ghostRect.height);
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);
}
this._loopId = setInterval(this._emulateDragOver, 150);
}
else {
if (dataTransfer) {
dataTransfer.effectAllowed = 'move';
options.setData && options.setData.call(this, dataTransfer, dragEl);
}
_on(document, 'drop', this);
}
setTimeout(this._dragStarted, 0);
},
_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 (!dragEl) {
return;
}
if (evt.preventDefault !== void 0) {
_preventDefault(evt);
!options.dragoverBubble && evt.stopPropagation();
}
if (activeGroup && !options.disabled &&
(isOwner
? canSort || (revert = !rootEl.contains(dragEl))
: 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)
) {
// 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;
}
if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
(el === evt.target) && (target = _ghostInBottom(el, evt))
) {
if (target) {
if (target.animated) {
return;
}
targetRect = target.getBoundingClientRect();
}
_cloneHide(isOwner);
el.appendChild(dragEl);
this._animate(dragRect, dragEl);
target && this._animate(targetRect, target);
}
else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) {
if (lastEl !== target) {
lastEl = target;
lastCSS = _css(target);
}
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 = _getNextElementSibling(target),
after
;
_silent = true;
setTimeout(_unsilent, 30);
_cloneHide(isOwner);
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);
}
}
},
_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)');
clearTimeout(target.animated);
target.animated = setTimeout(function() {
_css(target, 'transition', '');
_css(target, 'transform', '');
target.animated = false;
}, ms);
}
},
_offUpEvents: function() {
_off(document, 'mouseup', this._onDrop);
_off(document, 'touchmove', this._onTouchMove);
_off(document, 'touchend', this._onDrop);
_off(document, 'touchcancel', this._onDrop);
},
_onDrop: function(/**Event*/evt) {
var el = this.el,
options = this.options;
clearInterval(this._loopId);
clearInterval(autoScroll.pid);
// Unbind events
_off(document, 'drop', this);
_off(document, 'mousemove', this._onTouchMove);
_off(el, 'dragstart', this._onDragStart);
this._offUpEvents();
if (evt) {
_preventDefault(evt);
!options.dropBubble && evt.stopPropagation();
ghostEl && ghostEl.parentNode.removeChild(ghostEl);
if (dragEl) {
_off(dragEl, 'dragend', this);
_disableDraggable(dragEl);
_toggleClass(dragEl, this.options.ghostClass, false);
if (rootEl !== dragEl.parentNode) {
newIndex = _index(dragEl);
// drag from one list and drop into another
_dispatchEvent(dragEl.parentNode, 'sort', dragEl, rootEl, oldIndex, newIndex);
_dispatchEvent(rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
// Add event
_dispatchEvent(dragEl, 'add', dragEl, rootEl, oldIndex, newIndex);
// Remove event
_dispatchEvent(rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex);
}
else {
// Remove clone
cloneEl && cloneEl.parentNode.removeChild(cloneEl);
if (dragEl.nextSibling !== nextEl) {
// Get the index of the dragged element within its parent
newIndex = _index(dragEl);
// drag & drop within the same list
_dispatchEvent(rootEl, 'update', dragEl, rootEl, oldIndex, newIndex);
_dispatchEvent(rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
}
}
// Drag end event
Sortable.active && _dispatchEvent(rootEl, 'end', dragEl, rootEl, oldIndex, newIndex);
}
// Nulling
rootEl =
dragEl =
ghostEl =
nextEl =
cloneEl =
scrollEl =
scrollParentEl =
tapEvt =
touchEvt =
lastEl =
lastCSS =
activeGroup =
Sortable.active = null;
// Save sorting
this.save();
}
},
handleEvent: function(/**Event*/evt) {
var type = evt.type;
if (type === 'dragover' || type === 'dragenter') {
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;
for (; i < n; i++) {
el = children[i];
if (_closest(el, this.options.draggable, this.el)) {
order.push(el.getAttribute('data-id') || _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, options = this.options;
_customEvents.forEach(function(name) {
_off(el, name.substr(2).toLowerCase(), options[name]);
});
_off(el, 'mousedown', this._onTapStart);
_off(el, 'touchstart', this._onTapStart);
_off(el, 'dragover', this);
_off(el, 'dragenter', this);
//remove draggable attributes
Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function(el) {
el.removeAttribute('draggable');
});
touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1);
this._onDrop();
this.el = null;
}
};
function _cloneHide(state) {
if (cloneEl && (cloneEl.state !== state)) {
_css(cloneEl, 'display', state ? 'none' : '');
!state && cloneEl.state && rootEl.insertBefore(cloneEl, dragEl);
cloneEl.state = state;
}
}
function _preventDefault(evt) {
evt.preventDefault();
}
function _getNextElementSibling(el) {
return el.nextElementSibling;
}
function _getLastElementChild(el) {
return el.lastElementChild;
}
function _bind(ctx, fn) {
var args = slice.call(arguments, 2);
return fn.bind ? fn.bind.apply(fn, [ctx].concat(args)) : function() {
return fn.apply(ctx, args.concat(slice.call(arguments)));
};
}
function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) {
if (el) {
ctx = ctx || document;
selector = selector.split('.');
var tag = selector.shift().toUpperCase(),
re = new RegExp('\\s(' + selector.join('|') + ')\\s', 'g');
do {
if (
(tag === '>*' && el.parentNode === ctx) || (
(tag === '' || el.nodeName.toUpperCase() == tag) &&
(!selector.length || ((' ' + el.className + ' ').match(re) || []).length == selector.length)
)
) {
return el;
}
}
while (el !== ctx && (el = el.parentNode));
}
return null;
}
function _globalDragOver(/**Event*/evt) {
evt.dataTransfer.dropEffect = 'move';
_preventDefault(evt);
}
function _on(el, event, fn) {
el.addEventListener(event, fn, false);
}
function _off(el, event, fn) {
el.removeEventListener(event, fn, false);
}
function _toggleClass(el, name, state) {
if (el) {
if (el.classList) {
el.classList[state ? 'add' : 'remove'](name);
}
else {
var className = (' ' + el.className + ' ').replace(/\s+/g, ' ').replace(' ' + name + ' ', '');
el.className = className + (state ? ' ' + name : '');
}
}
}
function _css(el, prop, val) {
var style = el && el.style;
if (style) {
if (val === void 0 || (typeof val === "number" && isNaN(val))) {
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 = '-webkit-' + prop;
}
style[prop] = val + (typeof val === 'string' ? '' : 'px');
}
}
}
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 _disableDraggable(el) {
el.draggable = false;
}
function _unsilent() {
_silent = false;
}
/** @returns {HTMLElement|false} */
function _ghostInBottom(el, evt) {
var lastEl = _getLastElementChild(el), 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,
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;
}
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);
}
};
}
// Export utils
Sortable.utils = {
on: _on,
off: _off,
css: _css,
find: _find,
bind: _bind,
is: function(el, selector) {
return !!_closest(el, selector, el);
},
throttle: _throttle,
closest: _closest,
toggleClass: _toggleClass,
dispatchEvent: _dispatchEvent,
index: _index
};
Sortable.ie8Fix = function() {
leftButton = 1;
_on = function(el, evt, fn) {
$(el).on(evt, fn);
};
_off = function(el, evt, fn) {
$(el).off(evt, fn);
};
/*
// If not using jQuery this should be replaced with event.returnValue = false;
_preventDefault = function(evt) {
evt.preventDefault();
};*/
_dispatchEvent = function(rootEl, name, targetEl, fromEl, startIndex, newIndex) {
var evt = $.Event(name);
evt.item = targetEl || rootEl;
evt.from = fromEl || rootEl;
evt.clone = cloneEl;
evt.oldIndex = startIndex;
evt.newIndex = newIndex;
$(rootEl).trigger(evt);
};
_getNextElementSibling = function(el) {
do { el = el.nextSibling; } while (el && el.nodeType !== 1);
return el;
};
_getLastElementChild = function(el) {
el = el.lastChild;
do{el = el.previousSibling;} while(el && el.nodeType !== 1)
return el;
};
Sortable.prototype._onTouchMove = function(evt) {
if (tapEvt) {
var mouseMove = evt,
dx = mouseMove.clientX,
dy = mouseMove.clientY;
touchEvt = mouseMove;
_css(ghostEl, 'top', dy);
_css(ghostEl, 'left', dx);
_preventDefault(evt);
}
};
};
Sortable.version = '1.1.1';
/**
* Create sortable instance
* @param {HTMLElement} el
* @param {Object} [options]
*/
Sortable.create = function(el, options) {
return new Sortable(el, options);
};
// Export
return Sortable;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment