Browse Source

* upd

gh-pages
RubaXa 9 years ago
parent
commit
dc57948609
  1. 230
      Sortable.js
  2. 4
      Sortable.min.js
  3. 7
      jquery.binding.js
  4. 27
      knockout-sortable.js
  5. 51
      ng-sortable.js
  6. 5
      react-sortable-mixin.js

230
Sortable.js

@ -25,6 +25,7 @@
"use strict";
var dragEl,
parentEl,
ghostEl,
cloneEl,
rootEl,
@ -35,6 +36,7 @@
lastEl,
lastCSS,
lastParentCSS,
oldIndex,
newIndex,
@ -45,6 +47,8 @@
tapEvt,
touchEvt,
moved,
/** @const */
RSPACE = /\s+/g,
@ -55,6 +59,11 @@
parseInt = win.parseInt,
supportDraggable = !!('draggable' in document.createElement('div')),
supportCssPointerEvents = (function (el) {
el = document.createElement('x');
el.style.cssText = 'pointer-events:auto';
return el.style.pointerEvents === 'auto';
})(),
_silent = false,
@ -136,7 +145,23 @@
}
}
}
}, 30)
}, 30),
_prepareGroup = function (options) {
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(' ') : '') + ' ';
}
;
@ -167,6 +192,7 @@
scrollSpeed: 10,
draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*',
ghostClass: 'sortable-ghost',
chosenClass: 'sortable-chosen',
ignore: 'a, img',
filter: null,
animation: 0,
@ -176,7 +202,10 @@
dropBubble: false,
dragoverBubble: false,
dataIdAttr: 'data-id',
delay: 0
delay: 0,
forceFallback: false,
fallbackClass: 'sortable-fallback',
fallbackOnBody: false
};
@ -185,38 +214,26 @@
!(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;
}
});
options.groups = ' ' + group.name + (group.put.join ? ' ' + group.put.join(' ') : '') + ' ';
_prepareGroup(options);
// Bind all private methods
for (var fn in this) {
if (fn.charAt(0) === '_') {
this[fn] = _bind(this, this[fn]);
this[fn] = this[fn].bind(this);
}
}
// Setup drag mode
this.nativeDraggable = options.forceFallback ? false : supportDraggable;
// Bind events
_on(el, 'mousedown', this._onTapStart);
_on(el, 'touchstart', this._onTapStart);
if (this.nativeDraggable) {
_on(el, 'dragover', this);
_on(el, 'dragenter', this);
}
touchDragOverListeners.push(this._onDragOver);
@ -298,6 +315,7 @@
rootEl = el;
dragEl = target;
parentEl = dragEl.parentNode;
nextEl = dragEl.nextSibling;
activeGroup = options.group;
@ -309,22 +327,29 @@
// Make the element draggable
dragEl.draggable = true;
// Disable "draggable"
options.ignore.split(',').forEach(function (criteria) {
_find(dragEl, criteria.trim(), _disableDraggable);
});
// Chosen item
_toggleClass(dragEl, _this.options.chosenClass, true);
// Bind the events: dragstart/dragend
_this._triggerDragStart(touch);
};
// 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);
if (options.delay) {
// If the user moves the pointer before the delay has been reached:
// 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);
@ -339,7 +364,9 @@
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);
},
@ -355,7 +382,7 @@
this._onDragStart(tapEvt, 'touch');
}
else if (!supportDraggable) {
else if (!this.nativeDraggable) {
this._onDragStart(tapEvt, true);
}
else {
@ -387,7 +414,16 @@
_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');
}
var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY),
parent = target,
@ -415,18 +451,29 @@
while (parent = parent.parentNode);
}
if (!supportCssPointerEvents) {
_css(ghostEl, 'display', '');
}
}
},
_onTouchMove: function (/**TouchEvent*/evt) {
if (tapEvt) {
// only set the status to dragging, when we are actually dragging
if (!Sortable.active) {
this._dragStarted();
}
// as well as creating the ghost element on the document body
this._appendGhost();
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)';
moved = true;
touchEvt = touch;
_css(ghostEl, 'webkitTransform', translate3d);
@ -438,26 +485,17 @@
}
},
_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) {
_appendGhost: function () {
if (!ghostEl) {
var rect = dragEl.getBoundingClientRect(),
css = _css(dragEl),
ghostRect;
ghostEl = dragEl.cloneNode(true);
_toggleClass(ghostEl, this.options.ghostClass, false);
_toggleClass(ghostEl, this.options.fallbackClass, true);
_css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10));
_css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10));
_css(ghostEl, 'width', rect.width);
@ -465,13 +503,30 @@
_css(ghostEl, 'opacity', '0.8');
_css(ghostEl, 'position', 'fixed');
_css(ghostEl, 'zIndex', '100000');
_css(ghostEl, 'pointerEvents', 'none');
rootEl.appendChild(ghostEl);
this.options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
// Fixing dimensions.
ghostRect = ghostEl.getBoundingClientRect();
_css(ghostEl, 'width', rect.width * 2 - ghostRect.width);
_css(ghostEl, 'height', rect.height * 2 - ghostRect.height);
}
},
_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) {
if (useFallback === 'touch') {
// Bind touch events
@ -484,7 +539,7 @@
_on(document, 'mouseup', this._onDrop);
}
this._loopId = setInterval(this._emulateDragOver, 150);
this._loopId = setInterval(this._emulateDragOver, 50);
}
else {
if (dataTransfer) {
@ -493,9 +548,8 @@
}
_on(document, 'drop', this);
}
setTimeout(this._dragStarted, 0);
}
},
_onDragOver: function (/**Event*/evt) {
@ -514,6 +568,8 @@
!options.dragoverBubble && evt.stopPropagation();
}
moved = true;
if (activeGroup && !options.disabled &&
(isOwner
? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list
@ -534,7 +590,6 @@
target = _closest(evt.target, options.draggable, el);
dragRect = dragEl.getBoundingClientRect();
if (revert) {
_cloneHide(true);
@ -550,19 +605,25 @@
if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
(el === evt.target) && (target = _ghostInBottom(el, evt))
(el === evt.target) && (target = _ghostIsLast(el, evt))
) {
if (target) {
if (target.animated) {
return;
}
targetRect = target.getBoundingClientRect();
}
_cloneHide(isOwner);
if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect) !== false) {
if (!dragEl.contains(el)) {
el.appendChild(dragEl);
parentEl = el; // actualization
}
this._animate(dragRect, dragEl);
target && this._animate(targetRect, target);
}
@ -571,13 +632,15 @@
if (lastEl !== target) {
lastEl = target;
lastCSS = _css(target);
lastParentCSS = _css(target.parentNode);
}
var targetRect = target.getBoundingClientRect(),
width = targetRect.right - targetRect.left,
height = targetRect.bottom - targetRect.top,
floating = /left|right|inline/.test(lastCSS.cssFloat + lastCSS.display),
floating = /left|right|inline/.test(lastCSS.cssFloat + lastCSS.display)
|| (lastParentCSS.display == 'flex' && lastParentCSS['flex-direction'].indexOf('row') === 0),
isWide = (target.offsetWidth > dragEl.offsetWidth),
isLong = (target.offsetHeight > dragEl.offsetHeight),
halfway = (floating ? (evt.clientX - targetRect.left) / width : (evt.clientY - targetRect.top) / height) > 0.5,
@ -596,16 +659,27 @@
after = (moveVector === 1);
}
else if (floating) {
var elTop = dragEl.offsetTop,
tgTop = target.offsetTop;
if (elTop === tgTop) {
after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide;
} else {
after = tgTop > elTop;
}
} else {
after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
}
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);
@ -658,14 +732,17 @@
clearTimeout(this._dragStartTimer);
// Unbind events
_off(document, 'drop', this);
_off(document, 'mousemove', this._onTouchMove);
if (this.nativeDraggable) {
_off(document, 'drop', this);
_off(el, 'dragstart', this._onDragStart);
}
this._offUpEvents();
if (evt) {
if (Sortable.active) {
if (moved) {
evt.preventDefault();
!options.dropBubble && evt.stopPropagation();
}
@ -673,24 +750,31 @@
ghostEl && ghostEl.parentNode.removeChild(ghostEl);
if (dragEl) {
if (this.nativeDraggable) {
_off(dragEl, 'dragend', this);
}
_disableDraggable(dragEl);
// Remove class's
_toggleClass(dragEl, this.options.ghostClass, false);
_toggleClass(dragEl, this.options.chosenClass, false);
if (rootEl !== dragEl.parentNode) {
if (rootEl !== parentEl) {
newIndex = _index(dragEl);
if (newIndex != -1) {
// drag from one list and drop into another
_dispatchEvent(null, dragEl.parentNode, 'sort', dragEl, rootEl, oldIndex, newIndex);
_dispatchEvent(null, parentEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
// Add event
_dispatchEvent(null, dragEl.parentNode, 'add', dragEl, rootEl, oldIndex, newIndex);
_dispatchEvent(null, parentEl, 'add', dragEl, rootEl, oldIndex, newIndex);
// Remove event
_dispatchEvent(this, rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex);
}
}
else {
// Remove clone
cloneEl && cloneEl.parentNode.removeChild(cloneEl);
@ -698,12 +782,13 @@
if (dragEl.nextSibling !== nextEl) {
// Get the index of the dragged element within its parent
newIndex = _index(dragEl);
if (newIndex != -1) {
// drag & drop within the same list
_dispatchEvent(this, rootEl, 'update', dragEl, rootEl, oldIndex, newIndex);
_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
}
}
}
if (Sortable.active) {
// Drag end event
@ -717,6 +802,7 @@
// Nulling
rootEl =
dragEl =
parentEl =
ghostEl =
nextEl =
cloneEl =
@ -727,6 +813,9 @@
tapEvt =
touchEvt =
moved =
newIndex =
lastEl =
lastCSS =
@ -831,6 +920,10 @@
return options[name];
} else {
options[name] = value;
if (name === 'group') {
_prepareGroup(options);
}
}
},
@ -846,8 +939,10 @@
_off(el, 'mousedown', this._onTapStart);
_off(el, 'touchstart', 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) {
@ -872,14 +967,6 @@
}
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;
@ -906,7 +993,9 @@
function _globalDragOver(/**Event*/evt) {
if (evt.dataTransfer) {
evt.dataTransfer.dropEffect = 'move';
}
evt.preventDefault();
}
@ -1006,7 +1095,6 @@
onMoveFn = sortable.options.onMove,
retVal;
if (onMoveFn) {
evt = document.createEvent('Event');
evt.initEvent('move', true, true);
@ -1017,6 +1105,9 @@
evt.related = targetEl || toEl;
evt.relatedRect = targetRect || toEl.getBoundingClientRect();
fromEl.dispatchEvent(evt);
if (onMoveFn) {
retVal = onMoveFn.call(sortable, evt);
}
@ -1035,11 +1126,11 @@
/** @returns {HTMLElement|false} */
function _ghostInBottom(el, evt) {
function _ghostIsLast(el, evt) {
var lastEl = el.lastElementChild,
rect = lastEl.getBoundingClientRect();
return (evt.clientY - (rect.top + rect.height) > 5) && lastEl; // min delta
return ((evt.clientY - (rect.top + rect.height) > 5) || (evt.clientX - (rect.right + rect.width) > 5)) && lastEl; // min delta
}
@ -1068,6 +1159,9 @@
* @private
*/
function _index(/**HTMLElement*/el) {
if (!el || !el.parentNode) {
return -1;
}
var index = 0;
while (el && (el = el.previousElementSibling)) {
if (el.nodeName.toUpperCase() !== 'TEMPLATE') {
@ -1117,7 +1211,6 @@
off: _off,
css: _css,
find: _find,
bind: _bind,
is: function (el, selector) {
return !!_closest(el, selector, el);
},
@ -1129,9 +1222,6 @@
};
Sortable.version = '1.2.1';
/**
* Create sortable instance
* @param {HTMLElement} el
@ -1141,6 +1231,8 @@
return new Sortable(el, options);
};
// Export
Sortable.version = '1.3.0-rc1';
return Sortable;
});

4
Sortable.min.js vendored

File diff suppressed because one or more lines are too long

7
jquery.binding.js

@ -46,8 +46,11 @@
sortable.destroy();
$el.removeData('sortable');
}
else if (options in sortable) {
retVal = sortable[sortable].apply(sortable, [].slice.call(arguments, 1));
else if (typeof sortable[options] === 'function') {
retVal = sortable[options].apply(sortable, [].slice.call(arguments, 1));
}
else if (options in sortable.options) {
retVal = sortable.option.apply(sortable, arguments);
}
}
});

27
knockout-sortable.js

@ -1,4 +1,17 @@
(function () {
(function (factory) {
"use strict";
if (typeof define === "function" && define.amd) {
// AMD anonymous module
define(["knockout"], factory);
} else if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
// CommonJS module
var ko = require("knockout");
factory(ko);
} else {
// No module loader (plain <script> tag) - put directly in global namespace
factory(window.ko);
}
})(function (ko) {
"use strict";
var init = function (element, valueAccessor, allBindings, viewModel, bindingContext, sortableOptions) {
@ -67,7 +80,13 @@
//Unwrapping this allows us to manipulate the actual array
var fromArray = from(),
//It's not certain that the items actual index is the same as the index reported by sortable due to filtering etc.
originalIndex = fromArray.indexOf(itemVM);
originalIndex = fromArray.indexOf(itemVM),
newIndex = e.newIndex;
if (e.item.previousElementSibling)
newIndex = fromArray.indexOf(ko.dataFor(e.item.previousElementSibling));
if (originalIndex > newIndex)
newIndex = newIndex + 1;
//Remove sortables "unbound" element
e.item.parentNode.removeChild(e.item);
@ -85,7 +104,7 @@
from.valueHasMutated();
}
//Insert the item on its new position
to().splice(e.newIndex, 0, itemVM);
to().splice(newIndex, 0, itemVM);
//Make sure to tell knockout that we've modified the actual array.
to.valueHasMutated();
};
@ -158,4 +177,4 @@
}
};
})();
});

51
ng-sortable.js

@ -23,16 +23,15 @@
* @property {number} newIndex after sort
*/
var expando = 'Sortable:ng-sortable';
angular.module('ng-sortable', [])
.constant('ngSortableVersion', '0.3.7')
.constant('ngSortableVersion', '0.4.0')
.constant('ngSortableConfig', {})
.directive('ngSortable', ['$parse', 'ngSortableConfig', function ($parse, ngSortableConfig) {
var removed,
nextSibling;
function getSource(el) {
var scope = angular.element(el).scope();
nextSibling,
getSourceFactory = function getSourceFactory(el, scope) {
var ngRepeat = [].filter.call(el.childNodes, function (node) {
return (
(node.nodeType === 8) &&
@ -48,40 +47,36 @@
// tests: http://jsbin.com/kosubutilo/1/edit?js,output
ngRepeat = ngRepeat.nodeValue.match(/ngRepeat:\s*(?:\(.*?,\s*)?([^\s)]+)[\s)]+in\s+([^\s|]+)/);
var itemExpr = $parse(ngRepeat[1]);
var itemsExpr = $parse(ngRepeat[2]);
return {
item: function (el) {
return itemExpr(angular.element(el).scope());
},
items: function () {
return itemsExpr(scope);
}
return function () {
return itemsExpr(scope.$parent) || [];
};
};
}
// Export
return {
restrict: 'AC',
scope: { ngSortable: "=?" },
link: function (scope, $el, attrs) {
link: function (scope, $el) {
var el = $el[0],
options = angular.extend(scope.ngSortable || {}, ngSortableConfig),
source = getSource(el),
watchers = [],
getSource = getSourceFactory(el, scope),
sortable
;
el[expando] = getSource;
function _emitEvent(/**Event*/evt, /*Mixed*/item) {
var name = 'on' + evt.type.charAt(0).toUpperCase() + evt.type.substr(1);
var source = getSource();
/* jshint expr:true */
options[name] && options[name]({
model: item || source && source.item(evt.item),
models: source && source.items(),
model: item || source[evt.newIndex],
models: source,
oldIndex: evt.oldIndex,
newIndex: evt.newIndex
});
@ -89,25 +84,25 @@
function _sync(/**Event*/evt) {
if (!source) {
var items = getSource();
if (!items) {
// Without ng-repeat
return;
}
var oldIndex = evt.oldIndex,
newIndex = evt.newIndex,
items = source.items();
newIndex = evt.newIndex;
if (el !== evt.from) {
var prevSource = getSource(evt.from),
prevItems = prevSource.items();
var prevItems = evt.from[expando]();
oldIndex = prevItems.indexOf(prevSource.item(evt.item));
removed = prevItems[oldIndex];
if (evt.clone) {
evt.from.removeChild(evt.clone);
removed = angular.copy(removed);
prevItems.splice(Sortable.utils.index(evt.clone), 0, prevItems.splice(oldIndex, 1)[0]);
evt.from.removeChild(evt.clone);
}
else {
prevItems.splice(oldIndex, 1);
@ -159,14 +154,18 @@
angular.forEach(watchers, function (/** Function */unwatch) {
unwatch();
});
sortable.destroy();
el[expando] = null;
el = null;
watchers = null;
sortable = null;
nextSibling = null;
});
angular.forEach([
'sort', 'disabled', 'draggable', 'handle', 'animation',
'sort', 'disabled', 'draggable', 'handle', 'animation', 'group', 'ghostClass', 'filter',
'onStart', 'onEnd', 'onAdd', 'onUpdate', 'onRemove', 'onSort'
], function (name) {
watchers.push(scope.$watch('ngSortable.' + name, function (value) {

5
react-sortable-mixin.js vendored

@ -34,7 +34,8 @@
onUpdate: 'handleUpdate',
onRemove: 'handleRemove',
onSort: 'handleSort',
onFilter: 'handleFilter'
onFilter: 'handleFilter',
onMove: 'handleMove'
};
@ -90,7 +91,7 @@
// Bind callbacks so that "this" refers to the component
'onStart onEnd onAdd onSort onUpdate onRemove onFilter'.split(' ').forEach(function (/** string */name) {
'onStart onEnd onAdd onSort onUpdate onRemove onFilter onMove'.split(' ').forEach(function (/** string */name) {
copyOptions[name] = function (evt) {
if (name === 'onStart') {
_nextSibling = evt.item.nextElementSibling;

Loading…
Cancel
Save