Files

289 lines
11 KiB
JavaScript

if (!Element.prototype.matches) {
Element.prototype.matches =
Element.prototype.msMatchesSelector ||
Element.prototype.webkitMatchesSelector;
}
if (!Element.prototype.closest) {
Element.prototype.closest = function (s) {
var el = this;
do {
if (Element.prototype.matches.call(el, s)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
};
}
(function (name, factory) {
if (typeof window === "object") {
// add to window
window[name] = factory();
// add jquery plugin, if available
if (typeof jQuery === "function") {
jQuery.fn[name] = function (options) {
return this.each(function () {
new window[name](this, options);
});
};
}
}
})("Sortable", function () {
// get position of mouse/touch in relation to viewport
var getPoint = function (e) {
var _w = window,
_b = document.body,
_d = document.documentElement;
var scrollX = Math.max(0, _w.pageXOffset || _d.scrollLeft || _b.scrollLeft || 0) - (_d.clientLeft || 0),
scrollY = Math.max(0, _w.pageYOffset || _d.scrollTop || _b.scrollTop || 0) - (_d.clientTop || 0),
pointX = e ? (Math.max(0, e.pageX || e.clientX || 0) - scrollX) : 0,
pointY = e ? (Math.max(0, e.pageY || e.clientY || 0) - scrollY) : 0;
return { x: pointX, y: pointY };
};
// class constructor
var Factory = function (container, options) {
if (container && container instanceof Element) {
this._container = container;
this._options = options || {}; /* nothing atm */
this._clickItem = null;
this._dragItem = null;
this._showDragItem = (typeof this._options.dragItem === 'boolean' && this._options.dragItem === false) ? false : true;
this._hovItem = null;
this._sortLists = [];
this._click = {};
this._dragging = false;
this._dragHandleClass = this._options.dragHandleClass || '';
this._parentident = this._options.parentident || '';
this._swapdone = typeof this._options.swapdone === "function" ? this._options._swapdone : null;
this._container.setAttribute("data-is-sortable", 1);
this._container.classList.add("sortable");
this._container.style["position"] = "static";
window.addEventListener("mousedown", this._onPress.bind(this), true);
window.addEventListener("touchstart", this._onPress.bind(this), true);
window.addEventListener("mouseup", this._onRelease.bind(this), true);
window.addEventListener("touchend", this._onRelease.bind(this), true);
window.addEventListener("mousemove", this._onMove.bind(this), true);
window.addEventListener("touchmove", this._onMove.bind(this), true);
}
};
// class prototype
Factory.prototype = {
constructor: Factory,
// serialize order into array list
toArray: function (attr) {
attr = attr || "id";
var data = [],
item = null,
uniq = "";
for (var i = 0; i < this._container.children.length; ++i) {
item = this._container.children[i],
uniq = item.getAttribute(attr) || "";
uniq = uniq.replace(/[^0-9]+/gi, "");
data.push(uniq);
}
return data;
},
// serialize order array into a string
toString: function (attr, delimiter) {
delimiter = delimiter || ":";
return this.toArray(attr).join(delimiter);
},
// checks if mouse x/y is on top of an item
_isOnTop: function (item, x, y) {
var box = item.getBoundingClientRect(),
isx = (x > box.left && x < (box.left + box.width)),
isy = (y > box.top && y < (box.top + box.height));
return (isx && isy);
},
// manipulate the className of an item (for browsers that lack classList support)
_itemClass: function (item, task, cls) {
var list = item.className.split(/\s+/),
index = list.indexOf(cls);
if (task === "add" && index == -1) {
list.push(cls);
item.className = list.join(" ");
}
else if (task === "remove" && index != -1) {
list.splice(index, 1);
item.className = list.join(" ");
}
},
// swap position of two item in sortable list container
_swapItems: function (item1, item2) {
var parent1 = item1.parentNode,
parent2 = item2.parentNode;
if (item2.className.indexOf('nosort') < 0) {
if (parent1 !== parent2) {
// move to new list
parent2.insertBefore(item1, item2);
}
else {
// sort is same list
var temp = document.createElement("div");
parent1.insertBefore(temp, item1);
parent2.insertBefore(item1, item2);
parent1.insertBefore(item2, temp);
parent1.removeChild(temp);
}
if (typeof this._swapdone === 'function') {
this._swapdone(parent1, parent2, item1, item2);
}
}
},
// update item position
_moveItem: function (item, x, y) {
item.style["-webkit-transform"] = "translateX( " + x + "px ) translateY( " + y + "px )";
item.style["-moz-transform"] = "translateX( " + x + "px ) translateY( " + y + "px )";
item.style["-ms-transform"] = "translateX( " + x + "px ) translateY( " + y + "px )";
item.style["transform"] = "translateX( " + x + "px ) translateY( " + y + "px )";
},
// make a temp fake item for dragging and add to container
_makeDragItem: function (item) {
this._trashDragItem();
this._sortLists = document.querySelectorAll("[data-is-sortable]");
this._clickItem = item;
this._itemClass(this._clickItem, "add", "sortactive");
this._dragItem = document.createElement(item.tagName);
this._dragItem.className = "dragging";
this._dragItem.innerHTML = item.innerHTML;
this._dragItem.style["position"] = "absolute";
this._dragItem.style["z-index"] = "999";
this._dragItem.style["left"] = (item.offsetLeft || 0) + "px";
this._dragItem.style["top"] = (item.offsetTop || 0) + "px";
this._dragItem.style["width"] = (item.offsetWidth || 0) + "px";
if (this._showDragItem === true) {
this._container.appendChild(this._dragItem);
}
},
// remove drag item that was added to container
_trashDragItem: function () {
if (this._dragItem && this._clickItem) {
this._itemClass(this._clickItem, "remove", "sortactive");
this._clickItem = null;
if (this._showDragItem === true) {
this._container.removeChild(this._dragItem);
}
this._dragItem = null;
}
},
// on item press/drag
_onPress: function (e) {
if (!e) { return; }
function op(tgt) {
if (tgt && tgt.parentNode === this._container && (this._dragHandleClass === '' || e.target.className.indexOf(this._dragHandleClass) > -1) && tgt.className.indexOf("nosort") < 0) {
e.preventDefault();
this._dragging = true;
this._click = getPoint(e);
this._makeDragItem(tgt);
this._onMove(e);
return true;
} else {
return false;
}
}
if (op.call(this, e.target) === false && this._parentident !== '' && e.target.closest(this._parentident)) {
op.call(this, e.target.closest(this._parentident))
}
},
// on item release/drop
_onRelease: function (e) {
this._dragging = false;
this._trashDragItem();
},
// on item drag/move
_onMove: function (e) {
if (this._dragItem && this._dragging) {
e.preventDefault();
var point = getPoint(e);
var container = this._container;
// drag fake item
if (this._showDragItem === true) {
this._moveItem(this._dragItem, (point.x - this._click.x), (point.y - this._click.y));
}
// keep an eye for other sortable lists and switch over to it on hover
for (var a = 0; a < this._sortLists.length; ++a) {
var subContainer = this._sortLists[a];
if (this._isOnTop(subContainer, point.x, point.y)) {
container = subContainer;
}
}
// container is empty, move clicked item over to it on hover
if (this._isOnTop(container, point.x, point.y) && container.children.length === 0) {
container.appendChild(this._clickItem);
return;
}
// check if current drag item is over another item and swap places
for (var b = 0; b < container.children.length; ++b) {
var subItem = container.children[b];
if (subItem === this._clickItem || subItem === this._dragItem) {
continue;
}
if (this._isOnTop(subItem, point.x, point.y)) {
this._hovItem = subItem;
if (subItem.className.indexOf('nosort') < 0) {
this._dragItem.className = this._dragItem.className.replace(/\bnotgt\b/g, '');
this._swapItems(this._clickItem, subItem);
} else {
var arr = this._dragItem.className.split(' ');
if (arr.indexOf('notgt') === -1) {
this._dragItem.className += ' notgt';
}
}
}
}
}
},
};
// export
return Factory;
});
//// helper init function
//initSortable(list) {
// var listObj = $(list);
// var sortable = [];
// listObj.each(function (i, d) {
// sortable.push(new Sortable(d));
// });
//}