From 1c8839f08fdbe5399a216cfa4872df97eda794a6 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Mon, 16 Feb 2015 08:57:02 +0300 Subject: [PATCH 01/27] #277: * fixed handle & filter --- Sortable.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Sortable.js b/Sortable.js index 5c4672c..6f1d4f0 100644 --- a/Sortable.js +++ b/Sortable.js @@ -275,12 +275,12 @@ return; // only left button or enabled } - if (options.handle) { - target = _closest(target, options.handle, el); - } - target = _closest(target, options.draggable, el); + if (!target) { + return; + } + // get the index of the dragged element within its parent oldIndex = _index(target); @@ -308,6 +308,12 @@ } } + + if (options.handle && !_closest(originalTarget, options.handle, el)) { + return; + } + + // Prepare `dragstart` if (target && !dragEl && (target.parentNode === el)) { tapEvt = evt; From 7dc8be57559534d5a93cf5aff1306a0f83daf56d Mon Sep 17 00:00:00 2001 From: RubaXa Date: Mon, 16 Feb 2015 17:55:24 +0300 Subject: [PATCH 02/27] #279: + dataIdAttr --- README.md | 3 ++- Sortable.js | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7056008..1c1867d 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ var sortable = new Sortable(el, { filter: ".ignore-elements", // Selectors that do not lead to dragging (String or Function) draggable: ".item", // Specifies which items inside the element should be sortable ghostClass: "sortable-ghost", // Class name for the drop placeholder + dataIdAttr: 'data-id', scroll: true, // or HTMLElement scrollSensitivity: 30, // px, how near the mouse must be to an edge to start scrolling. @@ -399,7 +400,7 @@ For each element in the set, get the first element that matches the selector by ##### toArray():`String[]` -Serializes the sortable's item `data-id`'s into an array of string. +Serializes the sortable's item `data-id`'s (`dataIdAttr` option) into an array of string. ##### sort(order:`String[]`) diff --git a/Sortable.js b/Sortable.js index 6f1d4f0..c6295b5 100644 --- a/Sortable.js +++ b/Sortable.js @@ -187,7 +187,8 @@ dataTransfer.setData('Text', dragEl.textContent); }, dropBubble: false, - dragoverBubble: false + dragoverBubble: false, + dataIdAttr: 'data-id' }; @@ -726,12 +727,13 @@ el, children = this.el.children, i = 0, - n = children.length; + n = children.length, + options = this.options; for (; i < n; i++) { el = children[i]; - if (_closest(el, this.options.draggable, this.el)) { - order.push(el.getAttribute('data-id') || _generateId(el)); + if (_closest(el, options.draggable, this.el)) { + order.push(el.getAttribute(options.dataAttr) || _generateId(el)); } } From 8b3e8f949710d3360c92b8fdc2d9f3fcd58d7e30 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Mon, 16 Feb 2015 18:06:58 +0300 Subject: [PATCH 03/27] #279: * dataAttr -> dataIdAttr --- Sortable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sortable.js b/Sortable.js index c6295b5..1b054f7 100644 --- a/Sortable.js +++ b/Sortable.js @@ -733,7 +733,7 @@ for (; i < n; i++) { el = children[i]; if (_closest(el, options.draggable, this.el)) { - order.push(el.getAttribute(options.dataAttr) || _generateId(el)); + order.push(el.getAttribute(options.dataIdAttr) || _generateId(el)); } } From 1482449b1816b7bdce9f96be51c47000b9a73453 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Thu, 19 Feb 2015 21:20:16 +0300 Subject: [PATCH 04/27] #285: * fixed dragover handler --- Sortable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sortable.js b/Sortable.js index 1b054f7..a3cb20e 100644 --- a/Sortable.js +++ b/Sortable.js @@ -489,7 +489,7 @@ isOwner = (activeGroup === group), canSort = options.sort; - if (evt.dataTransfer && evt.dataTransfer.effectAllowed !== 'move') { + if (!dragEl) { return; } From c7257ae198ba136ebec55b9cfae760f9e89c4e9a Mon Sep 17 00:00:00 2001 From: Bogdan Mustiata Date: Tue, 3 Mar 2015 14:53:15 +0100 Subject: [PATCH 05/27] Don't join class names when removing classes after additions. --- Sortable.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Sortable.js b/Sortable.js index a3cb20e..e7bb2fe 100644 --- a/Sortable.js +++ b/Sortable.js @@ -896,7 +896,10 @@ el.classList[state ? 'add' : 'remove'](name); } else { - var className = (' ' + el.className + ' ').replace(/\s+/g, ' ').replace(' ' + name + ' ', ''); + var className = (' ' + el.className + ' ') + .replace(/\s+/g, ' ') + .replace(' ' + name + ' ', ' ') + .replace(/ /, ' '); el.className = className + (state ? ' ' + name : ''); } } From 3738fa8db47daa74ea14aad564dc5796664b4721 Mon Sep 17 00:00:00 2001 From: srosengren Date: Wed, 4 Mar 2015 14:59:58 +0100 Subject: [PATCH 06/27] Added bindinghandlers for KnockoutJS --- knockout-sortable.js | 158 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 knockout-sortable.js diff --git a/knockout-sortable.js b/knockout-sortable.js new file mode 100644 index 0000000..0a97d0a --- /dev/null +++ b/knockout-sortable.js @@ -0,0 +1,158 @@ +(function () { + + var init = function (element, valueAccessor, allBindings, viewModel, bindingContext, sortableOptions) { + var options = buildOptions(valueAccessor, sortableOptions); + + //It's seems that we cannot update the eventhandlers after we've created the sortable, so define them in init instead of update + ['onStart', 'onEnd', 'onRemove', 'onAdd', 'onUpdate', 'onSort', 'onFilter'].forEach(function (e) { + if (options[e] || eventHandlers[e]) + options[e] = function (eventType, parentVM, parentBindings, handler, e) { + var itemVM = ko.dataFor(e.item), + //All of the bindings on the parent element + bindings = ko.utils.peekObservable(parentBindings()), + //The binding options for the draggable/sortable binding of the parent element + bindingHandlerBinding = bindings.sortable || bindings.draggable, + //The collection that we should modify + collection = bindingHandlerBinding.collection || bindingHandlerBinding.foreach; + if (handler) + handler(e, itemVM, parentVM, collection, bindings); + if (eventHandlers[eventType]) + eventHandlers[eventType](e, itemVM, parentVM, collection, bindings); + }.bind(undefined, e, viewModel, allBindings, options[e]); + }); + + viewModel._sortable = Sortable.create(element, options); + + //Destroy the sortable if knockout disposes the element it's connected to + ko.utils.domNodeDisposal.addDisposeCallback(element, function () { + viewModel._sortable.destroy(); + }); + return ko.bindingHandlers['template']['init'](element, valueAccessor); + }, + update = function (element, valueAccessor, allBindings, viewModel, bindingContext, sortableOptions) { + + //There seems to be some problems with updating the options of a sortable + //Tested to change eventhandlers and the group options without any luck + + return ko.bindingHandlers['template']['update'](element, valueAccessor, allBindings, viewModel, bindingContext); + }, + eventHandlers = (function (handlers) { + + var moveOperations = [], + tryMoveOperation = function (e, itemVM, parentVM, collection, parentBindings) { + //A move operation is the combination of a add and remove event, this is to make sure that we have both the target and origin collections + var currentOperation = { event: e, itemVM: itemVM, parentVM: parentVM, collection: collection, parentBindings: parentBindings }, + existingOperation = moveOperations.filter(function (op) { + return op.itemVM === currentOperation.itemVM; + })[0]; + + if (!existingOperation) { + moveOperations.push(currentOperation); + } + else { + //We're finishing the operation and already have a handle on the operation item meaning that it's safe to remove it + moveOperations.splice(moveOperations.indexOf(existingOperation), 1); + + var removeOperation = currentOperation.event.type === 'remove' ? currentOperation : existingOperation, + addOperation = currentOperation.event.type === 'add' ? currentOperation : existingOperation; + + moveItem(itemVM, removeOperation.collection, addOperation.collection, addOperation.event.clone, addOperation.event); + } + }, + //Moves an item from the to (collection to from (collection), these can be references to the same collection which means it's a sort, + //clone indicates if we should move or copy the item into the new collection + moveItem = function (itemVM, from, to, clone, e) { + //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); + + //Remove sortables "unbound" element + e.item.parentNode.removeChild(e.item); + + //This splice is necessary for both clone and move/sort + //In sort/move since it shouldn't be at this index/in this array anymore + //In clone since we have to work around knockouts valuHasMutated when manipulating arrays and avoid a "unbound" item added by sortable + fromArray.splice(originalIndex, 1); + //Update the array, this will also remove sortables "unbound" clone + from.valueHasMutated(); + if (clone && from !== to) { + //Readd the item + fromArray.splice(originalIndex, 0, itemVM); + //Force knockout to update + from.valueHasMutated(); + } + //Insert the item on its new position + to().splice(e.newIndex, 0, itemVM); + //Make sure to tell knockout that we've modified the actual array. + to.valueHasMutated(); + }; + + handlers.onRemove = tryMoveOperation; + handlers.onAdd = tryMoveOperation; + handlers.onUpdate = function (e, itemVM, parentVM, collection, parentBindings) { + //This will be performed as a sort since the to/from collections reference the same collection and clone is set to false + moveItem(itemVM, collection, collection, false, e); + } + + return handlers; + })({}), + //bindingOptions are the options set in the "data-bind" attribute in the ui. + //options are custom options, for instance draggable/sortable specific options + buildOptions = function (bindingOptions, options) { + //deep clone/copy of properties from the "from" argument onto the "into" argument and returns the modified "into" + var merge = function (into, from) { + for (var prop in from) { + if (Object.prototype.toString.call(from[prop]) === '[object Object]') { + if (Object.prototype.toString.call(into[prop]) !== '[object Object]') { + into[prop] = {}; + } + into[prop] = merge(into[prop], from[prop]); + } + else + into[prop] = from[prop]; + } + + return into; + }, + //unwrap the supplied options + unwrappedOptions = ko.utils.peekObservable(bindingOptions()).options || {}, + //Make sure that we don't modify the provided settings object + options = merge({}, options); + + //group is handled differently since we should both allow to change a draggable to a sortable (and vice versa), + //but still be able to set a name on a draggable without it becoming a drop target. + if (unwrappedOptions.group && Object.prototype.toString.call(unwrappedOptions.group) !== '[object Object]') { + //group property is a name string declaration, convert to object. + unwrappedOptions.group = { name: unwrappedOptions.group }; + } + + return merge(options, unwrappedOptions); + }; + + ko.bindingHandlers.draggable = { + sortableOptions: { + group: { pull: 'clone', put: false }, + sort: false + }, + init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { + return init(element, valueAccessor, allBindings, viewModel, bindingContext, ko.bindingHandlers.draggable.sortableOptions); + }, + update: function (element, valueAccessor, allBindings, viewModel, bindingContext) { + return update(element, valueAccessor, allBindings, viewModel, bindingContext, ko.bindingHandlers.draggable.sortableOptions); + } + }; + + ko.bindingHandlers.sortable = { + sortableOptions: { + group: { pull: true, put: true } + }, + init: function (element, valueAccessor, allBindings, viewModel, bindingContext) { + return init(element, valueAccessor, allBindings, viewModel, bindingContext, ko.bindingHandlers.sortable.sortableOptions); + }, + update: function (element, valueAccessor, allBindings, viewModel, bindingContext) { + return update(element, valueAccessor, allBindings, viewModel, bindingContext, ko.bindingHandlers.sortable.sortableOptions); + } + } + +})(); \ No newline at end of file From 2920836d5c2ff372a03869c1c449a2621f1fa83c Mon Sep 17 00:00:00 2001 From: srosengren Date: Wed, 4 Mar 2015 15:12:00 +0100 Subject: [PATCH 07/27] Ran grunt after adding knockoutjs bindinghandlers --- Sortable.min.js | 2 +- knockout-sortable.js | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Sortable.min.js b/Sortable.min.js index 05f8468..4a75c2c 100644 --- a/Sortable.min.js +++ b/Sortable.min.js @@ -1,2 +1,2 @@ /*! Sortable 1.0.1 - MIT | git://github.com/rubaxa/Sortable.git */ -!function(a){"use strict";"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a():"undefined"!=typeof Package?Sortable=a():window.Sortable=a()}(function(){"use strict";function a(a,b){this.el=a,this.options=b=b||{};var d={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(a.nodeName)?"li":">*",ghostClass:"sortable-ghost",ignore:"a, img",filter:null,animation:0,setData:function(a,b){a.setData("Text",b.textContent)},dropBubble:!1,dragoverBubble:!1};for(var e in d)!(e in b)&&(b[e]=d[e]);var g=b.group;g&&"object"==typeof g||(g=b.group={name:g}),["pull","put"].forEach(function(a){a in g||(g[a]=!0)}),L.forEach(function(d){b[d]=c(this,b[d]||M),f(a,d.substr(2).toLowerCase(),b[d])},this),a[E]=g.name+" "+(g.put.join?g.put.join(" "):"");for(var h in this)"_"===h.charAt(0)&&(this[h]=c(this,this[h]));f(a,"mousedown",this._onTapStart),f(a,"touchstart",this._onTapStart),f(a,"dragover",this._onDragOver),f(a,"dragenter",this._onDragOver),P.push(this._onDragOver),b.store&&this.sort(b.store.get(this))}function b(a){s&&s.state!==a&&(i(s,"display",a?"none":""),!a&&s.state&&t.insertBefore(s,q),s.state=a)}function c(a,b){var c=O.call(arguments,2);return b.bind?b.bind.apply(b,[a].concat(c)):function(){return b.apply(a,c.concat(O.call(arguments)))}}function d(a,b,c){if(a){c=c||G,b=b.split(".");var d=b.shift().toUpperCase(),e=new RegExp("\\s("+b.join("|")+")\\s","g");do if(">*"===d&&a.parentNode===c||(""===d||a.nodeName.toUpperCase()==d)&&(!b.length||((" "+a.className+" ").match(e)||[]).length==b.length))return a;while(a!==c&&(a=a.parentNode))}return null}function e(a){a.dataTransfer.dropEffect="move",a.preventDefault()}function f(a,b,c){a.addEventListener(b,c,!1)}function g(a,b,c){a.removeEventListener(b,c,!1)}function h(a,b,c){if(a)if(a.classList)a.classList[c?"add":"remove"](b);else{var d=(" "+a.className+" ").replace(/\s+/g," ").replace(" "+b+" ","");a.className=d+(c?" "+b:"")}}function i(a,b,c){var d=a&&a.style;if(d){if(void 0===c)return G.defaultView&&G.defaultView.getComputedStyle?c=G.defaultView.getComputedStyle(a,""):a.currentStyle&&(c=a.currentStyle),void 0===b?c:c[b];b in d||(b="-webkit-"+b),d[b]=c+("string"==typeof c?"":"px")}}function j(a,b,c){if(a){var d=a.getElementsByTagName(b),e=0,f=d.length;if(c)for(;f>e;e++)c(d[e],e);return d}return[]}function k(a){a.draggable=!1}function l(){J=!1}function m(a,b){var c=a.lastElementChild,d=c.getBoundingClientRect();return b.clientY-(d.top+d.height)>5&&c}function n(a){for(var b=a.tagName+a.className+a.src+a.href+a.textContent,c=b.length,d=0;c--;)d+=b.charCodeAt(c);return d.toString(36)}function o(a){for(var b=0;a&&(a=a.previousElementSibling);)"TEMPLATE"!==a.nodeName.toUpperCase()&&b++;return b}function p(a,b){var c,d;return function(){void 0===c&&(c=arguments,d=this,setTimeout(function(){1===c.length?a.call(d,c[0]):a.apply(d,c),c=void 0},b))}}var q,r,s,t,u,v,w,x,y,z,A,B,C,D={},E="Sortable"+(new Date).getTime(),F=window,G=F.document,H=F.parseInt,I=!!("draggable"in G.createElement("div")),J=!1,K=function(a,b,c,d,e,f){var g=G.createEvent("Event");g.initEvent(b,!0,!0),g.item=c||a,g.from=d||a,g.clone=s,g.oldIndex=e,g.newIndex=f,a.dispatchEvent(g)},L="onAdd onUpdate onRemove onStart onEnd onFilter onSort".split(" "),M=function(){},N=Math.abs,O=[].slice,P=[];return a.prototype={constructor:a,_dragStarted:function(){h(q,this.options.ghostClass,!0),a.active=this,K(t,"start",q,t,y)},_onTapStart:function(a){var b=a.type,c=a.touches&&a.touches[0],e=(c||a).target,g=e,h=this.options,i=this.el,l=h.filter;if(!("mousedown"===b&&0!==a.button||h.disabled)){if(h.handle&&(e=d(e,h.handle,i)),e=d(e,h.draggable,i),y=o(e),"function"==typeof l){if(l.call(this,a,e,this))return K(g,"filter",e,i,y),void a.preventDefault()}else if(l&&(l=l.split(",").some(function(a){return a=d(g,a.trim(),i),a?(K(a,"filter",e,i,y),!0):void 0})))return void a.preventDefault();if(e&&!q&&e.parentNode===i){B=a,t=this.el,q=e,v=q.nextSibling,A=this.options.group,q.draggable=!0,h.ignore.split(",").forEach(function(a){j(e,a.trim(),k)}),c&&(B={target:e,clientX:c.clientX,clientY:c.clientY},this._onDragStart(B,"touch"),a.preventDefault()),f(G,"mouseup",this._onDrop),f(G,"touchend",this._onDrop),f(G,"touchcancel",this._onDrop),f(q,"dragend",this),f(t,"dragstart",this._onDragStart),f(G,"dragover",this),I||(f(G,"mousemove",this),this._onDragStart(B,!0));try{G.selection?G.selection.empty():window.getSelection().removeAllRanges()}catch(m){}}}},_emulateDragOver:function(){if(C){i(r,"display","none");var a=G.elementFromPoint(C.clientX,C.clientY),b=a,c=this.options.group.name,d=P.length;if(b)do{if((" "+b[E]+" ").indexOf(c)>-1){for(;d--;)P[d]({clientX:C.clientX,clientY:C.clientY,target:a,rootEl:b});break}a=b}while(b=b.parentNode);i(r,"display","")}},_onTouchMove:function(a){if(B){var b=a.touches?a.touches[0]:a,c=b.clientX-B.clientX,d=b.clientY-B.clientY,e=a.touches?"translate3d("+c+"px,"+d+"px,0)":"translate("+c+"px,"+d+"px)";C=b,i(r,"webkitTransform",e),i(r,"mozTransform",e),i(r,"msTransform",e),i(r,"transform",e),this._onDrag(b),a.preventDefault()}},_onDragStart:function(a,b){var c=a.dataTransfer,d=this.options;if(this._offUpEvents(),"clone"==A.pull&&(s=q.cloneNode(!0),i(s,"display","none"),t.insertBefore(s,q)),b){var e,g=q.getBoundingClientRect(),h=i(q);r=q.cloneNode(!0),i(r,"top",g.top-H(h.marginTop,10)),i(r,"left",g.left-H(h.marginLeft,10)),i(r,"width",g.width),i(r,"height",g.height),i(r,"opacity","0.8"),i(r,"position","fixed"),i(r,"zIndex","100000"),t.appendChild(r),e=r.getBoundingClientRect(),i(r,"width",2*g.width-e.width),i(r,"height",2*g.height-e.height),"touch"===b?(f(G,"touchmove",this._onTouchMove),f(G,"touchend",this._onDrop),f(G,"touchcancel",this._onDrop)):(f(G,"mousemove",this._onTouchMove),f(G,"mouseup",this._onDrop)),this._loopId=setInterval(this._emulateDragOver,150)}else c&&(c.effectAllowed="move",d.setData&&d.setData.call(this,c,q)),f(G,"drop",this);if(u=d.scroll,u===!0){u=t;do if(u.offsetWidth=i-g)-(e>=g),l=(e>=j-h)-(e>=h);k||l?b=F:u&&(b=u,c=u.getBoundingClientRect(),k=(N(c.right-g)<=e)-(N(c.left-g)<=e),l=(N(c.bottom-h)<=e)-(N(c.top-h)<=e)),(D.vx!==k||D.vy!==l||D.el!==b)&&(D.el=b,D.vx=k,D.vy=l,clearInterval(D.pid),b&&(D.pid=setInterval(function(){b===F?F.scrollTo(F.scrollX+k*f,F.scrollY+l*f):(l&&(b.scrollTop+=l*f),k&&(b.scrollLeft+=k*f))},24)))}},30),_onDragOver:function(a){var c,e,f,g=this.el,h=this.options,j=h.group,k=j.put,n=A===j,o=h.sort;if(void 0!==a.preventDefault&&(a.preventDefault(),!h.dragoverBubble&&a.stopPropagation()),!J&&A&&!h.disabled&&(n?o||(f=!t.contains(q)):A.pull&&k&&(A.name===j.name||k.indexOf&&~k.indexOf(A.name)))&&(void 0===a.rootEl||a.rootEl===this.el)){if(c=d(a.target,h.draggable,g),e=q.getBoundingClientRect(),f)return b(!0),void(s||v?t.insertBefore(q,s||v):o||t.appendChild(q));if(0===g.children.length||g.children[0]===r||g===a.target&&(c=m(g,a))){if(c){if(c.animated)return;u=c.getBoundingClientRect()}b(n),g.appendChild(q),this._animate(e,q),c&&this._animate(u,c)}else if(c&&!c.animated&&c!==q&&void 0!==c.parentNode[E]){w!==c&&(w=c,x=i(c));var p,u=c.getBoundingClientRect(),y=u.right-u.left,z=u.bottom-u.top,B=/left|right|inline/.test(x.cssFloat+x.display),C=c.offsetWidth>q.offsetWidth,D=c.offsetHeight>q.offsetHeight,F=(B?(a.clientX-u.left)/y:(a.clientY-u.top)/z)>.5,G=c.nextElementSibling;J=!0,setTimeout(l,30),b(n),p=B?c.previousElementSibling===q&&!C||F&&C:G!==q&&!D||F&&D,p&&!G?g.appendChild(q):c.parentNode.insertBefore(q,p?G:c),this._animate(e,q),this._animate(u,c)}}},_animate:function(a,b){var c=this.options.animation;if(c){var d=b.getBoundingClientRect();i(b,"transition","none"),i(b,"transform","translate3d("+(a.left-d.left)+"px,"+(a.top-d.top)+"px,0)"),b.offsetWidth,i(b,"transition","all "+c+"ms"),i(b,"transform","translate3d(0,0,0)"),clearTimeout(b.animated),b.animated=setTimeout(function(){i(b,"transition",""),b.animated=!1},c)}},_offUpEvents:function(){g(G,"mouseup",this._onDrop),g(G,"touchmove",this._onTouchMove),g(G,"touchend",this._onDrop),g(G,"touchcancel",this._onDrop)},_onDrop:function(b){var c=this.el,d=this.options;clearInterval(this._loopId),clearInterval(D.pid),g(G,"drop",this),g(G,"dragover",this),g(c,"dragstart",this._onDragStart),this._offUpEvents(),b&&(b.preventDefault(),!d.dropBubble&&b.stopPropagation(),r&&r.parentNode.removeChild(r),q&&(g(q,"dragend",this),k(q),h(q,this.options.ghostClass,!1),t!==q.parentNode?(z=o(q),K(q.parentNode,"sort",q,t,y,z),K(t,"sort",q,t,y,z),K(q,"add",q,t,y,z),K(t,"remove",q,t,y,z)):(s&&s.parentNode.removeChild(s),q.nextSibling!==v&&(z=o(q),K(t,"update",q,t,y,z),K(t,"sort",q,t,y,z))),a.active&&K(t,"end",q,t,y,z)),t=q=r=v=s=B=C=w=x=A=a.active=null,this.save())},handleEvent:function(a){var b=a.type;"dragover"===b?(this._onDrag(a),e(a)):("drop"===b||"dragend"===b)&&this._onDrop(a)},toArray:function(){for(var a,b=[],c=this.el.children,e=0,f=c.length;f>e;e++)a=c[e],d(a,this.options.draggable,this.el)&&b.push(a.getAttribute("data-id")||n(a));return b},sort:function(a){var b={},c=this.el;this.toArray().forEach(function(a,e){var f=c.children[e];d(f,this.options.draggable,c)&&(b[a]=f)},this),a.forEach(function(a){b[a]&&(c.removeChild(b[a]),c.appendChild(b[a]))})},save:function(){var a=this.options.store;a&&a.set(this)},closest:function(a,b){return d(a,b||this.options.draggable,this.el)},option:function(a,b){var c=this.options;return void 0===b?c[a]:void(c[a]=b)},destroy:function(){var a=this.el,b=this.options;L.forEach(function(c){g(a,c.substr(2).toLowerCase(),b[c])}),g(a,"mousedown",this._onTapStart),g(a,"touchstart",this._onTapStart),g(a,"dragover",this._onDragOver),g(a,"dragenter",this._onDragOver),Array.prototype.forEach.call(a.querySelectorAll("[draggable]"),function(a){a.removeAttribute("draggable")}),P.splice(P.indexOf(this._onDragOver),1),this._onDrop(),this.el=null}},a.utils={on:f,off:g,css:i,find:j,bind:c,is:function(a,b){return!!d(a,b,a)},throttle:p,closest:d,toggleClass:h,dispatchEvent:K,index:o},a.version="1.0.1",a.create=function(b,c){return new a(b,c)},a}); \ No newline at end of file +!function(a){"use strict";"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a():"undefined"!=typeof Package?Sortable=a():window.Sortable=a()}(function(){"use strict";function a(a,b){this.el=a,this.options=b=b||{};var d={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(a.nodeName)?"li":">*",ghostClass:"sortable-ghost",ignore:"a, img",filter:null,animation:0,setData:function(a,b){a.setData("Text",b.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id"};for(var e in d)!(e in b)&&(b[e]=d[e]);var g=b.group;g&&"object"==typeof g||(g=b.group={name:g}),["pull","put"].forEach(function(a){a in g||(g[a]=!0)}),M.forEach(function(d){b[d]=c(this,b[d]||N),f(a,d.substr(2).toLowerCase(),b[d])},this),b.groups=" "+g.name+(g.put.join?" "+g.put.join(" "):"")+" ",a[F]=b;for(var h in this)"_"===h.charAt(0)&&(this[h]=c(this,this[h]));f(a,"mousedown",this._onTapStart),f(a,"touchstart",this._onTapStart),f(a,"dragover",this),f(a,"dragenter",this),Q.push(this._onDragOver),b.store&&this.sort(b.store.get(this))}function b(a){s&&s.state!==a&&(i(s,"display",a?"none":""),!a&&s.state&&t.insertBefore(s,q),s.state=a)}function c(a,b){var c=P.call(arguments,2);return b.bind?b.bind.apply(b,[a].concat(c)):function(){return b.apply(a,c.concat(P.call(arguments)))}}function d(a,b,c){if(a){c=c||H,b=b.split(".");var d=b.shift().toUpperCase(),e=new RegExp("\\s("+b.join("|")+")\\s","g");do if(">*"===d&&a.parentNode===c||(""===d||a.nodeName.toUpperCase()==d)&&(!b.length||((" "+a.className+" ").match(e)||[]).length==b.length))return a;while(a!==c&&(a=a.parentNode))}return null}function e(a){a.dataTransfer.dropEffect="move",a.preventDefault()}function f(a,b,c){a.addEventListener(b,c,!1)}function g(a,b,c){a.removeEventListener(b,c,!1)}function h(a,b,c){if(a)if(a.classList)a.classList[c?"add":"remove"](b);else{var d=(" "+a.className+" ").replace(/\s+/g," ").replace(" "+b+" ","");a.className=d+(c?" "+b:"")}}function i(a,b,c){var d=a&&a.style;if(d){if(void 0===c)return H.defaultView&&H.defaultView.getComputedStyle?c=H.defaultView.getComputedStyle(a,""):a.currentStyle&&(c=a.currentStyle),void 0===b?c:c[b];b in d||(b="-webkit-"+b),d[b]=c+("string"==typeof c?"":"px")}}function j(a,b,c){if(a){var d=a.getElementsByTagName(b),e=0,f=d.length;if(c)for(;f>e;e++)c(d[e],e);return d}return[]}function k(a){a.draggable=!1}function l(){K=!1}function m(a,b){var c=a.lastElementChild,d=c.getBoundingClientRect();return b.clientY-(d.top+d.height)>5&&c}function n(a){for(var b=a.tagName+a.className+a.src+a.href+a.textContent,c=b.length,d=0;c--;)d+=b.charCodeAt(c);return d.toString(36)}function o(a){for(var b=0;a&&(a=a.previousElementSibling);)"TEMPLATE"!==a.nodeName.toUpperCase()&&b++;return b}function p(a,b){var c,d;return function(){void 0===c&&(c=arguments,d=this,setTimeout(function(){1===c.length?a.call(d,c[0]):a.apply(d,c),c=void 0},b))}}var q,r,s,t,u,v,w,x,y,z,A,B,C,D,E={},F="Sortable"+(new Date).getTime(),G=window,H=G.document,I=G.parseInt,J=!!("draggable"in H.createElement("div")),K=!1,L=function(a,b,c,d,e,f){var g=H.createEvent("Event");g.initEvent(b,!0,!0),g.item=c||a,g.from=d||a,g.clone=s,g.oldIndex=e,g.newIndex=f,a.dispatchEvent(g)},M="onAdd onUpdate onRemove onStart onEnd onFilter onSort".split(" "),N=function(){},O=Math.abs,P=[].slice,Q=[],R=p(function(a,b,c){if(c&&b.scroll){var d,e,f,g,h=b.scrollSensitivity,i=b.scrollSpeed,j=a.clientX,k=a.clientY,l=window.innerWidth,m=window.innerHeight;if(w!==c&&(v=b.scroll,w=c,v===!0)){v=c;do if(v.offsetWidth=l-j)-(h>=j),g=(h>=m-k)-(h>=k),(f||g)&&(d=G)),(E.vx!==f||E.vy!==g||E.el!==d)&&(E.el=d,E.vx=f,E.vy=g,clearInterval(E.pid),d&&(E.pid=setInterval(function(){d===G?G.scrollTo(G.scrollX+f*i,G.scrollY+g*i):(g&&(d.scrollTop+=g*i),f&&(d.scrollLeft+=f*i))},24)))}},30);return a.prototype={constructor:a,_dragStarted:function(){t&&q&&(h(q,this.options.ghostClass,!0),a.active=this,L(t,"start",q,t,z))},_onTapStart:function(a){var b=a.type,c=a.touches&&a.touches[0],e=(c||a).target,g=e,h=this.options,i=this.el,l=h.filter;if(!("mousedown"===b&&0!==a.button||h.disabled)&&(e=d(e,h.draggable,i))){if(z=o(e),"function"==typeof l){if(l.call(this,a,e,this))return L(g,"filter",e,i,z),void a.preventDefault()}else if(l&&(l=l.split(",").some(function(a){return a=d(g,a.trim(),i),a?(L(a,"filter",e,i,z),!0):void 0})))return void a.preventDefault();if((!h.handle||d(g,h.handle,i))&&e&&!q&&e.parentNode===i){C=a,t=this.el,q=e,u=q.nextSibling,B=this.options.group,q.draggable=!0,h.ignore.split(",").forEach(function(a){j(e,a.trim(),k)}),c&&(C={target:e,clientX:c.clientX,clientY:c.clientY},this._onDragStart(C,"touch"),a.preventDefault()),f(H,"mouseup",this._onDrop),f(H,"touchend",this._onDrop),f(H,"touchcancel",this._onDrop),f(q,"dragend",this),f(t,"dragstart",this._onDragStart),J||this._onDragStart(C,!0);try{H.selection?H.selection.empty():window.getSelection().removeAllRanges()}catch(m){}}}},_emulateDragOver:function(){if(D){i(r,"display","none");var a=H.elementFromPoint(D.clientX,D.clientY),b=a,c=" "+this.options.group.name,d=Q.length;if(b)do{if(b[F]&&b[F].groups.indexOf(c)>-1){for(;d--;)Q[d]({clientX:D.clientX,clientY:D.clientY,target:a,rootEl:b});break}a=b}while(b=b.parentNode);i(r,"display","")}},_onTouchMove:function(a){if(C){var b=a.touches?a.touches[0]:a,c=b.clientX-C.clientX,d=b.clientY-C.clientY,e=a.touches?"translate3d("+c+"px,"+d+"px,0)":"translate("+c+"px,"+d+"px)";D=b,i(r,"webkitTransform",e),i(r,"mozTransform",e),i(r,"msTransform",e),i(r,"transform",e),a.preventDefault()}},_onDragStart:function(a,b){var c=a.dataTransfer,d=this.options;if(this._offUpEvents(),"clone"==B.pull&&(s=q.cloneNode(!0),i(s,"display","none"),t.insertBefore(s,q)),b){var e,g=q.getBoundingClientRect(),h=i(q);r=q.cloneNode(!0),i(r,"top",g.top-I(h.marginTop,10)),i(r,"left",g.left-I(h.marginLeft,10)),i(r,"width",g.width),i(r,"height",g.height),i(r,"opacity","0.8"),i(r,"position","fixed"),i(r,"zIndex","100000"),t.appendChild(r),e=r.getBoundingClientRect(),i(r,"width",2*g.width-e.width),i(r,"height",2*g.height-e.height),"touch"===b?(f(H,"touchmove",this._onTouchMove),f(H,"touchend",this._onDrop),f(H,"touchcancel",this._onDrop)):(f(H,"mousemove",this._onTouchMove),f(H,"mouseup",this._onDrop)),this._loopId=setInterval(this._emulateDragOver,150)}else c&&(c.effectAllowed="move",d.setData&&d.setData.call(this,c,q)),f(H,"drop",this);setTimeout(this._dragStarted,0)},_onDragOver:function(a){var c,e,f,g=this.el,h=this.options,j=h.group,k=j.put,n=B===j,o=h.sort;if(q&&(void 0!==a.preventDefault&&(a.preventDefault(),!h.dragoverBubble&&a.stopPropagation()),B&&!h.disabled&&(n?o||(f=!t.contains(q)):B.pull&&k&&(B.name===j.name||k.indexOf&&~k.indexOf(B.name)))&&(void 0===a.rootEl||a.rootEl===this.el))){if(R(a,h,this.el),K)return;if(c=d(a.target,h.draggable,g),e=q.getBoundingClientRect(),f)return b(!0),void(s||u?t.insertBefore(q,s||u):o||t.appendChild(q));if(0===g.children.length||g.children[0]===r||g===a.target&&(c=m(g,a))){if(c){if(c.animated)return;v=c.getBoundingClientRect()}b(n),g.appendChild(q),this._animate(e,q),c&&this._animate(v,c)}else if(c&&!c.animated&&c!==q&&void 0!==c.parentNode[F]){x!==c&&(x=c,y=i(c));var p,v=c.getBoundingClientRect(),w=v.right-v.left,z=v.bottom-v.top,A=/left|right|inline/.test(y.cssFloat+y.display),C=c.offsetWidth>q.offsetWidth,D=c.offsetHeight>q.offsetHeight,E=(A?(a.clientX-v.left)/w:(a.clientY-v.top)/z)>.5,G=c.nextElementSibling;K=!0,setTimeout(l,30),b(n),p=A?c.previousElementSibling===q&&!C||E&&C:G!==q&&!D||E&&D,p&&!G?g.appendChild(q):c.parentNode.insertBefore(q,p?G:c),this._animate(e,q),this._animate(v,c)}}},_animate:function(a,b){var c=this.options.animation;if(c){var d=b.getBoundingClientRect();i(b,"transition","none"),i(b,"transform","translate3d("+(a.left-d.left)+"px,"+(a.top-d.top)+"px,0)"),b.offsetWidth,i(b,"transition","all "+c+"ms"),i(b,"transform","translate3d(0,0,0)"),clearTimeout(b.animated),b.animated=setTimeout(function(){i(b,"transition",""),i(b,"transform",""),b.animated=!1},c)}},_offUpEvents:function(){g(H,"mouseup",this._onDrop),g(H,"touchmove",this._onTouchMove),g(H,"touchend",this._onDrop),g(H,"touchcancel",this._onDrop)},_onDrop:function(b){var c=this.el,d=this.options;clearInterval(this._loopId),clearInterval(E.pid),g(H,"drop",this),g(H,"mousemove",this._onTouchMove),g(c,"dragstart",this._onDragStart),this._offUpEvents(),b&&(b.preventDefault(),!d.dropBubble&&b.stopPropagation(),r&&r.parentNode.removeChild(r),q&&(g(q,"dragend",this),k(q),h(q,this.options.ghostClass,!1),t!==q.parentNode?(A=o(q),L(q.parentNode,"sort",q,t,z,A),L(t,"sort",q,t,z,A),L(q,"add",q,t,z,A),L(t,"remove",q,t,z,A)):(s&&s.parentNode.removeChild(s),q.nextSibling!==u&&(A=o(q),L(t,"update",q,t,z,A),L(t,"sort",q,t,z,A))),a.active&&L(t,"end",q,t,z,A)),t=q=r=u=s=v=w=C=D=x=y=B=a.active=null,this.save())},handleEvent:function(a){var b=a.type;"dragover"===b||"dragenter"===b?(this._onDragOver(a),e(a)):("drop"===b||"dragend"===b)&&this._onDrop(a)},toArray:function(){for(var a,b=[],c=this.el.children,e=0,f=c.length,g=this.options;f>e;e++)a=c[e],d(a,g.draggable,this.el)&&b.push(a.getAttribute(g.dataIdAttr)||n(a));return b},sort:function(a){var b={},c=this.el;this.toArray().forEach(function(a,e){var f=c.children[e];d(f,this.options.draggable,c)&&(b[a]=f)},this),a.forEach(function(a){b[a]&&(c.removeChild(b[a]),c.appendChild(b[a]))})},save:function(){var a=this.options.store;a&&a.set(this)},closest:function(a,b){return d(a,b||this.options.draggable,this.el)},option:function(a,b){var c=this.options;return void 0===b?c[a]:void(c[a]=b)},destroy:function(){var a=this.el,b=this.options;M.forEach(function(c){g(a,c.substr(2).toLowerCase(),b[c])}),g(a,"mousedown",this._onTapStart),g(a,"touchstart",this._onTapStart),g(a,"dragover",this),g(a,"dragenter",this),Array.prototype.forEach.call(a.querySelectorAll("[draggable]"),function(a){a.removeAttribute("draggable")}),Q.splice(Q.indexOf(this._onDragOver),1),this._onDrop(),this.el=null}},a.utils={on:f,off:g,css:i,find:j,bind:c,is:function(a,b){return!!d(a,b,a)},throttle:p,closest:d,toggleClass:h,dispatchEvent:L,index:o},a.version="1.0.1",a.create=function(b,c){return new a(b,c)},a}); \ No newline at end of file diff --git a/knockout-sortable.js b/knockout-sortable.js index 0a97d0a..6a7f701 100644 --- a/knockout-sortable.js +++ b/knockout-sortable.js @@ -1,6 +1,8 @@ (function () { + "use strict"; var init = function (element, valueAccessor, allBindings, viewModel, bindingContext, sortableOptions) { + var options = buildOptions(valueAccessor, sortableOptions); //It's seems that we cannot update the eventhandlers after we've created the sortable, so define them in init instead of update @@ -27,14 +29,14 @@ ko.utils.domNodeDisposal.addDisposeCallback(element, function () { viewModel._sortable.destroy(); }); - return ko.bindingHandlers['template']['init'](element, valueAccessor); + return ko.bindingHandlers.template.init(element, valueAccessor); }, update = function (element, valueAccessor, allBindings, viewModel, bindingContext, sortableOptions) { //There seems to be some problems with updating the options of a sortable //Tested to change eventhandlers and the group options without any luck - return ko.bindingHandlers['template']['update'](element, valueAccessor, allBindings, viewModel, bindingContext); + return ko.bindingHandlers.template.update(element, valueAccessor, allBindings, viewModel, bindingContext); }, eventHandlers = (function (handlers) { @@ -93,7 +95,7 @@ handlers.onUpdate = function (e, itemVM, parentVM, collection, parentBindings) { //This will be performed as a sort since the to/from collections reference the same collection and clone is set to false moveItem(itemVM, collection, collection, false, e); - } + }; return handlers; })({}), @@ -116,7 +118,8 @@ return into; }, //unwrap the supplied options - unwrappedOptions = ko.utils.peekObservable(bindingOptions()).options || {}, + unwrappedOptions = ko.utils.peekObservable(bindingOptions()).options || {}; + //Make sure that we don't modify the provided settings object options = merge({}, options); @@ -153,6 +156,6 @@ update: function (element, valueAccessor, allBindings, viewModel, bindingContext) { return update(element, valueAccessor, allBindings, viewModel, bindingContext, ko.bindingHandlers.sortable.sortableOptions); } - } + }; })(); \ No newline at end of file From 6928b193ab9d571c78a34550e71b9c4712f9dae6 Mon Sep 17 00:00:00 2001 From: Sebastian Rosengren Date: Wed, 4 Mar 2015 21:35:34 +0100 Subject: [PATCH 08/27] Added KnockoutJS description to README --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 1c1867d..0e949de 100644 --- a/README.md +++ b/README.md @@ -387,6 +387,32 @@ React.render(
--- + +### Support KnockoutJS +Include [knockout-sortable.js](knockout-sortable.js) + +```html +
+ +
+ +
+ +
+``` + +Using this bindingHandler sorts the observableArray when the user sorts the HTMLElements. + +The sortable/draggable bindingHandlers supports the same syntax as Knockouts built in [template](http://knockoutjs.com/documentation/template-binding.html) binding except for the `data` option, meaning that you could supply the name of a template or specify a separate templateEngine. The difference between the sortable and draggable handlers is that the draggable has the sortable `group` option set to `{pull:'clone',put: false}` and the `sort` option set to false by default (overridable). + +Other attributes are: +* options: an object that contains settings for the underlaying sortable, ie `group`,`handle`, events etc. +* collection: if your `foreach` array is a computed then you would supply the underlaying observableArray that you would like to sort here. + + +--- + + ### Method From f802c84d05f49adcbf8627dedb689225a27afba9 Mon Sep 17 00:00:00 2001 From: Bogdan Mustiata Date: Thu, 5 Mar 2015 12:13:56 +0100 Subject: [PATCH 09/27] Use precompiled RegExp. --- Sortable.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Sortable.js b/Sortable.js index e7bb2fe..c20e3be 100644 --- a/Sortable.js +++ b/Sortable.js @@ -889,6 +889,8 @@ el.removeEventListener(event, fn, false); } + /** @const */ + var RSPACE = /\s+/g; function _toggleClass(el, name, state) { if (el) { @@ -896,11 +898,8 @@ el.classList[state ? 'add' : 'remove'](name); } else { - var className = (' ' + el.className + ' ') - .replace(/\s+/g, ' ') - .replace(' ' + name + ' ', ' ') - .replace(/ /, ' '); - el.className = className + (state ? ' ' + name : ''); + var className = (' ' + el.className + ' ').replace(RSPACE, ' ').replace(' ' + name + ' ', ' '); + el.className = (className + (state ? ' ' + name : '')).replace(RSPACE, ' '); } } } From 1f58b1b10b7eca676654e0989c7831ae8e9f9fdd Mon Sep 17 00:00:00 2001 From: Noah Chase Date: Tue, 10 Mar 2015 12:38:43 -0400 Subject: [PATCH 10/27] use `page{d}Offset` instead of `scroll{d}` All modern browsers seem to support `window.pageXOffset` and `window.pageYOffset`, which are aliases for `window.scrollX` and `window.scrollY`. IE doesn't seem to have `window.scrollX` and `window.scrollY`. fixes #302 --- Sortable.js | 2 +- Sortable.min.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sortable.js b/Sortable.js index a3cb20e..9bdc895 100644 --- a/Sortable.js +++ b/Sortable.js @@ -144,7 +144,7 @@ if (el) { autoScroll.pid = setInterval(function () { if (el === win) { - win.scrollTo(win.scrollX + vx * speed, win.scrollY + vy * speed); + win.scrollTo(win.pageXOffset + vx * speed, win.pageYOffset + vy * speed); } else { vy && (el.scrollTop += vy * speed); vx && (el.scrollLeft += vx * speed); diff --git a/Sortable.min.js b/Sortable.min.js index 4a75c2c..3dc5f5d 100644 --- a/Sortable.min.js +++ b/Sortable.min.js @@ -1,2 +1,2 @@ /*! Sortable 1.0.1 - MIT | git://github.com/rubaxa/Sortable.git */ -!function(a){"use strict";"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a():"undefined"!=typeof Package?Sortable=a():window.Sortable=a()}(function(){"use strict";function a(a,b){this.el=a,this.options=b=b||{};var d={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(a.nodeName)?"li":">*",ghostClass:"sortable-ghost",ignore:"a, img",filter:null,animation:0,setData:function(a,b){a.setData("Text",b.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id"};for(var e in d)!(e in b)&&(b[e]=d[e]);var g=b.group;g&&"object"==typeof g||(g=b.group={name:g}),["pull","put"].forEach(function(a){a in g||(g[a]=!0)}),M.forEach(function(d){b[d]=c(this,b[d]||N),f(a,d.substr(2).toLowerCase(),b[d])},this),b.groups=" "+g.name+(g.put.join?" "+g.put.join(" "):"")+" ",a[F]=b;for(var h in this)"_"===h.charAt(0)&&(this[h]=c(this,this[h]));f(a,"mousedown",this._onTapStart),f(a,"touchstart",this._onTapStart),f(a,"dragover",this),f(a,"dragenter",this),Q.push(this._onDragOver),b.store&&this.sort(b.store.get(this))}function b(a){s&&s.state!==a&&(i(s,"display",a?"none":""),!a&&s.state&&t.insertBefore(s,q),s.state=a)}function c(a,b){var c=P.call(arguments,2);return b.bind?b.bind.apply(b,[a].concat(c)):function(){return b.apply(a,c.concat(P.call(arguments)))}}function d(a,b,c){if(a){c=c||H,b=b.split(".");var d=b.shift().toUpperCase(),e=new RegExp("\\s("+b.join("|")+")\\s","g");do if(">*"===d&&a.parentNode===c||(""===d||a.nodeName.toUpperCase()==d)&&(!b.length||((" "+a.className+" ").match(e)||[]).length==b.length))return a;while(a!==c&&(a=a.parentNode))}return null}function e(a){a.dataTransfer.dropEffect="move",a.preventDefault()}function f(a,b,c){a.addEventListener(b,c,!1)}function g(a,b,c){a.removeEventListener(b,c,!1)}function h(a,b,c){if(a)if(a.classList)a.classList[c?"add":"remove"](b);else{var d=(" "+a.className+" ").replace(/\s+/g," ").replace(" "+b+" ","");a.className=d+(c?" "+b:"")}}function i(a,b,c){var d=a&&a.style;if(d){if(void 0===c)return H.defaultView&&H.defaultView.getComputedStyle?c=H.defaultView.getComputedStyle(a,""):a.currentStyle&&(c=a.currentStyle),void 0===b?c:c[b];b in d||(b="-webkit-"+b),d[b]=c+("string"==typeof c?"":"px")}}function j(a,b,c){if(a){var d=a.getElementsByTagName(b),e=0,f=d.length;if(c)for(;f>e;e++)c(d[e],e);return d}return[]}function k(a){a.draggable=!1}function l(){K=!1}function m(a,b){var c=a.lastElementChild,d=c.getBoundingClientRect();return b.clientY-(d.top+d.height)>5&&c}function n(a){for(var b=a.tagName+a.className+a.src+a.href+a.textContent,c=b.length,d=0;c--;)d+=b.charCodeAt(c);return d.toString(36)}function o(a){for(var b=0;a&&(a=a.previousElementSibling);)"TEMPLATE"!==a.nodeName.toUpperCase()&&b++;return b}function p(a,b){var c,d;return function(){void 0===c&&(c=arguments,d=this,setTimeout(function(){1===c.length?a.call(d,c[0]):a.apply(d,c),c=void 0},b))}}var q,r,s,t,u,v,w,x,y,z,A,B,C,D,E={},F="Sortable"+(new Date).getTime(),G=window,H=G.document,I=G.parseInt,J=!!("draggable"in H.createElement("div")),K=!1,L=function(a,b,c,d,e,f){var g=H.createEvent("Event");g.initEvent(b,!0,!0),g.item=c||a,g.from=d||a,g.clone=s,g.oldIndex=e,g.newIndex=f,a.dispatchEvent(g)},M="onAdd onUpdate onRemove onStart onEnd onFilter onSort".split(" "),N=function(){},O=Math.abs,P=[].slice,Q=[],R=p(function(a,b,c){if(c&&b.scroll){var d,e,f,g,h=b.scrollSensitivity,i=b.scrollSpeed,j=a.clientX,k=a.clientY,l=window.innerWidth,m=window.innerHeight;if(w!==c&&(v=b.scroll,w=c,v===!0)){v=c;do if(v.offsetWidth=l-j)-(h>=j),g=(h>=m-k)-(h>=k),(f||g)&&(d=G)),(E.vx!==f||E.vy!==g||E.el!==d)&&(E.el=d,E.vx=f,E.vy=g,clearInterval(E.pid),d&&(E.pid=setInterval(function(){d===G?G.scrollTo(G.scrollX+f*i,G.scrollY+g*i):(g&&(d.scrollTop+=g*i),f&&(d.scrollLeft+=f*i))},24)))}},30);return a.prototype={constructor:a,_dragStarted:function(){t&&q&&(h(q,this.options.ghostClass,!0),a.active=this,L(t,"start",q,t,z))},_onTapStart:function(a){var b=a.type,c=a.touches&&a.touches[0],e=(c||a).target,g=e,h=this.options,i=this.el,l=h.filter;if(!("mousedown"===b&&0!==a.button||h.disabled)&&(e=d(e,h.draggable,i))){if(z=o(e),"function"==typeof l){if(l.call(this,a,e,this))return L(g,"filter",e,i,z),void a.preventDefault()}else if(l&&(l=l.split(",").some(function(a){return a=d(g,a.trim(),i),a?(L(a,"filter",e,i,z),!0):void 0})))return void a.preventDefault();if((!h.handle||d(g,h.handle,i))&&e&&!q&&e.parentNode===i){C=a,t=this.el,q=e,u=q.nextSibling,B=this.options.group,q.draggable=!0,h.ignore.split(",").forEach(function(a){j(e,a.trim(),k)}),c&&(C={target:e,clientX:c.clientX,clientY:c.clientY},this._onDragStart(C,"touch"),a.preventDefault()),f(H,"mouseup",this._onDrop),f(H,"touchend",this._onDrop),f(H,"touchcancel",this._onDrop),f(q,"dragend",this),f(t,"dragstart",this._onDragStart),J||this._onDragStart(C,!0);try{H.selection?H.selection.empty():window.getSelection().removeAllRanges()}catch(m){}}}},_emulateDragOver:function(){if(D){i(r,"display","none");var a=H.elementFromPoint(D.clientX,D.clientY),b=a,c=" "+this.options.group.name,d=Q.length;if(b)do{if(b[F]&&b[F].groups.indexOf(c)>-1){for(;d--;)Q[d]({clientX:D.clientX,clientY:D.clientY,target:a,rootEl:b});break}a=b}while(b=b.parentNode);i(r,"display","")}},_onTouchMove:function(a){if(C){var b=a.touches?a.touches[0]:a,c=b.clientX-C.clientX,d=b.clientY-C.clientY,e=a.touches?"translate3d("+c+"px,"+d+"px,0)":"translate("+c+"px,"+d+"px)";D=b,i(r,"webkitTransform",e),i(r,"mozTransform",e),i(r,"msTransform",e),i(r,"transform",e),a.preventDefault()}},_onDragStart:function(a,b){var c=a.dataTransfer,d=this.options;if(this._offUpEvents(),"clone"==B.pull&&(s=q.cloneNode(!0),i(s,"display","none"),t.insertBefore(s,q)),b){var e,g=q.getBoundingClientRect(),h=i(q);r=q.cloneNode(!0),i(r,"top",g.top-I(h.marginTop,10)),i(r,"left",g.left-I(h.marginLeft,10)),i(r,"width",g.width),i(r,"height",g.height),i(r,"opacity","0.8"),i(r,"position","fixed"),i(r,"zIndex","100000"),t.appendChild(r),e=r.getBoundingClientRect(),i(r,"width",2*g.width-e.width),i(r,"height",2*g.height-e.height),"touch"===b?(f(H,"touchmove",this._onTouchMove),f(H,"touchend",this._onDrop),f(H,"touchcancel",this._onDrop)):(f(H,"mousemove",this._onTouchMove),f(H,"mouseup",this._onDrop)),this._loopId=setInterval(this._emulateDragOver,150)}else c&&(c.effectAllowed="move",d.setData&&d.setData.call(this,c,q)),f(H,"drop",this);setTimeout(this._dragStarted,0)},_onDragOver:function(a){var c,e,f,g=this.el,h=this.options,j=h.group,k=j.put,n=B===j,o=h.sort;if(q&&(void 0!==a.preventDefault&&(a.preventDefault(),!h.dragoverBubble&&a.stopPropagation()),B&&!h.disabled&&(n?o||(f=!t.contains(q)):B.pull&&k&&(B.name===j.name||k.indexOf&&~k.indexOf(B.name)))&&(void 0===a.rootEl||a.rootEl===this.el))){if(R(a,h,this.el),K)return;if(c=d(a.target,h.draggable,g),e=q.getBoundingClientRect(),f)return b(!0),void(s||u?t.insertBefore(q,s||u):o||t.appendChild(q));if(0===g.children.length||g.children[0]===r||g===a.target&&(c=m(g,a))){if(c){if(c.animated)return;v=c.getBoundingClientRect()}b(n),g.appendChild(q),this._animate(e,q),c&&this._animate(v,c)}else if(c&&!c.animated&&c!==q&&void 0!==c.parentNode[F]){x!==c&&(x=c,y=i(c));var p,v=c.getBoundingClientRect(),w=v.right-v.left,z=v.bottom-v.top,A=/left|right|inline/.test(y.cssFloat+y.display),C=c.offsetWidth>q.offsetWidth,D=c.offsetHeight>q.offsetHeight,E=(A?(a.clientX-v.left)/w:(a.clientY-v.top)/z)>.5,G=c.nextElementSibling;K=!0,setTimeout(l,30),b(n),p=A?c.previousElementSibling===q&&!C||E&&C:G!==q&&!D||E&&D,p&&!G?g.appendChild(q):c.parentNode.insertBefore(q,p?G:c),this._animate(e,q),this._animate(v,c)}}},_animate:function(a,b){var c=this.options.animation;if(c){var d=b.getBoundingClientRect();i(b,"transition","none"),i(b,"transform","translate3d("+(a.left-d.left)+"px,"+(a.top-d.top)+"px,0)"),b.offsetWidth,i(b,"transition","all "+c+"ms"),i(b,"transform","translate3d(0,0,0)"),clearTimeout(b.animated),b.animated=setTimeout(function(){i(b,"transition",""),i(b,"transform",""),b.animated=!1},c)}},_offUpEvents:function(){g(H,"mouseup",this._onDrop),g(H,"touchmove",this._onTouchMove),g(H,"touchend",this._onDrop),g(H,"touchcancel",this._onDrop)},_onDrop:function(b){var c=this.el,d=this.options;clearInterval(this._loopId),clearInterval(E.pid),g(H,"drop",this),g(H,"mousemove",this._onTouchMove),g(c,"dragstart",this._onDragStart),this._offUpEvents(),b&&(b.preventDefault(),!d.dropBubble&&b.stopPropagation(),r&&r.parentNode.removeChild(r),q&&(g(q,"dragend",this),k(q),h(q,this.options.ghostClass,!1),t!==q.parentNode?(A=o(q),L(q.parentNode,"sort",q,t,z,A),L(t,"sort",q,t,z,A),L(q,"add",q,t,z,A),L(t,"remove",q,t,z,A)):(s&&s.parentNode.removeChild(s),q.nextSibling!==u&&(A=o(q),L(t,"update",q,t,z,A),L(t,"sort",q,t,z,A))),a.active&&L(t,"end",q,t,z,A)),t=q=r=u=s=v=w=C=D=x=y=B=a.active=null,this.save())},handleEvent:function(a){var b=a.type;"dragover"===b||"dragenter"===b?(this._onDragOver(a),e(a)):("drop"===b||"dragend"===b)&&this._onDrop(a)},toArray:function(){for(var a,b=[],c=this.el.children,e=0,f=c.length,g=this.options;f>e;e++)a=c[e],d(a,g.draggable,this.el)&&b.push(a.getAttribute(g.dataIdAttr)||n(a));return b},sort:function(a){var b={},c=this.el;this.toArray().forEach(function(a,e){var f=c.children[e];d(f,this.options.draggable,c)&&(b[a]=f)},this),a.forEach(function(a){b[a]&&(c.removeChild(b[a]),c.appendChild(b[a]))})},save:function(){var a=this.options.store;a&&a.set(this)},closest:function(a,b){return d(a,b||this.options.draggable,this.el)},option:function(a,b){var c=this.options;return void 0===b?c[a]:void(c[a]=b)},destroy:function(){var a=this.el,b=this.options;M.forEach(function(c){g(a,c.substr(2).toLowerCase(),b[c])}),g(a,"mousedown",this._onTapStart),g(a,"touchstart",this._onTapStart),g(a,"dragover",this),g(a,"dragenter",this),Array.prototype.forEach.call(a.querySelectorAll("[draggable]"),function(a){a.removeAttribute("draggable")}),Q.splice(Q.indexOf(this._onDragOver),1),this._onDrop(),this.el=null}},a.utils={on:f,off:g,css:i,find:j,bind:c,is:function(a,b){return!!d(a,b,a)},throttle:p,closest:d,toggleClass:h,dispatchEvent:L,index:o},a.version="1.0.1",a.create=function(b,c){return new a(b,c)},a}); \ No newline at end of file +!function(a){"use strict";"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a():"undefined"!=typeof Package?Sortable=a():window.Sortable=a()}(function(){"use strict";function a(a,b){this.el=a,this.options=b=b||{};var d={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(a.nodeName)?"li":">*",ghostClass:"sortable-ghost",ignore:"a, img",filter:null,animation:0,setData:function(a,b){a.setData("Text",b.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id"};for(var e in d)!(e in b)&&(b[e]=d[e]);var g=b.group;g&&"object"==typeof g||(g=b.group={name:g}),["pull","put"].forEach(function(a){a in g||(g[a]=!0)}),M.forEach(function(d){b[d]=c(this,b[d]||N),f(a,d.substr(2).toLowerCase(),b[d])},this),b.groups=" "+g.name+(g.put.join?" "+g.put.join(" "):"")+" ",a[F]=b;for(var h in this)"_"===h.charAt(0)&&(this[h]=c(this,this[h]));f(a,"mousedown",this._onTapStart),f(a,"touchstart",this._onTapStart),f(a,"dragover",this),f(a,"dragenter",this),Q.push(this._onDragOver),b.store&&this.sort(b.store.get(this))}function b(a){s&&s.state!==a&&(i(s,"display",a?"none":""),!a&&s.state&&t.insertBefore(s,q),s.state=a)}function c(a,b){var c=P.call(arguments,2);return b.bind?b.bind.apply(b,[a].concat(c)):function(){return b.apply(a,c.concat(P.call(arguments)))}}function d(a,b,c){if(a){c=c||H,b=b.split(".");var d=b.shift().toUpperCase(),e=new RegExp("\\s("+b.join("|")+")\\s","g");do if(">*"===d&&a.parentNode===c||(""===d||a.nodeName.toUpperCase()==d)&&(!b.length||((" "+a.className+" ").match(e)||[]).length==b.length))return a;while(a!==c&&(a=a.parentNode))}return null}function e(a){a.dataTransfer.dropEffect="move",a.preventDefault()}function f(a,b,c){a.addEventListener(b,c,!1)}function g(a,b,c){a.removeEventListener(b,c,!1)}function h(a,b,c){if(a)if(a.classList)a.classList[c?"add":"remove"](b);else{var d=(" "+a.className+" ").replace(/\s+/g," ").replace(" "+b+" ","");a.className=d+(c?" "+b:"")}}function i(a,b,c){var d=a&&a.style;if(d){if(void 0===c)return H.defaultView&&H.defaultView.getComputedStyle?c=H.defaultView.getComputedStyle(a,""):a.currentStyle&&(c=a.currentStyle),void 0===b?c:c[b];b in d||(b="-webkit-"+b),d[b]=c+("string"==typeof c?"":"px")}}function j(a,b,c){if(a){var d=a.getElementsByTagName(b),e=0,f=d.length;if(c)for(;f>e;e++)c(d[e],e);return d}return[]}function k(a){a.draggable=!1}function l(){K=!1}function m(a,b){var c=a.lastElementChild,d=c.getBoundingClientRect();return b.clientY-(d.top+d.height)>5&&c}function n(a){for(var b=a.tagName+a.className+a.src+a.href+a.textContent,c=b.length,d=0;c--;)d+=b.charCodeAt(c);return d.toString(36)}function o(a){for(var b=0;a&&(a=a.previousElementSibling);)"TEMPLATE"!==a.nodeName.toUpperCase()&&b++;return b}function p(a,b){var c,d;return function(){void 0===c&&(c=arguments,d=this,setTimeout(function(){1===c.length?a.call(d,c[0]):a.apply(d,c),c=void 0},b))}}var q,r,s,t,u,v,w,x,y,z,A,B,C,D,E={},F="Sortable"+(new Date).getTime(),G=window,H=G.document,I=G.parseInt,J=!!("draggable"in H.createElement("div")),K=!1,L=function(a,b,c,d,e,f){var g=H.createEvent("Event");g.initEvent(b,!0,!0),g.item=c||a,g.from=d||a,g.clone=s,g.oldIndex=e,g.newIndex=f,a.dispatchEvent(g)},M="onAdd onUpdate onRemove onStart onEnd onFilter onSort".split(" "),N=function(){},O=Math.abs,P=[].slice,Q=[],R=p(function(a,b,c){if(c&&b.scroll){var d,e,f,g,h=b.scrollSensitivity,i=b.scrollSpeed,j=a.clientX,k=a.clientY,l=window.innerWidth,m=window.innerHeight;if(w!==c&&(v=b.scroll,w=c,v===!0)){v=c;do if(v.offsetWidth=l-j)-(h>=j),g=(h>=m-k)-(h>=k),(f||g)&&(d=G)),(E.vx!==f||E.vy!==g||E.el!==d)&&(E.el=d,E.vx=f,E.vy=g,clearInterval(E.pid),d&&(E.pid=setInterval(function(){d===G?G.scrollTo(G.pageXOffset+f*i,G.pageYOffset+g*i):(g&&(d.scrollTop+=g*i),f&&(d.scrollLeft+=f*i))},24)))}},30);return a.prototype={constructor:a,_dragStarted:function(){t&&q&&(h(q,this.options.ghostClass,!0),a.active=this,L(t,"start",q,t,z))},_onTapStart:function(a){var b=a.type,c=a.touches&&a.touches[0],e=(c||a).target,g=e,h=this.options,i=this.el,l=h.filter;if(!("mousedown"===b&&0!==a.button||h.disabled)&&(e=d(e,h.draggable,i))){if(z=o(e),"function"==typeof l){if(l.call(this,a,e,this))return L(g,"filter",e,i,z),void a.preventDefault()}else if(l&&(l=l.split(",").some(function(a){return a=d(g,a.trim(),i),a?(L(a,"filter",e,i,z),!0):void 0})))return void a.preventDefault();if((!h.handle||d(g,h.handle,i))&&e&&!q&&e.parentNode===i){C=a,t=this.el,q=e,u=q.nextSibling,B=this.options.group,q.draggable=!0,h.ignore.split(",").forEach(function(a){j(e,a.trim(),k)}),c&&(C={target:e,clientX:c.clientX,clientY:c.clientY},this._onDragStart(C,"touch"),a.preventDefault()),f(H,"mouseup",this._onDrop),f(H,"touchend",this._onDrop),f(H,"touchcancel",this._onDrop),f(q,"dragend",this),f(t,"dragstart",this._onDragStart),J||this._onDragStart(C,!0);try{H.selection?H.selection.empty():window.getSelection().removeAllRanges()}catch(m){}}}},_emulateDragOver:function(){if(D){i(r,"display","none");var a=H.elementFromPoint(D.clientX,D.clientY),b=a,c=" "+this.options.group.name,d=Q.length;if(b)do{if(b[F]&&b[F].groups.indexOf(c)>-1){for(;d--;)Q[d]({clientX:D.clientX,clientY:D.clientY,target:a,rootEl:b});break}a=b}while(b=b.parentNode);i(r,"display","")}},_onTouchMove:function(a){if(C){var b=a.touches?a.touches[0]:a,c=b.clientX-C.clientX,d=b.clientY-C.clientY,e=a.touches?"translate3d("+c+"px,"+d+"px,0)":"translate("+c+"px,"+d+"px)";D=b,i(r,"webkitTransform",e),i(r,"mozTransform",e),i(r,"msTransform",e),i(r,"transform",e),a.preventDefault()}},_onDragStart:function(a,b){var c=a.dataTransfer,d=this.options;if(this._offUpEvents(),"clone"==B.pull&&(s=q.cloneNode(!0),i(s,"display","none"),t.insertBefore(s,q)),b){var e,g=q.getBoundingClientRect(),h=i(q);r=q.cloneNode(!0),i(r,"top",g.top-I(h.marginTop,10)),i(r,"left",g.left-I(h.marginLeft,10)),i(r,"width",g.width),i(r,"height",g.height),i(r,"opacity","0.8"),i(r,"position","fixed"),i(r,"zIndex","100000"),t.appendChild(r),e=r.getBoundingClientRect(),i(r,"width",2*g.width-e.width),i(r,"height",2*g.height-e.height),"touch"===b?(f(H,"touchmove",this._onTouchMove),f(H,"touchend",this._onDrop),f(H,"touchcancel",this._onDrop)):(f(H,"mousemove",this._onTouchMove),f(H,"mouseup",this._onDrop)),this._loopId=setInterval(this._emulateDragOver,150)}else c&&(c.effectAllowed="move",d.setData&&d.setData.call(this,c,q)),f(H,"drop",this);setTimeout(this._dragStarted,0)},_onDragOver:function(a){var c,e,f,g=this.el,h=this.options,j=h.group,k=j.put,n=B===j,o=h.sort;if(q&&(void 0!==a.preventDefault&&(a.preventDefault(),!h.dragoverBubble&&a.stopPropagation()),B&&!h.disabled&&(n?o||(f=!t.contains(q)):B.pull&&k&&(B.name===j.name||k.indexOf&&~k.indexOf(B.name)))&&(void 0===a.rootEl||a.rootEl===this.el))){if(R(a,h,this.el),K)return;if(c=d(a.target,h.draggable,g),e=q.getBoundingClientRect(),f)return b(!0),void(s||u?t.insertBefore(q,s||u):o||t.appendChild(q));if(0===g.children.length||g.children[0]===r||g===a.target&&(c=m(g,a))){if(c){if(c.animated)return;v=c.getBoundingClientRect()}b(n),g.appendChild(q),this._animate(e,q),c&&this._animate(v,c)}else if(c&&!c.animated&&c!==q&&void 0!==c.parentNode[F]){x!==c&&(x=c,y=i(c));var p,v=c.getBoundingClientRect(),w=v.right-v.left,z=v.bottom-v.top,A=/left|right|inline/.test(y.cssFloat+y.display),C=c.offsetWidth>q.offsetWidth,D=c.offsetHeight>q.offsetHeight,E=(A?(a.clientX-v.left)/w:(a.clientY-v.top)/z)>.5,G=c.nextElementSibling;K=!0,setTimeout(l,30),b(n),p=A?c.previousElementSibling===q&&!C||E&&C:G!==q&&!D||E&&D,p&&!G?g.appendChild(q):c.parentNode.insertBefore(q,p?G:c),this._animate(e,q),this._animate(v,c)}}},_animate:function(a,b){var c=this.options.animation;if(c){var d=b.getBoundingClientRect();i(b,"transition","none"),i(b,"transform","translate3d("+(a.left-d.left)+"px,"+(a.top-d.top)+"px,0)"),b.offsetWidth,i(b,"transition","all "+c+"ms"),i(b,"transform","translate3d(0,0,0)"),clearTimeout(b.animated),b.animated=setTimeout(function(){i(b,"transition",""),i(b,"transform",""),b.animated=!1},c)}},_offUpEvents:function(){g(H,"mouseup",this._onDrop),g(H,"touchmove",this._onTouchMove),g(H,"touchend",this._onDrop),g(H,"touchcancel",this._onDrop)},_onDrop:function(b){var c=this.el,d=this.options;clearInterval(this._loopId),clearInterval(E.pid),g(H,"drop",this),g(H,"mousemove",this._onTouchMove),g(c,"dragstart",this._onDragStart),this._offUpEvents(),b&&(b.preventDefault(),!d.dropBubble&&b.stopPropagation(),r&&r.parentNode.removeChild(r),q&&(g(q,"dragend",this),k(q),h(q,this.options.ghostClass,!1),t!==q.parentNode?(A=o(q),L(q.parentNode,"sort",q,t,z,A),L(t,"sort",q,t,z,A),L(q,"add",q,t,z,A),L(t,"remove",q,t,z,A)):(s&&s.parentNode.removeChild(s),q.nextSibling!==u&&(A=o(q),L(t,"update",q,t,z,A),L(t,"sort",q,t,z,A))),a.active&&L(t,"end",q,t,z,A)),t=q=r=u=s=v=w=C=D=x=y=B=a.active=null,this.save())},handleEvent:function(a){var b=a.type;"dragover"===b||"dragenter"===b?(this._onDragOver(a),e(a)):("drop"===b||"dragend"===b)&&this._onDrop(a)},toArray:function(){for(var a,b=[],c=this.el.children,e=0,f=c.length,g=this.options;f>e;e++)a=c[e],d(a,g.draggable,this.el)&&b.push(a.getAttribute(g.dataIdAttr)||n(a));return b},sort:function(a){var b={},c=this.el;this.toArray().forEach(function(a,e){var f=c.children[e];d(f,this.options.draggable,c)&&(b[a]=f)},this),a.forEach(function(a){b[a]&&(c.removeChild(b[a]),c.appendChild(b[a]))})},save:function(){var a=this.options.store;a&&a.set(this)},closest:function(a,b){return d(a,b||this.options.draggable,this.el)},option:function(a,b){var c=this.options;return void 0===b?c[a]:void(c[a]=b)},destroy:function(){var a=this.el,b=this.options;M.forEach(function(c){g(a,c.substr(2).toLowerCase(),b[c])}),g(a,"mousedown",this._onTapStart),g(a,"touchstart",this._onTapStart),g(a,"dragover",this),g(a,"dragenter",this),Array.prototype.forEach.call(a.querySelectorAll("[draggable]"),function(a){a.removeAttribute("draggable")}),Q.splice(Q.indexOf(this._onDragOver),1),this._onDrop(),this.el=null}},a.utils={on:f,off:g,css:i,find:j,bind:c,is:function(a,b){return!!d(a,b,a)},throttle:p,closest:d,toggleClass:h,dispatchEvent:L,index:o},a.version="1.0.1",a.create=function(b,c){return new a(b,c)},a}); \ No newline at end of file From 7309734dbf52e0e9d9682ad563660a5d0acb51de Mon Sep 17 00:00:00 2001 From: RubaXa Date: Wed, 11 Mar 2015 18:06:34 +0300 Subject: [PATCH 11/27] #290: + clone simple 'option' and 'extend' method --- Sortable.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Sortable.js b/Sortable.js index 9bdc895..db894b6 100644 --- a/Sortable.js +++ b/Sortable.js @@ -165,7 +165,7 @@ */ function Sortable(el, options) { this.el = el; // root element - this.options = options = (options || {}); + this.options = options = _extend({}, options); // Default options @@ -1017,6 +1017,18 @@ }; } + function _extend(dst, src) { + if (dst && src) { + for (var key in src) { + if (src.hasOwnProperty(key)) { + dst[key] = src[key]; + } + } + } + + return dst; + } + // Export utils Sortable.utils = { @@ -1028,6 +1040,7 @@ is: function (el, selector) { return !!_closest(el, selector, el); }, + extend: _extend, throttle: _throttle, closest: _closest, toggleClass: _toggleClass, From 8b818ed280bfe54e4e51d5ff375f751eb590239f Mon Sep 17 00:00:00 2001 From: RubaXa Date: Wed, 11 Mar 2015 18:33:36 +0300 Subject: [PATCH 12/27] #288: + use 'ownerDocument' for correct working with/into iframe --- Sortable.js | 17 ++++++++------- st/iframe/frame.html | 32 +++++++++++++++++++++++++++++ st/iframe/index.html | 49 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 st/iframe/frame.html create mode 100644 st/iframe/index.html diff --git a/Sortable.js b/Sortable.js index db894b6..82e1525 100644 --- a/Sortable.js +++ b/Sortable.js @@ -269,8 +269,9 @@ target = (touch || evt).target, originalTarget = target, options = this.options, + filter = options.filter, el = this.el, - filter = options.filter; + ownerDocument = el.ownerDocument; // for correct working with/into iframe if (type === 'mousedown' && evt.button !== 0 || options.disabled) { return; // only left button or enabled @@ -343,9 +344,9 @@ evt.preventDefault(); } - _on(document, 'mouseup', this._onDrop); - _on(document, 'touchend', this._onDrop); - _on(document, 'touchcancel', this._onDrop); + _on(ownerDocument, 'mouseup', this._onDrop); + _on(ownerDocument, 'touchend', this._onDrop); + _on(ownerDocument, 'touchcancel', this._onDrop); _on(dragEl, 'dragend', this); _on(rootEl, 'dragstart', this._onDragStart); @@ -617,10 +618,12 @@ }, _offUpEvents: function () { - _off(document, 'mouseup', this._onDrop); + var ownerDocument = this.el.ownerDocument; + _off(document, 'touchmove', this._onTouchMove); - _off(document, 'touchend', this._onDrop); - _off(document, 'touchcancel', this._onDrop); + _off(ownerDocument, 'mouseup', this._onDrop); + _off(ownerDocument, 'touchend', this._onDrop); + _off(ownerDocument, 'touchcancel', this._onDrop); }, _onDrop: function (/**Event*/evt) { diff --git a/st/iframe/frame.html b/st/iframe/frame.html new file mode 100644 index 0000000..677eeef --- /dev/null +++ b/st/iframe/frame.html @@ -0,0 +1,32 @@ + + + + + + + + + + + + +
+
+ 14 + + Drag me by the handle +
+
+ 2 + + You can also select text +
+
+ 1 + + Best of both worlds! +
+
+ + + diff --git a/st/iframe/index.html b/st/iframe/index.html new file mode 100644 index 0000000..fcd0898 --- /dev/null +++ b/st/iframe/index.html @@ -0,0 +1,49 @@ + + + + + IFrame playground + + + + + + + + + + + + + +
+
This is Sortable
+
It works with Bootstrap...
+
...out of the box.
+
It has support for touch devices.
+
Just drag some elements around.
+
+ + + + + From 5fd36b19b18ef81148ae38078a159fbd281120aa Mon Sep 17 00:00:00 2001 From: RubaXa Date: Wed, 11 Mar 2015 18:36:32 +0300 Subject: [PATCH 13/27] * RSPACE --- Sortable.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sortable.js b/Sortable.js index ec08750..c64980f 100644 --- a/Sortable.js +++ b/Sortable.js @@ -45,6 +45,9 @@ tapEvt, touchEvt, + /** @const */ + RSPACE = /\s+/g, + expando = 'Sortable' + (new Date).getTime(), win = window, @@ -53,7 +56,6 @@ supportDraggable = !!('draggable' in document.createElement('div')), - _silent = false, _dispatchEvent = function (rootEl, name, targetEl, fromEl, startIndex, newIndex) { @@ -892,8 +894,6 @@ el.removeEventListener(event, fn, false); } - /** @const */ - var RSPACE = /\s+/g; function _toggleClass(el, name, state) { if (el) { From 6209024019951f6250540d3fb1a8adc4d28faa33 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Wed, 11 Mar 2015 19:27:47 +0300 Subject: [PATCH 14/27] #284: -> version --- ng-sortable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ng-sortable.js b/ng-sortable.js index a872561..26df499 100644 --- a/ng-sortable.js +++ b/ng-sortable.js @@ -25,7 +25,7 @@ angular.module('ng-sortable', []) - .constant('$version', '0.3.5') + .constant('version', '0.3.6') .directive('ngSortable', ['$parse', function ($parse) { var removed, nextSibling; From b7749c4576af790cdd3620336dea69729cf35b57 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Thu, 12 Mar 2015 08:46:36 +0300 Subject: [PATCH 15/27] #298: + watchers of events --- ng-sortable.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ng-sortable.js b/ng-sortable.js index 26df499..4d40d3d 100644 --- a/ng-sortable.js +++ b/ng-sortable.js @@ -160,7 +160,10 @@ }); if (ngSortable && !/{|}/.test(ngSortable)) { // todo: ugly - angular.forEach(['sort', 'disabled', 'draggable', 'handle', 'animation'], function (name) { + angular.forEach([ + 'sort', 'disabled', 'draggable', 'handle', 'animation', + 'onStart', 'onEnd', 'onAdd', 'onUpdate', 'onRemove', 'onSort' + ], function (name) { scope.$watch(ngSortable + '.' + name, function (value) { if (value !== void 0) { options[name] = value; From 78c53ba5e19c6616893183e655cf9d28cb1ec77d Mon Sep 17 00:00:00 2001 From: jboulouloubi Date: Wed, 1 Apr 2015 18:35:36 -0400 Subject: [PATCH 16/27] Add delay between touchstart and dragstart --- Sortable.js | 135 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 86 insertions(+), 49 deletions(-) diff --git a/Sortable.js b/Sortable.js index c64980f..962c6e9 100644 --- a/Sortable.js +++ b/Sortable.js @@ -190,7 +190,8 @@ }, dropBubble: false, dragoverBubble: false, - dataIdAttr: 'data-id' + dataIdAttr: 'data-id', + delay: 0 }; @@ -264,6 +265,87 @@ } }, + _triggerDragStart: function (evt, target, touch) { + evt.preventDefault(); + + if (touch) { + // Touch device support + tapEvt = { + target: target, + 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) { + } + }, + + _enableDragStart: function (dragEl, target) { + dragEl.draggable = true; + + // Disable "draggable" + this.options.ignore.split(',').forEach(function (criteria) { + _find(target, criteria.trim(), _disableDraggable); + }); + }, + + _disableDelayedDrag: function () { + clearTimeout(this.dragStartTimer); + + _off(document, 'mousemove', this._disableDelayedDrag); + _off(document, 'touchmove', this._disableDelayedDrag); + }, + + _prepareDragStart: function (evt, target, el, touch) { + var _this = this; + + if (target && !dragEl && (target.parentNode === el)) { + tapEvt = evt; + + rootEl = this.el; + dragEl = target; + nextEl = dragEl.nextSibling; + activeGroup = this.options.group; + + this.options.delay = this.options.delay || 1; + if (this.options.delay) { + _on(document, 'mouseup', this._onDrop); + _on(document, 'touchend', this._onDrop); + _on(document, 'touchcancel', this._onDrop); + + // If the user moves the pointer before the delay has been reached: + // disable the delayed drag + _on(document, 'mousemove', this._disableDelayedDrag); + _on(document, 'touchmove', this._disableDelayedDrag); + + this.dragStartTimer = setTimeout(function () { + // Delayed drag has been triggered + // we can re-enable the events: touchmove/mousemove + _off(document, 'mousemove', _this._disableDelayedDrag); + _off(document, 'touchmove', _this._disableDelayedDrag); + + // Make the element draggable + _this._enableDragStart(dragEl, target); + // Bind the events: dragstart/dragend + _this._triggerDragStart(evt, target, touch); + }, this.options.delay); + } + } + }, _onTapStart: function (/**Event|TouchEvent*/evt) { var type = evt.type, @@ -317,55 +399,8 @@ 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'); - evt.preventDefault(); - } - - _on(ownerDocument, 'mouseup', this._onDrop); - _on(ownerDocument, 'touchend', this._onDrop); - _on(ownerDocument, '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) { - } - } + this._prepareDragStart(evt, target, el, touch); }, _emulateDragOver: function () { @@ -635,6 +670,8 @@ clearInterval(this._loopId); clearInterval(autoScroll.pid); + clearTimeout(this.dragStartTimer); + // Unbind events _off(document, 'drop', this); _off(document, 'mousemove', this._onTouchMove); From 723bc1e605de32555fa9e5fd5d531832cfc9848e Mon Sep 17 00:00:00 2001 From: RubaXa Date: Sun, 5 Apr 2015 23:57:39 +0300 Subject: [PATCH 17/27] #325: PR refactoring: - _enableDragStart & revert lost fixes --- Sortable.js | 103 +++++++++++++++++++++++++++------------------------- 1 file changed, 54 insertions(+), 49 deletions(-) diff --git a/Sortable.js b/Sortable.js index 962c6e9..ea26139 100644 --- a/Sortable.js +++ b/Sortable.js @@ -252,7 +252,6 @@ Sortable.prototype = /** @lends Sortable.prototype */ { constructor: Sortable, - _dragStarted: function () { if (rootEl && dragEl) { // Apply effect @@ -265,21 +264,21 @@ } }, - _triggerDragStart: function (evt, target, touch) { - evt.preventDefault(); - + _triggerDragStart: function (/** Touch */touch) { if (touch) { // Touch device support tapEvt = { - target: target, + target: dragEl, clientX: touch.clientX, clientY: touch.clientY }; this._onDragStart(tapEvt, 'touch'); - } else if (!supportDraggable) { + } + else if (!supportDraggable) { this._onDragStart(tapEvt, true); - } else { + } + else { _on(dragEl, 'dragend', this); _on(rootEl, 'dragstart', this._onDragStart); } @@ -294,68 +293,73 @@ } }, - _enableDragStart: function (dragEl, target) { - dragEl.draggable = true; - - // Disable "draggable" - this.options.ignore.split(',').forEach(function (criteria) { - _find(target, criteria.trim(), _disableDraggable); - }); - }, - _disableDelayedDrag: function () { - clearTimeout(this.dragStartTimer); + var ownerDocument = this.el.ownerDocument; + + clearTimeout(this._dragStartTimer); - _off(document, 'mousemove', this._disableDelayedDrag); - _off(document, 'touchmove', this._disableDelayedDrag); + _off(ownerDocument, 'mousemove', this._disableDelayedDrag); + _off(ownerDocument, 'touchmove', this._disableDelayedDrag); }, - _prepareDragStart: function (evt, target, el, touch) { - var _this = this; + _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 = this.el; + rootEl = el; dragEl = target; nextEl = dragEl.nextSibling; - activeGroup = this.options.group; + activeGroup = options.group; - this.options.delay = this.options.delay || 1; - if (this.options.delay) { - _on(document, 'mouseup', this._onDrop); - _on(document, 'touchend', this._onDrop); - _on(document, 'touchcancel', this._onDrop); + 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(document, 'mousemove', this._disableDelayedDrag); - _on(document, 'touchmove', this._disableDelayedDrag); - - this.dragStartTimer = setTimeout(function () { - // Delayed drag has been triggered - // we can re-enable the events: touchmove/mousemove - _off(document, 'mousemove', _this._disableDelayedDrag); - _off(document, 'touchmove', _this._disableDelayedDrag); - - // Make the element draggable - _this._enableDragStart(dragEl, target); - // Bind the events: dragstart/dragend - _this._triggerDragStart(evt, target, touch); - }, this.options.delay); + _on(ownerDocument, 'mousemove', _this._disableDelayedDrag); + _on(ownerDocument, 'touchmove', _this._disableDelayedDrag); + + _this._dragStartTimer = setTimeout(dragStartFn, options.delay); + } else { + dragStartFn(); } } }, - _onTapStart: function (/**Event|TouchEvent*/evt) { - var type = evt.type, + _onTapStart: function (/** Event|TouchEvent */evt) { + var el = this.el, + options = this.options, + type = evt.type, touch = evt.touches && evt.touches[0], target = (touch || evt).target, originalTarget = target, - options = this.options, - filter = options.filter, - el = this.el, - ownerDocument = el.ownerDocument; // for correct working with/into iframe + filter = options.filter; + if (type === 'mousedown' && evt.button !== 0 || options.disabled) { return; // only left button or enabled @@ -399,8 +403,9 @@ return; } + // Prepare `dragstart` - this._prepareDragStart(evt, target, el, touch); + this._prepareDragStart(evt, touch, target); }, _emulateDragOver: function () { From 40af728d53c8ed6ca41dead85ceb08d71b8a77fa Mon Sep 17 00:00:00 2001 From: RubaXa Date: Mon, 6 Apr 2015 00:00:55 +0300 Subject: [PATCH 18/27] #325: * changed the order of the methods (code style) --- Sortable.js | 156 ++++++++++++++++++++++++++-------------------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/Sortable.js b/Sortable.js index ea26139..a3b82fe 100644 --- a/Sortable.js +++ b/Sortable.js @@ -252,54 +252,61 @@ Sortable.prototype = /** @lends Sortable.prototype */ { constructor: Sortable, - _dragStarted: function () { - if (rootEl && dragEl) { - // Apply effect - _toggleClass(dragEl, this.options.ghostClass, true); + _onTapStart: function (/** Event|TouchEvent */evt) { + var el = this.el, + options = this.options, + type = evt.type, + touch = evt.touches && evt.touches[0], + target = (touch || evt).target, + originalTarget = target, + filter = options.filter; - Sortable.active = this; - // Drag start event - _dispatchEvent(rootEl, 'start', dragEl, rootEl, oldIndex); + if (type === 'mousedown' && evt.button !== 0 || options.disabled) { + return; // only left button or enabled } - }, - _triggerDragStart: function (/** Touch */touch) { - if (touch) { - // Touch device support - tapEvt = { - target: dragEl, - clientX: touch.clientX, - clientY: touch.clientY - }; + target = _closest(target, options.draggable, el); - this._onDragStart(tapEvt, 'touch'); - } - else if (!supportDraggable) { - this._onDragStart(tapEvt, true); + if (!target) { + return; } - else { - _on(dragEl, 'dragend', this); - _on(rootEl, 'dragstart', this._onDragStart); + + // 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); + evt.preventDefault(); + return; // cancel dnd + } } + else if (filter) { + filter = filter.split(',').some(function (criteria) { + criteria = _closest(originalTarget, criteria.trim(), el); - try { - if (document.selection) { - document.selection.empty(); - } else { - window.getSelection().removeAllRanges(); + if (criteria) { + _dispatchEvent(criteria, 'filter', target, el, oldIndex); + return true; + } + }); + + if (filter) { + evt.preventDefault(); + return; // cancel dnd } - } catch (err) { } - }, - _disableDelayedDrag: function () { - var ownerDocument = this.el.ownerDocument; - clearTimeout(this._dragStartTimer); + if (options.handle && !_closest(originalTarget, options.handle, el)) { + return; + } - _off(ownerDocument, 'mousemove', this._disableDelayedDrag); - _off(ownerDocument, 'touchmove', this._disableDelayedDrag); + + // Prepare `dragstart` + this._prepareDragStart(evt, touch, target); }, _prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target) { @@ -351,61 +358,54 @@ } }, - _onTapStart: function (/** Event|TouchEvent */evt) { - var el = this.el, - options = this.options, - type = evt.type, - touch = evt.touches && evt.touches[0], - target = (touch || evt).target, - originalTarget = target, - filter = options.filter; + _disableDelayedDrag: function () { + var ownerDocument = this.el.ownerDocument; + clearTimeout(this._dragStartTimer); - if (type === 'mousedown' && evt.button !== 0 || options.disabled) { - return; // only left button or enabled - } + _off(ownerDocument, 'mousemove', this._disableDelayedDrag); + _off(ownerDocument, 'touchmove', this._disableDelayedDrag); + }, - target = _closest(target, options.draggable, el); + _triggerDragStart: function (/** Touch */touch) { + if (touch) { + // Touch device support + tapEvt = { + target: dragEl, + clientX: touch.clientX, + clientY: touch.clientY + }; - if (!target) { - return; + this._onDragStart(tapEvt, 'touch'); } - - // 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); - evt.preventDefault(); - return; // cancel dnd - } + else if (!supportDraggable) { + this._onDragStart(tapEvt, true); + } + else { + _on(dragEl, 'dragend', this); + _on(rootEl, 'dragstart', this._onDragStart); } - 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) { - evt.preventDefault(); - return; // cancel dnd + 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); - if (options.handle && !_closest(originalTarget, options.handle, el)) { - return; - } - + Sortable.active = this; - // Prepare `dragstart` - this._prepareDragStart(evt, touch, target); + // Drag start event + _dispatchEvent(rootEl, 'start', dragEl, rootEl, oldIndex); + } }, _emulateDragOver: function () { From 5c4e3dce2a8b0b28c0e7d7235377d13cc71a9bbd Mon Sep 17 00:00:00 2001 From: RubaXa Date: Thu, 9 Apr 2015 11:42:14 +0300 Subject: [PATCH 19/27] #318, #328: + Improved work with events transmitted through the options --- Sortable.js | 63 +++++++++++++++++++++++--------------------------- ng-sortable.js | 5 +++- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/Sortable.js b/Sortable.js index cfdd8af..71f5a32 100644 --- a/Sortable.js +++ b/Sortable.js @@ -58,8 +58,10 @@ _silent = false, - _dispatchEvent = function (rootEl, name, targetEl, fromEl, startIndex, newIndex) { - var evt = document.createEvent('Event'); + _dispatchEvent = function (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); @@ -70,13 +72,13 @@ evt.oldIndex = startIndex; evt.newIndex = newIndex; + if (options[onName]) { + options[onName].call(sortable, evt); + } + rootEl.dispatchEvent(evt); }, - _customEvents = 'onAdd onUpdate onRemove onStart onEnd onFilter onSort'.split(' '), - - noop = function () {}, - abs = Math.abs, slice = [].slice, @@ -170,6 +172,10 @@ this.options = options = _extend({}, options); + // Export instance + el[expando] = this; + + // Default options var defaults = { group: Math.random(), @@ -215,16 +221,7 @@ }); - // 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 @@ -253,7 +250,8 @@ constructor: Sortable, _onTapStart: function (/** Event|TouchEvent */evt) { - var el = this.el, + var _this = this, + el = this.el, options = this.options, type = evt.type, touch = evt.touches && evt.touches[0], @@ -278,7 +276,7 @@ // Check filter if (typeof filter === 'function') { if (filter.call(this, evt, target, this)) { - _dispatchEvent(originalTarget, 'filter', target, el, oldIndex); + _dispatchEvent(_this, originalTarget, 'filter', target, el, oldIndex); evt.preventDefault(); return; // cancel dnd } @@ -288,7 +286,7 @@ criteria = _closest(originalTarget, criteria.trim(), el); if (criteria) { - _dispatchEvent(criteria, 'filter', target, el, oldIndex); + _dispatchEvent(_this, criteria, 'filter', target, el, oldIndex); return true; } }); @@ -404,7 +402,7 @@ Sortable.active = this; // Drag start event - _dispatchEvent(rootEl, 'start', dragEl, rootEl, oldIndex); + _dispatchEvent(this, rootEl, 'start', dragEl, rootEl, oldIndex); } }, @@ -419,7 +417,7 @@ if (parent) { do { - if (parent[expando] && parent[expando].groups.indexOf(groupName) > -1) { + if (parent[expando] && parent[expando].options.groups.indexOf(groupName) > -1) { while (i--) { touchDragOverListeners[i]({ clientX: touchEvt.clientX, @@ -700,14 +698,14 @@ 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); + _dispatchEvent(null, dragEl.parentNode, 'sort', dragEl, rootEl, oldIndex, newIndex); + _dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex); // Add event - _dispatchEvent(dragEl, 'add', dragEl, rootEl, oldIndex, newIndex); + _dispatchEvent(null, dragEl.parentNode, 'add', dragEl, rootEl, oldIndex, newIndex); // Remove event - _dispatchEvent(rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex); + _dispatchEvent(this, rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex); } else { // Remove clone @@ -718,13 +716,13 @@ newIndex = _index(dragEl); // drag & drop within the same list - _dispatchEvent(rootEl, 'update', dragEl, rootEl, oldIndex, newIndex); - _dispatchEvent(rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex); + _dispatchEvent(this, rootEl, 'update', dragEl, rootEl, oldIndex, newIndex); + _dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex); } } // Drag end event - Sortable.active && _dispatchEvent(rootEl, 'end', dragEl, rootEl, oldIndex, newIndex); + Sortable.active && _dispatchEvent(this, rootEl, 'end', dragEl, rootEl, oldIndex, newIndex); } // Nulling @@ -853,11 +851,9 @@ * Destroy */ destroy: function () { - var el = this.el, options = this.options; + var el = this.el; - _customEvents.forEach(function (name) { - _off(el, name.substr(2).toLowerCase(), options[name]); - }); + el[expando] = null; _off(el, 'mousedown', this._onTapStart); _off(el, 'touchstart', this._onTapStart); @@ -865,7 +861,7 @@ _off(el, 'dragover', this); _off(el, 'dragenter', this); - //remove draggable attributes + // Remove draggable attributes Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) { el.removeAttribute('draggable'); }); @@ -874,7 +870,7 @@ this._onDrop(); - this.el = null; + this.el = el = null; } }; @@ -1091,7 +1087,6 @@ throttle: _throttle, closest: _closest, toggleClass: _toggleClass, - dispatchEvent: _dispatchEvent, index: _index }; diff --git a/ng-sortable.js b/ng-sortable.js index 4d40d3d..a34c756 100644 --- a/ng-sortable.js +++ b/ng-sortable.js @@ -167,7 +167,10 @@ scope.$watch(ngSortable + '.' + name, function (value) { if (value !== void 0) { options[name] = value; - sortable.option(name, value); + + if (!/^on[A-Z]/.test(name)) { + sortable.option(name, value); + } } }); }); From c1f6cf50c4fc6fa16d12c0c804fe8464bbda0c0f Mon Sep 17 00:00:00 2001 From: RubaXa Date: Sat, 11 Apr 2015 11:13:32 +0300 Subject: [PATCH 20/27] #308: * allow drop text --- Sortable.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Sortable.js b/Sortable.js index 71f5a32..8fbb61a 100644 --- a/Sortable.js +++ b/Sortable.js @@ -530,10 +530,6 @@ isOwner = (activeGroup === group), canSort = options.sort; - if (!dragEl) { - return; - } - if (evt.preventDefault !== void 0) { evt.preventDefault(); !options.dragoverBubble && evt.stopPropagation(); @@ -754,8 +750,10 @@ var type = evt.type; if (type === 'dragover' || type === 'dragenter') { - this._onDragOver(evt); - _globalDragOver(evt); + if (dragEl) { + this._onDragOver(evt); + _globalDragOver(evt); + } } else if (type === 'drop' || type === 'dragend') { this._onDrop(evt); From 12df12c2b57c48831a728100a4bab5bda26ec896 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Mon, 13 Apr 2015 22:52:02 +0300 Subject: [PATCH 21/27] * correct ng-events --- ng-sortable.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ng-sortable.js b/ng-sortable.js index a34c756..d340a59 100644 --- a/ng-sortable.js +++ b/ng-sortable.js @@ -25,7 +25,7 @@ angular.module('ng-sortable', []) - .constant('version', '0.3.6') + .constant('version', '0.3.7') .directive('ngSortable', ['$parse', function ($parse) { var removed, nextSibling; @@ -78,7 +78,7 @@ /* jshint expr:true */ options[name] && options[name]({ - model: item, + model: item || source && source.item(evt.item), models: source && source.items(), oldIndex: evt.oldIndex, newIndex: evt.newIndex @@ -143,13 +143,13 @@ }, onUpdate: function (/**Event*/evt) { _sync(evt); - _emitEvent(evt, source && source.item(evt.item)); + _emitEvent(evt); }, onRemove: function (/**Event*/evt) { _emitEvent(evt, removed); }, onSort: function (/**Event*/evt) { - _emitEvent(evt, source && source.item(evt.item)); + _emitEvent(evt); } })); From 8db2fd26ecf3919e8fb78f697075e24fdce0bdc1 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Tue, 14 Apr 2015 21:36:49 +0300 Subject: [PATCH 22/27] v1.2.0: Events, Nested, Drop text, IE11, iframe and etc --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 44367c2..6b40d2a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sortablejs", "exportName": "Sortable", - "version": "1.1.1", + "version": "1.2.0", "devDependencies": { "grunt": "*", "grunt-version": "*", From b30ece99c5c87752db61b452e04467e0a3cf5f79 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Tue, 14 Apr 2015 21:37:41 +0300 Subject: [PATCH 23/27] v1.2.0: Events, Nested, Drop text, IE11, iframe and etc --- README.md | 4 ++-- Sortable.js | 2 +- Sortable.min.js | 4 ++-- bower.json | 2 +- component.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4474bfe..c685025 100644 --- a/README.md +++ b/README.md @@ -563,11 +563,11 @@ Link to the active instance. ```html - + - + diff --git a/Sortable.js b/Sortable.js index 8fbb61a..4527f6c 100644 --- a/Sortable.js +++ b/Sortable.js @@ -1089,7 +1089,7 @@ }; - Sortable.version = '1.1.1'; + Sortable.version = '1.2.0'; /** diff --git a/Sortable.min.js b/Sortable.min.js index 43a4fd7..6b3c942 100644 --- a/Sortable.min.js +++ b/Sortable.min.js @@ -1,2 +1,2 @@ -/*! Sortable 1.1.1 - MIT | git://github.com/rubaxa/Sortable.git */ -!function(a){"use strict";"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a():"undefined"!=typeof Package?Sortable=a():window.Sortable=a()}(function(){"use strict";function a(a,b){this.el=a,this.options=b=q({},b);var d={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(a.nodeName)?"li":">*",ghostClass:"sortable-ghost",ignore:"a, img",filter:null,animation:0,setData:function(a,b){a.setData("Text",b.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0};for(var e in d)!(e in b)&&(b[e]=d[e]);var g=b.group;g&&"object"==typeof g||(g=b.group={name:g}),["pull","put"].forEach(function(a){a in g||(g[a]=!0)}),O.forEach(function(d){b[d]=c(this,b[d]||P),f(a,d.substr(2).toLowerCase(),b[d])},this),b.groups=" "+g.name+(g.put.join?" "+g.put.join(" "):"")+" ",a[H]=b;for(var h in this)"_"===h.charAt(0)&&(this[h]=c(this,this[h]));f(a,"mousedown",this._onTapStart),f(a,"touchstart",this._onTapStart),f(a,"dragover",this),f(a,"dragenter",this),S.push(this._onDragOver),b.store&&this.sort(b.store.get(this))}function b(a){t&&t.state!==a&&(i(t,"display",a?"none":""),!a&&t.state&&u.insertBefore(t,r),t.state=a)}function c(a,b){var c=R.call(arguments,2);return b.bind?b.bind.apply(b,[a].concat(c)):function(){return b.apply(a,c.concat(R.call(arguments)))}}function d(a,b,c){if(a){c=c||J,b=b.split(".");var d=b.shift().toUpperCase(),e=new RegExp("\\s("+b.join("|")+")\\s","g");do if(">*"===d&&a.parentNode===c||(""===d||a.nodeName.toUpperCase()==d)&&(!b.length||((" "+a.className+" ").match(e)||[]).length==b.length))return a;while(a!==c&&(a=a.parentNode))}return null}function e(a){a.dataTransfer.dropEffect="move",a.preventDefault()}function f(a,b,c){a.addEventListener(b,c,!1)}function g(a,b,c){a.removeEventListener(b,c,!1)}function h(a,b,c){if(a)if(a.classList)a.classList[c?"add":"remove"](b);else{var d=(" "+a.className+" ").replace(G," ").replace(" "+b+" "," ");a.className=(d+(c?" "+b:"")).replace(G," ")}}function i(a,b,c){var d=a&&a.style;if(d){if(void 0===c)return J.defaultView&&J.defaultView.getComputedStyle?c=J.defaultView.getComputedStyle(a,""):a.currentStyle&&(c=a.currentStyle),void 0===b?c:c[b];b in d||(b="-webkit-"+b),d[b]=c+("string"==typeof c?"":"px")}}function j(a,b,c){if(a){var d=a.getElementsByTagName(b),e=0,f=d.length;if(c)for(;f>e;e++)c(d[e],e);return d}return[]}function k(a){a.draggable=!1}function l(){M=!1}function m(a,b){var c=a.lastElementChild,d=c.getBoundingClientRect();return b.clientY-(d.top+d.height)>5&&c}function n(a){for(var b=a.tagName+a.className+a.src+a.href+a.textContent,c=b.length,d=0;c--;)d+=b.charCodeAt(c);return d.toString(36)}function o(a){for(var b=0;a&&(a=a.previousElementSibling);)"TEMPLATE"!==a.nodeName.toUpperCase()&&b++;return b}function p(a,b){var c,d;return function(){void 0===c&&(c=arguments,d=this,setTimeout(function(){1===c.length?a.call(d,c[0]):a.apply(d,c),c=void 0},b))}}function q(a,b){if(a&&b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}var r,s,t,u,v,w,x,y,z,A,B,C,D,E,F={},G=/\s+/g,H="Sortable"+(new Date).getTime(),I=window,J=I.document,K=I.parseInt,L=!!("draggable"in J.createElement("div")),M=!1,N=function(a,b,c,d,e,f){var g=J.createEvent("Event");g.initEvent(b,!0,!0),g.item=c||a,g.from=d||a,g.clone=t,g.oldIndex=e,g.newIndex=f,a.dispatchEvent(g)},O="onAdd onUpdate onRemove onStart onEnd onFilter onSort".split(" "),P=function(){},Q=Math.abs,R=[].slice,S=[],T=p(function(a,b,c){if(c&&b.scroll){var d,e,f,g,h=b.scrollSensitivity,i=b.scrollSpeed,j=a.clientX,k=a.clientY,l=window.innerWidth,m=window.innerHeight;if(x!==c&&(w=b.scroll,x=c,w===!0)){w=c;do if(w.offsetWidth=l-j)-(h>=j),g=(h>=m-k)-(h>=k),(f||g)&&(d=I)),(F.vx!==f||F.vy!==g||F.el!==d)&&(F.el=d,F.vx=f,F.vy=g,clearInterval(F.pid),d&&(F.pid=setInterval(function(){d===I?I.scrollTo(I.pageXOffset+f*i,I.pageYOffset+g*i):(g&&(d.scrollTop+=g*i),f&&(d.scrollLeft+=f*i))},24)))}},30);return a.prototype={constructor:a,_onTapStart:function(a){var b=this.el,c=this.options,e=a.type,f=a.touches&&a.touches[0],g=(f||a).target,h=g,i=c.filter;if(!("mousedown"===e&&0!==a.button||c.disabled)&&(g=d(g,c.draggable,b))){if(A=o(g),"function"==typeof i){if(i.call(this,a,g,this))return N(h,"filter",g,b,A),void a.preventDefault()}else if(i&&(i=i.split(",").some(function(a){return a=d(h,a.trim(),b),a?(N(a,"filter",g,b,A),!0):void 0})))return void a.preventDefault();(!c.handle||d(h,c.handle,b))&&this._prepareDragStart(a,f,g)}},_prepareDragStart:function(a,b,c){var d,e=this,g=e.el,h=e.options,i=g.ownerDocument;c&&!r&&c.parentNode===g&&(D=a,u=g,r=c,v=r.nextSibling,C=h.group,d=function(){e._disableDelayedDrag(),r.draggable=!0,h.ignore.split(",").forEach(function(a){j(r,a.trim(),k)}),e._triggerDragStart(b)},f(i,"mouseup",e._onDrop),f(i,"touchend",e._onDrop),f(i,"touchcancel",e._onDrop),h.delay?(f(i,"mousemove",e._disableDelayedDrag),f(i,"touchmove",e._disableDelayedDrag),e._dragStartTimer=setTimeout(d,h.delay)):d())},_disableDelayedDrag:function(){var a=this.el.ownerDocument;clearTimeout(this._dragStartTimer),g(a,"mousemove",this._disableDelayedDrag),g(a,"touchmove",this._disableDelayedDrag)},_triggerDragStart:function(a){a?(D={target:r,clientX:a.clientX,clientY:a.clientY},this._onDragStart(D,"touch")):L?(f(r,"dragend",this),f(u,"dragstart",this._onDragStart)):this._onDragStart(D,!0);try{J.selection?J.selection.empty():window.getSelection().removeAllRanges()}catch(b){}},_dragStarted:function(){u&&r&&(h(r,this.options.ghostClass,!0),a.active=this,N(u,"start",r,u,A))},_emulateDragOver:function(){if(E){i(s,"display","none");var a=J.elementFromPoint(E.clientX,E.clientY),b=a,c=" "+this.options.group.name,d=S.length;if(b)do{if(b[H]&&b[H].groups.indexOf(c)>-1){for(;d--;)S[d]({clientX:E.clientX,clientY:E.clientY,target:a,rootEl:b});break}a=b}while(b=b.parentNode);i(s,"display","")}},_onTouchMove:function(a){if(D){var b=a.touches?a.touches[0]:a,c=b.clientX-D.clientX,d=b.clientY-D.clientY,e=a.touches?"translate3d("+c+"px,"+d+"px,0)":"translate("+c+"px,"+d+"px)";E=b,i(s,"webkitTransform",e),i(s,"mozTransform",e),i(s,"msTransform",e),i(s,"transform",e),a.preventDefault()}},_onDragStart:function(a,b){var c=a.dataTransfer,d=this.options;if(this._offUpEvents(),"clone"==C.pull&&(t=r.cloneNode(!0),i(t,"display","none"),u.insertBefore(t,r)),b){var e,g=r.getBoundingClientRect(),h=i(r);s=r.cloneNode(!0),i(s,"top",g.top-K(h.marginTop,10)),i(s,"left",g.left-K(h.marginLeft,10)),i(s,"width",g.width),i(s,"height",g.height),i(s,"opacity","0.8"),i(s,"position","fixed"),i(s,"zIndex","100000"),u.appendChild(s),e=s.getBoundingClientRect(),i(s,"width",2*g.width-e.width),i(s,"height",2*g.height-e.height),"touch"===b?(f(J,"touchmove",this._onTouchMove),f(J,"touchend",this._onDrop),f(J,"touchcancel",this._onDrop)):(f(J,"mousemove",this._onTouchMove),f(J,"mouseup",this._onDrop)),this._loopId=setInterval(this._emulateDragOver,150)}else c&&(c.effectAllowed="move",d.setData&&d.setData.call(this,c,r)),f(J,"drop",this);setTimeout(this._dragStarted,0)},_onDragOver:function(a){var c,e,f,g=this.el,h=this.options,j=h.group,k=j.put,n=C===j,o=h.sort;if(r&&(void 0!==a.preventDefault&&(a.preventDefault(),!h.dragoverBubble&&a.stopPropagation()),C&&!h.disabled&&(n?o||(f=!u.contains(r)):C.pull&&k&&(C.name===j.name||k.indexOf&&~k.indexOf(C.name)))&&(void 0===a.rootEl||a.rootEl===this.el))){if(T(a,h,this.el),M)return;if(c=d(a.target,h.draggable,g),e=r.getBoundingClientRect(),f)return b(!0),void(t||v?u.insertBefore(r,t||v):o||u.appendChild(r));if(0===g.children.length||g.children[0]===s||g===a.target&&(c=m(g,a))){if(c){if(c.animated)return;q=c.getBoundingClientRect()}b(n),g.appendChild(r),this._animate(e,r),c&&this._animate(q,c)}else if(c&&!c.animated&&c!==r&&void 0!==c.parentNode[H]){y!==c&&(y=c,z=i(c));var p,q=c.getBoundingClientRect(),w=q.right-q.left,x=q.bottom-q.top,A=/left|right|inline/.test(z.cssFloat+z.display),B=c.offsetWidth>r.offsetWidth,D=c.offsetHeight>r.offsetHeight,E=(A?(a.clientX-q.left)/w:(a.clientY-q.top)/x)>.5,F=c.nextElementSibling;M=!0,setTimeout(l,30),b(n),p=A?c.previousElementSibling===r&&!B||E&&B:F!==r&&!D||E&&D,p&&!F?g.appendChild(r):c.parentNode.insertBefore(r,p?F:c),this._animate(e,r),this._animate(q,c)}}},_animate:function(a,b){var c=this.options.animation;if(c){var d=b.getBoundingClientRect();i(b,"transition","none"),i(b,"transform","translate3d("+(a.left-d.left)+"px,"+(a.top-d.top)+"px,0)"),b.offsetWidth,i(b,"transition","all "+c+"ms"),i(b,"transform","translate3d(0,0,0)"),clearTimeout(b.animated),b.animated=setTimeout(function(){i(b,"transition",""),i(b,"transform",""),b.animated=!1},c)}},_offUpEvents:function(){var a=this.el.ownerDocument;g(J,"touchmove",this._onTouchMove),g(a,"mouseup",this._onDrop),g(a,"touchend",this._onDrop),g(a,"touchcancel",this._onDrop)},_onDrop:function(b){var c=this.el,d=this.options;clearInterval(this._loopId),clearInterval(F.pid),clearTimeout(this.dragStartTimer),g(J,"drop",this),g(J,"mousemove",this._onTouchMove),g(c,"dragstart",this._onDragStart),this._offUpEvents(),b&&(b.preventDefault(),!d.dropBubble&&b.stopPropagation(),s&&s.parentNode.removeChild(s),r&&(g(r,"dragend",this),k(r),h(r,this.options.ghostClass,!1),u!==r.parentNode?(B=o(r),N(r.parentNode,"sort",r,u,A,B),N(u,"sort",r,u,A,B),N(r,"add",r,u,A,B),N(u,"remove",r,u,A,B)):(t&&t.parentNode.removeChild(t),r.nextSibling!==v&&(B=o(r),N(u,"update",r,u,A,B),N(u,"sort",r,u,A,B))),a.active&&N(u,"end",r,u,A,B)),u=r=s=v=t=w=x=D=E=y=z=C=a.active=null,this.save())},handleEvent:function(a){var b=a.type;"dragover"===b||"dragenter"===b?(this._onDragOver(a),e(a)):("drop"===b||"dragend"===b)&&this._onDrop(a)},toArray:function(){for(var a,b=[],c=this.el.children,e=0,f=c.length,g=this.options;f>e;e++)a=c[e],d(a,g.draggable,this.el)&&b.push(a.getAttribute(g.dataIdAttr)||n(a));return b},sort:function(a){var b={},c=this.el;this.toArray().forEach(function(a,e){var f=c.children[e];d(f,this.options.draggable,c)&&(b[a]=f)},this),a.forEach(function(a){b[a]&&(c.removeChild(b[a]),c.appendChild(b[a]))})},save:function(){var a=this.options.store;a&&a.set(this)},closest:function(a,b){return d(a,b||this.options.draggable,this.el)},option:function(a,b){var c=this.options;return void 0===b?c[a]:void(c[a]=b)},destroy:function(){var a=this.el,b=this.options;O.forEach(function(c){g(a,c.substr(2).toLowerCase(),b[c])}),g(a,"mousedown",this._onTapStart),g(a,"touchstart",this._onTapStart),g(a,"dragover",this),g(a,"dragenter",this),Array.prototype.forEach.call(a.querySelectorAll("[draggable]"),function(a){a.removeAttribute("draggable")}),S.splice(S.indexOf(this._onDragOver),1),this._onDrop(),this.el=null}},a.utils={on:f,off:g,css:i,find:j,bind:c,is:function(a,b){return!!d(a,b,a)},extend:q,throttle:p,closest:d,toggleClass:h,dispatchEvent:N,index:o},a.version="1.1.1",a.create=function(b,c){return new a(b,c)},a}); \ No newline at end of file +/*! Sortable 1.2.0 - MIT | git://github.com/rubaxa/Sortable.git */ +!function(a){"use strict";"function"==typeof define&&define.amd?define(a):"undefined"!=typeof module&&"undefined"!=typeof module.exports?module.exports=a():"undefined"!=typeof Package?Sortable=a():window.Sortable=a()}(function(){"use strict";function a(a,b){this.el=a,this.options=b=q({},b),a[H]=this;var d={group:Math.random(),sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,draggable:/[uo]l/i.test(a.nodeName)?"li":">*",ghostClass:"sortable-ghost",ignore:"a, img",filter:null,animation:0,setData:function(a,b){a.setData("Text",b.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0};for(var e in d)!(e in b)&&(b[e]=d[e]);var g=b.group;g&&"object"==typeof g||(g=b.group={name:g}),["pull","put"].forEach(function(a){a in g||(g[a]=!0)}),b.groups=" "+g.name+(g.put.join?" "+g.put.join(" "):"")+" ";for(var h in this)"_"===h.charAt(0)&&(this[h]=c(this,this[h]));f(a,"mousedown",this._onTapStart),f(a,"touchstart",this._onTapStart),f(a,"dragover",this),f(a,"dragenter",this),Q.push(this._onDragOver),b.store&&this.sort(b.store.get(this))}function b(a){t&&t.state!==a&&(i(t,"display",a?"none":""),!a&&t.state&&u.insertBefore(t,r),t.state=a)}function c(a,b){var c=P.call(arguments,2);return b.bind?b.bind.apply(b,[a].concat(c)):function(){return b.apply(a,c.concat(P.call(arguments)))}}function d(a,b,c){if(a){c=c||J,b=b.split(".");var d=b.shift().toUpperCase(),e=new RegExp("\\s("+b.join("|")+")\\s","g");do if(">*"===d&&a.parentNode===c||(""===d||a.nodeName.toUpperCase()==d)&&(!b.length||((" "+a.className+" ").match(e)||[]).length==b.length))return a;while(a!==c&&(a=a.parentNode))}return null}function e(a){a.dataTransfer.dropEffect="move",a.preventDefault()}function f(a,b,c){a.addEventListener(b,c,!1)}function g(a,b,c){a.removeEventListener(b,c,!1)}function h(a,b,c){if(a)if(a.classList)a.classList[c?"add":"remove"](b);else{var d=(" "+a.className+" ").replace(G," ").replace(" "+b+" "," ");a.className=(d+(c?" "+b:"")).replace(G," ")}}function i(a,b,c){var d=a&&a.style;if(d){if(void 0===c)return J.defaultView&&J.defaultView.getComputedStyle?c=J.defaultView.getComputedStyle(a,""):a.currentStyle&&(c=a.currentStyle),void 0===b?c:c[b];b in d||(b="-webkit-"+b),d[b]=c+("string"==typeof c?"":"px")}}function j(a,b,c){if(a){var d=a.getElementsByTagName(b),e=0,f=d.length;if(c)for(;f>e;e++)c(d[e],e);return d}return[]}function k(a){a.draggable=!1}function l(){M=!1}function m(a,b){var c=a.lastElementChild,d=c.getBoundingClientRect();return b.clientY-(d.top+d.height)>5&&c}function n(a){for(var b=a.tagName+a.className+a.src+a.href+a.textContent,c=b.length,d=0;c--;)d+=b.charCodeAt(c);return d.toString(36)}function o(a){for(var b=0;a&&(a=a.previousElementSibling);)"TEMPLATE"!==a.nodeName.toUpperCase()&&b++;return b}function p(a,b){var c,d;return function(){void 0===c&&(c=arguments,d=this,setTimeout(function(){1===c.length?a.call(d,c[0]):a.apply(d,c),c=void 0},b))}}function q(a,b){if(a&&b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}var r,s,t,u,v,w,x,y,z,A,B,C,D,E,F={},G=/\s+/g,H="Sortable"+(new Date).getTime(),I=window,J=I.document,K=I.parseInt,L=!!("draggable"in J.createElement("div")),M=!1,N=function(a,b,c,d,e,f,g){var h=J.createEvent("Event"),i=(a||b[H]).options,j="on"+c.charAt(0).toUpperCase()+c.substr(1);h.initEvent(c,!0,!0),h.item=d||b,h.from=e||b,h.clone=t,h.oldIndex=f,h.newIndex=g,i[j]&&i[j].call(a,h),b.dispatchEvent(h)},O=Math.abs,P=[].slice,Q=[],R=p(function(a,b,c){if(c&&b.scroll){var d,e,f,g,h=b.scrollSensitivity,i=b.scrollSpeed,j=a.clientX,k=a.clientY,l=window.innerWidth,m=window.innerHeight;if(x!==c&&(w=b.scroll,x=c,w===!0)){w=c;do if(w.offsetWidth=l-j)-(h>=j),g=(h>=m-k)-(h>=k),(f||g)&&(d=I)),(F.vx!==f||F.vy!==g||F.el!==d)&&(F.el=d,F.vx=f,F.vy=g,clearInterval(F.pid),d&&(F.pid=setInterval(function(){d===I?I.scrollTo(I.pageXOffset+f*i,I.pageYOffset+g*i):(g&&(d.scrollTop+=g*i),f&&(d.scrollLeft+=f*i))},24)))}},30);return a.prototype={constructor:a,_onTapStart:function(a){var b=this,c=this.el,e=this.options,f=a.type,g=a.touches&&a.touches[0],h=(g||a).target,i=h,j=e.filter;if(!("mousedown"===f&&0!==a.button||e.disabled)&&(h=d(h,e.draggable,c))){if(A=o(h),"function"==typeof j){if(j.call(this,a,h,this))return N(b,i,"filter",h,c,A),void a.preventDefault()}else if(j&&(j=j.split(",").some(function(a){return a=d(i,a.trim(),c),a?(N(b,a,"filter",h,c,A),!0):void 0})))return void a.preventDefault();(!e.handle||d(i,e.handle,c))&&this._prepareDragStart(a,g,h)}},_prepareDragStart:function(a,b,c){var d,e=this,g=e.el,h=e.options,i=g.ownerDocument;c&&!r&&c.parentNode===g&&(D=a,u=g,r=c,v=r.nextSibling,C=h.group,d=function(){e._disableDelayedDrag(),r.draggable=!0,h.ignore.split(",").forEach(function(a){j(r,a.trim(),k)}),e._triggerDragStart(b)},f(i,"mouseup",e._onDrop),f(i,"touchend",e._onDrop),f(i,"touchcancel",e._onDrop),h.delay?(f(i,"mousemove",e._disableDelayedDrag),f(i,"touchmove",e._disableDelayedDrag),e._dragStartTimer=setTimeout(d,h.delay)):d())},_disableDelayedDrag:function(){var a=this.el.ownerDocument;clearTimeout(this._dragStartTimer),g(a,"mousemove",this._disableDelayedDrag),g(a,"touchmove",this._disableDelayedDrag)},_triggerDragStart:function(a){a?(D={target:r,clientX:a.clientX,clientY:a.clientY},this._onDragStart(D,"touch")):L?(f(r,"dragend",this),f(u,"dragstart",this._onDragStart)):this._onDragStart(D,!0);try{J.selection?J.selection.empty():window.getSelection().removeAllRanges()}catch(b){}},_dragStarted:function(){u&&r&&(h(r,this.options.ghostClass,!0),a.active=this,N(this,u,"start",r,u,A))},_emulateDragOver:function(){if(E){i(s,"display","none");var a=J.elementFromPoint(E.clientX,E.clientY),b=a,c=" "+this.options.group.name,d=Q.length;if(b)do{if(b[H]&&b[H].options.groups.indexOf(c)>-1){for(;d--;)Q[d]({clientX:E.clientX,clientY:E.clientY,target:a,rootEl:b});break}a=b}while(b=b.parentNode);i(s,"display","")}},_onTouchMove:function(a){if(D){var b=a.touches?a.touches[0]:a,c=b.clientX-D.clientX,d=b.clientY-D.clientY,e=a.touches?"translate3d("+c+"px,"+d+"px,0)":"translate("+c+"px,"+d+"px)";E=b,i(s,"webkitTransform",e),i(s,"mozTransform",e),i(s,"msTransform",e),i(s,"transform",e),a.preventDefault()}},_onDragStart:function(a,b){var c=a.dataTransfer,d=this.options;if(this._offUpEvents(),"clone"==C.pull&&(t=r.cloneNode(!0),i(t,"display","none"),u.insertBefore(t,r)),b){var e,g=r.getBoundingClientRect(),h=i(r);s=r.cloneNode(!0),i(s,"top",g.top-K(h.marginTop,10)),i(s,"left",g.left-K(h.marginLeft,10)),i(s,"width",g.width),i(s,"height",g.height),i(s,"opacity","0.8"),i(s,"position","fixed"),i(s,"zIndex","100000"),u.appendChild(s),e=s.getBoundingClientRect(),i(s,"width",2*g.width-e.width),i(s,"height",2*g.height-e.height),"touch"===b?(f(J,"touchmove",this._onTouchMove),f(J,"touchend",this._onDrop),f(J,"touchcancel",this._onDrop)):(f(J,"mousemove",this._onTouchMove),f(J,"mouseup",this._onDrop)),this._loopId=setInterval(this._emulateDragOver,150)}else c&&(c.effectAllowed="move",d.setData&&d.setData.call(this,c,r)),f(J,"drop",this);setTimeout(this._dragStarted,0)},_onDragOver:function(a){var c,e,f,g=this.el,h=this.options,j=h.group,k=j.put,n=C===j,o=h.sort;if(void 0!==a.preventDefault&&(a.preventDefault(),!h.dragoverBubble&&a.stopPropagation()),C&&!h.disabled&&(n?o||(f=!u.contains(r)):C.pull&&k&&(C.name===j.name||k.indexOf&&~k.indexOf(C.name)))&&(void 0===a.rootEl||a.rootEl===this.el)){if(R(a,h,this.el),M)return;if(c=d(a.target,h.draggable,g),e=r.getBoundingClientRect(),f)return b(!0),void(t||v?u.insertBefore(r,t||v):o||u.appendChild(r));if(0===g.children.length||g.children[0]===s||g===a.target&&(c=m(g,a))){if(c){if(c.animated)return;q=c.getBoundingClientRect()}b(n),g.appendChild(r),this._animate(e,r),c&&this._animate(q,c)}else if(c&&!c.animated&&c!==r&&void 0!==c.parentNode[H]){y!==c&&(y=c,z=i(c));var p,q=c.getBoundingClientRect(),w=q.right-q.left,x=q.bottom-q.top,A=/left|right|inline/.test(z.cssFloat+z.display),B=c.offsetWidth>r.offsetWidth,D=c.offsetHeight>r.offsetHeight,E=(A?(a.clientX-q.left)/w:(a.clientY-q.top)/x)>.5,F=c.nextElementSibling;M=!0,setTimeout(l,30),b(n),p=A?c.previousElementSibling===r&&!B||E&&B:F!==r&&!D||E&&D,p&&!F?g.appendChild(r):c.parentNode.insertBefore(r,p?F:c),this._animate(e,r),this._animate(q,c)}}},_animate:function(a,b){var c=this.options.animation;if(c){var d=b.getBoundingClientRect();i(b,"transition","none"),i(b,"transform","translate3d("+(a.left-d.left)+"px,"+(a.top-d.top)+"px,0)"),b.offsetWidth,i(b,"transition","all "+c+"ms"),i(b,"transform","translate3d(0,0,0)"),clearTimeout(b.animated),b.animated=setTimeout(function(){i(b,"transition",""),i(b,"transform",""),b.animated=!1},c)}},_offUpEvents:function(){var a=this.el.ownerDocument;g(J,"touchmove",this._onTouchMove),g(a,"mouseup",this._onDrop),g(a,"touchend",this._onDrop),g(a,"touchcancel",this._onDrop)},_onDrop:function(b){var c=this.el,d=this.options;clearInterval(this._loopId),clearInterval(F.pid),clearTimeout(this.dragStartTimer),g(J,"drop",this),g(J,"mousemove",this._onTouchMove),g(c,"dragstart",this._onDragStart),this._offUpEvents(),b&&(b.preventDefault(),!d.dropBubble&&b.stopPropagation(),s&&s.parentNode.removeChild(s),r&&(g(r,"dragend",this),k(r),h(r,this.options.ghostClass,!1),u!==r.parentNode?(B=o(r),N(null,r.parentNode,"sort",r,u,A,B),N(this,u,"sort",r,u,A,B),N(null,r.parentNode,"add",r,u,A,B),N(this,u,"remove",r,u,A,B)):(t&&t.parentNode.removeChild(t),r.nextSibling!==v&&(B=o(r),N(this,u,"update",r,u,A,B),N(this,u,"sort",r,u,A,B))),a.active&&N(this,u,"end",r,u,A,B)),u=r=s=v=t=w=x=D=E=y=z=C=a.active=null,this.save())},handleEvent:function(a){var b=a.type;"dragover"===b||"dragenter"===b?r&&(this._onDragOver(a),e(a)):("drop"===b||"dragend"===b)&&this._onDrop(a)},toArray:function(){for(var a,b=[],c=this.el.children,e=0,f=c.length,g=this.options;f>e;e++)a=c[e],d(a,g.draggable,this.el)&&b.push(a.getAttribute(g.dataIdAttr)||n(a));return b},sort:function(a){var b={},c=this.el;this.toArray().forEach(function(a,e){var f=c.children[e];d(f,this.options.draggable,c)&&(b[a]=f)},this),a.forEach(function(a){b[a]&&(c.removeChild(b[a]),c.appendChild(b[a]))})},save:function(){var a=this.options.store;a&&a.set(this)},closest:function(a,b){return d(a,b||this.options.draggable,this.el)},option:function(a,b){var c=this.options;return void 0===b?c[a]:void(c[a]=b)},destroy:function(){var a=this.el;a[H]=null,g(a,"mousedown",this._onTapStart),g(a,"touchstart",this._onTapStart),g(a,"dragover",this),g(a,"dragenter",this),Array.prototype.forEach.call(a.querySelectorAll("[draggable]"),function(a){a.removeAttribute("draggable")}),Q.splice(Q.indexOf(this._onDragOver),1),this._onDrop(),this.el=a=null}},a.utils={on:f,off:g,css:i,find:j,bind:c,is:function(a,b){return!!d(a,b,a)},extend:q,throttle:p,closest:d,toggleClass:h,index:o},a.version="1.2.0",a.create=function(b,c){return new a(b,c)},a}); \ No newline at end of file diff --git a/bower.json b/bower.json index c5e42aa..4ae98d7 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "Sortable", "main": "Sortable.js", - "version": "1.1.1", + "version": "1.2.0", "homepage": "http://rubaxa.github.io/Sortable/", "authors": [ "RubaXa " diff --git a/component.json b/component.json index de414f1..04a090a 100644 --- a/component.json +++ b/component.json @@ -1,7 +1,7 @@ { "name": "Sortable", "main": "Sortable.js", - "version": "1.1.1", + "version": "1.2.0", "homepage": "http://rubaxa.github.io/Sortable/", "repo": "RubaXa/Sortable", "authors": [ From 711695c9119531208548aca9bfe7736171aa278f Mon Sep 17 00:00:00 2001 From: RubaXa Date: Tue, 14 Apr 2015 22:13:19 +0300 Subject: [PATCH 24/27] #281: + exportName for jQuery extention --- Gruntfile.js | 25 +++++++++++++++++++------ README.md | 1 + 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 26e1656..5d67184 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -35,9 +35,7 @@ module.exports = function (grunt) { } }, jquery: { - files: { - 'jquery.fn.sortable.min.js': 'jquery.fn.sortable.js' - } + files: {} } }, @@ -54,22 +52,37 @@ module.exports = function (grunt) { }); - grunt.registerTask('jquery', function (arg) { + grunt.registerTask('jquery', function (exportName, uglify) { + if (exportName == 'min') { + exportName = null; + uglify = 'min'; + } + + if (!exportName) { + exportName = 'sortable'; + } + var fs = require('fs'), - filename = 'jquery.fn.sortable.js'; + filename = 'jquery.fn.' + exportName + '.js'; grunt.log.oklns(filename); fs.writeFileSync( filename, (fs.readFileSync('jquery.binding.js') + '') + .replace('$.fn.sortable', '$.fn.' + exportName) .replace('/* CODE */', (fs.readFileSync('Sortable.js') + '') .replace(/^[\s\S]*?function[\s\S]*?(var[\s\S]+)\/\/\s+Export[\s\S]+/, '$1') ) ); - if (arg === 'min') { + if (uglify) { + var opts = {}; + + opts['jquery.fn.' + exportName + '.min.js'] = filename; + grunt.config.set('uglify.jquery.files', opts); + grunt.task.run('uglify:jquery'); } }); diff --git a/README.md b/README.md index c685025..60caa87 100644 --- a/README.md +++ b/README.md @@ -603,6 +603,7 @@ Now you can use `jquery.fn.sortable.js`:
$("#list").sortable("{method-name}", "foo", "bar"); // call an instance method with parameters ``` +And `grunt jquery:mySortableFunc` → `jquery.fn.mySortableFunc.js` --- From 34328a81b527218358b4bb17ca8613d2891a2c82 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Wed, 15 Apr 2015 13:19:18 +0300 Subject: [PATCH 25/27] #273: + onMove event --- README.md | 10 +++++ Sortable.js | 113 +++++++++++++++++++++++++++++++++------------------- 2 files changed, 82 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 60caa87..a935ba4 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,16 @@ var sortable = new Sortable(el, { // Attempt to drag a filtered element onFilter: function (/**Event*/evt) { var itemEl = evt.item; // HTMLElement receiving the `mousedown|tapstart` event. + }, + + // Event when you move an item in the list or between lists + onMove: function (/**Event*/evt) { + // Example: http://jsbin.com/tuyafe/1/edit?js,output + evt.dragged; // dragged HTMLElement + evt.draggedRect; // TextRectangle {left, top, right и bottom} + evt.related; // HTMLElement on which have guided + evt.relatedRect; // TextRectangle + // retrun false; — for cancel } }); ``` diff --git a/Sortable.js b/Sortable.js index 4527f6c..346887e 100644 --- a/Sortable.js +++ b/Sortable.js @@ -58,27 +58,6 @@ _silent = false, - _dispatchEvent = function (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.item = targetEl || rootEl; - evt.from = fromEl || rootEl; - evt.clone = cloneEl; - - evt.oldIndex = startIndex; - evt.newIndex = newIndex; - - if (options[onName]) { - options[onName].call(sortable, evt); - } - - rootEl.dispatchEvent(evt); - }, - abs = Math.abs, slice = [].slice, @@ -537,13 +516,13 @@ if (activeGroup && !options.disabled && (isOwner - ? canSort || (revert = !rootEl.contains(dragEl)) + ? 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) + (evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback ) { // Smart auto-scrolling _autoScroll(evt, options, this.el); @@ -582,9 +561,11 @@ _cloneHide(isOwner); - el.appendChild(dragEl); - this._animate(dragRect, dragEl); - target && this._animate(targetRect, target); + if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect)) { + 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) { @@ -604,25 +585,27 @@ after ; - _silent = true; - setTimeout(_unsilent, 30); + if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect)) { + _silent = true; + setTimeout(_unsilent, 30); - _cloneHide(isOwner); + _cloneHide(isOwner); - if (floating) { - after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide; - } else { - after = (nextSibling !== dragEl) && !isLong || halfway && isLong; - } + 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); - } + if (after && !nextSibling) { + el.appendChild(dragEl); + } else { + target.parentNode.insertBefore(dragEl, after ? nextSibling : target); + } - this._animate(dragRect, dragEl); - this._animate(targetRect, target); + this._animate(dragRect, dragEl); + this._animate(targetRect, target); + } } } }, @@ -986,6 +969,54 @@ } + + 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; + + if (options[onName]) { + options[onName].call(sortable, evt); + } + + rootEl.dispatchEvent(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 !== false; + } + + function _disableDraggable(el) { el.draggable = false; } From a89b3b3edc6422f1b3540a4811434d4900d91719 Mon Sep 17 00:00:00 2001 From: RubaXa Date: Wed, 15 Apr 2015 14:27:28 +0300 Subject: [PATCH 26/27] #347: + moveVector --- Sortable.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Sortable.js b/Sortable.js index 346887e..7888d05 100644 --- a/Sortable.js +++ b/Sortable.js @@ -561,7 +561,7 @@ _cloneHide(isOwner); - if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect)) { + if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect) !== false) { el.appendChild(dragEl); this._animate(dragRect, dragEl); target && this._animate(targetRect, target); @@ -582,16 +582,20 @@ 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), after ; - if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect)) { + if (moveVector !== false) { _silent = true; setTimeout(_unsilent, 30); _cloneHide(isOwner); - if (floating) { + 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; @@ -1013,7 +1017,7 @@ retVal = onMoveFn.call(sortable, evt); } - return retVal !== false; + return retVal; } From 14864bc067dc937b88031dac77344ad9c44fa7aa Mon Sep 17 00:00:00 2001 From: RubaXa Date: Thu, 16 Apr 2015 18:15:27 +0300 Subject: [PATCH 27/27] #335: call save only if 'active' --- Sortable.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Sortable.js b/Sortable.js index 7888d05..6de4e05 100644 --- a/Sortable.js +++ b/Sortable.js @@ -704,8 +704,13 @@ } } - // Drag end event - Sortable.active && _dispatchEvent(this, rootEl, 'end', dragEl, rootEl, oldIndex, newIndex); + if (Sortable.active) { + // Drag end event + _dispatchEvent(this, rootEl, 'end', dragEl, rootEl, oldIndex, newIndex); + + // Save sorting + this.save(); + } } // Nulling @@ -726,9 +731,6 @@ activeGroup = Sortable.active = null; - - // Save sorting - this.save(); } },