diff --git a/README.md b/README.md index f63afd7..8d7504b 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ ``` +Note: The sortable binding assumes that the child "templates" have a single container element. You cannot use containerless bindings (comment-based) bindings at the top-level of your template, as the jQuery draggable/sortable functionality needs an element to operate on. + **Additional Options** * **connectClass** - specify the class that should be used to indicate a droppable target. The default class is "ko_container". This value can be passed in the binding or configured globally by setting `ko.bindingHandlers.sortable.connectClass`. diff --git a/build/knockout-sortable.js b/build/knockout-sortable.js index 148e980..ee6f3d7 100644 --- a/build/knockout-sortable.js +++ b/build/knockout-sortable.js @@ -1,4 +1,4 @@ -// knockout-sortable 0.8.1 | (c) 2013 Ryan Niemeyer | http://www.opensource.org/licenses/mit-license +// knockout-sortable 0.8.2 | (c) 2013 Ryan Niemeyer | http://www.opensource.org/licenses/mit-license ;(function(factory) { if (typeof define === "function" && define.amd) { // AMD anonymous module @@ -87,9 +87,9 @@ sortable = {}, startActual, updateActual; - //remove leading/trailing text nodes from anonymous templates + //remove leading/trailing non-elements from anonymous templates ko.utils.arrayForEach(element.childNodes, function(node) { - if (node && node.nodeType === 3) { + if (node && node.nodeType !== 1) { node.parentNode.removeChild(node); } }); diff --git a/build/knockout-sortable.min.js b/build/knockout-sortable.min.js index dc9db54..8f18e41 100644 --- a/build/knockout-sortable.min.js +++ b/build/knockout-sortable.min.js @@ -1,2 +1,2 @@ -// knockout-sortable 0.8.1 | (c) 2013 Ryan Niemeyer | http://www.opensource.org/licenses/mit-license -!function(a){"function"==typeof define&&define.amd?define(["knockout","jquery","jquery.ui.sortable"],a):a(window.ko,jQuery)}(function(a,b,c){var d="ko_sortItem",e="ko_sourceIndex",f="ko_sortList",g="ko_parentList",h="ko_dragItem",i=a.utils.unwrapObservable,j=a.utils.domData.get,k=a.utils.domData.set,l=function(b,c){a.utils.arrayForEach(b,function(a){1===a.nodeType&&(k(a,d,c),k(a,g,j(a.parentNode,f)))})},m=function(b,c){var d,e={},f=i(b());return f.data?(e[c]=f.data,e.name=f.template):e[c]=b(),a.utils.arrayForEach(["afterAdd","afterRender","as","beforeRemove","includeDestroyed","templateEngine","templateOptions"],function(b){e[b]=f[b]||a.bindingHandlers.sortable[b]}),"foreach"===c&&(e.afterRender?(d=e.afterRender,e.afterRender=function(a,b){l.call(b,a,b),d.call(b,a,b)}):e.afterRender=l),e},n=function(a,b){var c=i(b);if(c)for(var d=0;a>d;d++)c[d]&&i(c[d]._destroy)&&a++;return a};a.bindingHandlers.sortable={init:function(l,o,p,q,r){var s,t,u=b(l),v=i(o())||{},w=m(o,"foreach"),x={};a.utils.arrayForEach(l.childNodes,function(a){a&&3===a.nodeType&&a.parentNode.removeChild(a)}),b.extend(!0,x,a.bindingHandlers.sortable),v.options&&x.options&&(a.utils.extend(x.options,v.options),delete v.options),a.utils.extend(x,v),x.connectClass&&(a.isObservable(x.allowDrop)||"function"==typeof x.allowDrop)?a.computed({read:function(){var b=i(x.allowDrop),c="function"==typeof b?b.call(this,w.foreach):b;a.utils.toggleDomNodeCssClass(l,x.connectClass,c)},disposeWhenNodeIsRemoved:l},this):a.utils.toggleDomNodeCssClass(l,x.connectClass,x.allowDrop),a.bindingHandlers.template.init(l,function(){return w},p,q,r),s=x.options.start,t=x.options.update;var y=setTimeout(function(){var m;u.sortable(a.utils.extend(x.options,{start:function(b,c){var d=c.item[0];k(d,e,a.utils.arrayIndexOf(c.item.parent().children(),d)),c.item.find("input:focus").change(),s&&s.apply(this,arguments)},receive:function(a,b){m=j(b.item[0],h),m&&(m.clone&&(m=m.clone()),x.dragged&&(m=x.dragged.call(this,m,a,b)||m))},update:function(c,h){var i,l,o,p,q,r=h.item[0],s=h.item.parent()[0],u=j(r,d)||m;if(m=null,u&&(this===s||b.contains(this,s))){if(i=j(r,g),o=j(r,e),l=j(r.parentNode,f),p=a.utils.arrayIndexOf(h.item.parent().children(),r),w.includeDestroyed||(o=n(o,i),p=n(p,l)),(x.beforeMove||x.afterMove)&&(q={item:u,sourceParent:i,sourceParentNode:i&&h.sender||r.parentNode,sourceIndex:o,targetParent:l,targetIndex:p,cancelDrop:!1}),x.beforeMove&&(x.beforeMove.call(this,q,c,h),q.cancelDrop))return q.sourceParent?b(q.sourceParent===q.targetParent?this:h.sender).sortable("cancel"):b(r).remove(),void 0;p>=0&&(i&&(i.splice(o,1),a.processAllDeferredBindingUpdates&&a.processAllDeferredBindingUpdates()),l.splice(p,0,u)),k(r,d,null),h.item.remove(),a.processAllDeferredBindingUpdates&&a.processAllDeferredBindingUpdates(),x.afterMove&&x.afterMove.call(this,q,c,h)}t&&t.apply(this,arguments)},connectWith:x.connectClass?"."+x.connectClass:!1})),x.isEnabled!==c&&a.computed({read:function(){u.sortable(i(x.isEnabled)?"enable":"disable")},disposeWhenNodeIsRemoved:l})},0);return a.utils.domNodeDisposal.addDisposeCallback(l,function(){u.data("sortable")&&u.sortable("destroy"),clearTimeout(y)}),{controlsDescendantBindings:!0}},update:function(b,c,d,e,g){var h=m(c,"foreach");k(b,f,h.foreach),a.bindingHandlers.template.update(b,function(){return h},d,e,g)},connectClass:"ko_container",allowDrop:!0,afterMove:null,beforeMove:null,options:{}},a.bindingHandlers.draggable={init:function(d,e,f,g,j){var l=i(e())||{},n=l.options||{},o=a.utils.extend({},a.bindingHandlers.draggable.options),p=m(e,"data"),q=l.connectClass||a.bindingHandlers.draggable.connectClass,r=l.isEnabled!==c?l.isEnabled:a.bindingHandlers.draggable.isEnabled;return l=l.data||l,k(d,h,l),a.utils.extend(o,n),o.connectToSortable=q?"."+q:!1,b(d).draggable(o),r!==c&&a.computed({read:function(){b(d).draggable(i(r)?"enable":"disable")},disposeWhenNodeIsRemoved:d}),a.bindingHandlers.template.init(d,function(){return p},f,g,j)},update:function(b,c,d,e,f){var g=m(c,"data");return a.bindingHandlers.template.update(b,function(){return g},d,e,f)},connectClass:a.bindingHandlers.sortable.connectClass,options:{helper:"clone"}}}); \ No newline at end of file +// knockout-sortable 0.8.2 | (c) 2013 Ryan Niemeyer | http://www.opensource.org/licenses/mit-license +!function(a){"function"==typeof define&&define.amd?define(["knockout","jquery","jquery.ui.sortable"],a):a(window.ko,jQuery)}(function(a,b,c){var d="ko_sortItem",e="ko_sourceIndex",f="ko_sortList",g="ko_parentList",h="ko_dragItem",i=a.utils.unwrapObservable,j=a.utils.domData.get,k=a.utils.domData.set,l=function(b,c){a.utils.arrayForEach(b,function(a){1===a.nodeType&&(k(a,d,c),k(a,g,j(a.parentNode,f)))})},m=function(b,c){var d,e={},f=i(b());return f.data?(e[c]=f.data,e.name=f.template):e[c]=b(),a.utils.arrayForEach(["afterAdd","afterRender","as","beforeRemove","includeDestroyed","templateEngine","templateOptions"],function(b){e[b]=f[b]||a.bindingHandlers.sortable[b]}),"foreach"===c&&(e.afterRender?(d=e.afterRender,e.afterRender=function(a,b){l.call(b,a,b),d.call(b,a,b)}):e.afterRender=l),e},n=function(a,b){var c=i(b);if(c)for(var d=0;a>d;d++)c[d]&&i(c[d]._destroy)&&a++;return a};a.bindingHandlers.sortable={init:function(l,o,p,q,r){var s,t,u=b(l),v=i(o())||{},w=m(o,"foreach"),x={};a.utils.arrayForEach(l.childNodes,function(a){a&&1!==a.nodeType&&a.parentNode.removeChild(a)}),b.extend(!0,x,a.bindingHandlers.sortable),v.options&&x.options&&(a.utils.extend(x.options,v.options),delete v.options),a.utils.extend(x,v),x.connectClass&&(a.isObservable(x.allowDrop)||"function"==typeof x.allowDrop)?a.computed({read:function(){var b=i(x.allowDrop),c="function"==typeof b?b.call(this,w.foreach):b;a.utils.toggleDomNodeCssClass(l,x.connectClass,c)},disposeWhenNodeIsRemoved:l},this):a.utils.toggleDomNodeCssClass(l,x.connectClass,x.allowDrop),a.bindingHandlers.template.init(l,function(){return w},p,q,r),s=x.options.start,t=x.options.update;var y=setTimeout(function(){var m;u.sortable(a.utils.extend(x.options,{start:function(b,c){var d=c.item[0];k(d,e,a.utils.arrayIndexOf(c.item.parent().children(),d)),c.item.find("input:focus").change(),s&&s.apply(this,arguments)},receive:function(a,b){m=j(b.item[0],h),m&&(m.clone&&(m=m.clone()),x.dragged&&(m=x.dragged.call(this,m,a,b)||m))},update:function(c,h){var i,l,o,p,q,r=h.item[0],s=h.item.parent()[0],u=j(r,d)||m;if(m=null,u&&(this===s||b.contains(this,s))){if(i=j(r,g),o=j(r,e),l=j(r.parentNode,f),p=a.utils.arrayIndexOf(h.item.parent().children(),r),w.includeDestroyed||(o=n(o,i),p=n(p,l)),(x.beforeMove||x.afterMove)&&(q={item:u,sourceParent:i,sourceParentNode:i&&h.sender||r.parentNode,sourceIndex:o,targetParent:l,targetIndex:p,cancelDrop:!1}),x.beforeMove&&(x.beforeMove.call(this,q,c,h),q.cancelDrop))return q.sourceParent?b(q.sourceParent===q.targetParent?this:h.sender).sortable("cancel"):b(r).remove(),void 0;p>=0&&(i&&(i.splice(o,1),a.processAllDeferredBindingUpdates&&a.processAllDeferredBindingUpdates()),l.splice(p,0,u)),k(r,d,null),h.item.remove(),a.processAllDeferredBindingUpdates&&a.processAllDeferredBindingUpdates(),x.afterMove&&x.afterMove.call(this,q,c,h)}t&&t.apply(this,arguments)},connectWith:x.connectClass?"."+x.connectClass:!1})),x.isEnabled!==c&&a.computed({read:function(){u.sortable(i(x.isEnabled)?"enable":"disable")},disposeWhenNodeIsRemoved:l})},0);return a.utils.domNodeDisposal.addDisposeCallback(l,function(){u.data("sortable")&&u.sortable("destroy"),clearTimeout(y)}),{controlsDescendantBindings:!0}},update:function(b,c,d,e,g){var h=m(c,"foreach");k(b,f,h.foreach),a.bindingHandlers.template.update(b,function(){return h},d,e,g)},connectClass:"ko_container",allowDrop:!0,afterMove:null,beforeMove:null,options:{}},a.bindingHandlers.draggable={init:function(d,e,f,g,j){var l=i(e())||{},n=l.options||{},o=a.utils.extend({},a.bindingHandlers.draggable.options),p=m(e,"data"),q=l.connectClass||a.bindingHandlers.draggable.connectClass,r=l.isEnabled!==c?l.isEnabled:a.bindingHandlers.draggable.isEnabled;return l=l.data||l,k(d,h,l),a.utils.extend(o,n),o.connectToSortable=q?"."+q:!1,b(d).draggable(o),r!==c&&a.computed({read:function(){b(d).draggable(i(r)?"enable":"disable")},disposeWhenNodeIsRemoved:d}),a.bindingHandlers.template.init(d,function(){return p},f,g,j)},update:function(b,c,d,e,f){var g=m(c,"data");return a.bindingHandlers.template.update(b,function(){return g},d,e,f)},connectClass:a.bindingHandlers.sortable.connectClass,options:{helper:"clone"}}}); \ No newline at end of file diff --git a/package.json b/package.json index ed38ddd..42fe95d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "knockout-sortable", - "version": "0.8.1", + "version": "0.8.2", "devDependencies": { "grunt": "~0.4.1", "grunt-contrib-uglify": "0.x.x", diff --git a/spec/knockout-sortable.spec.js b/spec/knockout-sortable.spec.js index fd302ad..62f88e9 100644 --- a/spec/knockout-sortable.spec.js +++ b/spec/knockout-sortable.spec.js @@ -64,6 +64,42 @@ describe("knockout-sortable", function(){ expect(children.eq(2).text()).toEqual("3"); }); + it("should strip top-level text nodes", function() { + var children, + options = { + elems: $(""), + vm: { items: ko.observableArray([1, 2, 3]) } + }; + + setup(options); + + //all children including text/comment nodes + children = options.root.contents(); + + expect(children.length).toEqual(3); + expect(children.eq(0).text()).toEqual("1"); + expect(children.eq(1).text()).toEqual("2"); + expect(children.eq(2).text()).toEqual("3"); + }); + + it("should strip top-level comment nodes", function() { + var children, + options = { + elems: $(""), + vm: { items: ko.observableArray([1, 2, 3]) } + }; + + setup(options); + + //all children including text/comment nodes + children = options.root.contents(); + + expect(children.length).toEqual(3); + expect(children.eq(0).text()).toEqual("1"); + expect(children.eq(1).text()).toEqual("2"); + expect(children.eq(2).text()).toEqual("3"); + }); + describe("when using 'as' to name the context", function() { it("should allow referring to child items by 'as' name", function() { var children, diff --git a/src/knockout-sortable.js b/src/knockout-sortable.js index 0b4ea93..12ddd72 100644 --- a/src/knockout-sortable.js +++ b/src/knockout-sortable.js @@ -86,9 +86,9 @@ sortable = {}, startActual, updateActual; - //remove leading/trailing text nodes from anonymous templates + //remove leading/trailing non-elements from anonymous templates ko.utils.arrayForEach(element.childNodes, function(node) { - if (node && node.nodeType === 3) { + if (node && node.nodeType !== 1) { node.parentNode.removeChild(node); } });