nestable3
Version:
Drag & drop hierarchical list with mouse and touch compatibility
1 lines • 16.8 kB
JavaScript
!function(h,c,p,i){var o="ontouchstart"in p,f=function(){var t=p.createElement("div"),e=p.documentElement;if(!("pointerEvents"in t.style))return!1;t.style.pointerEvents="auto",t.style.pointerEvents="x",e.appendChild(t);var s=c.getComputedStyle&&"auto"===c.getComputedStyle(t,"").pointerEvents;return e.removeChild(t),!!s}(),s={contentCallback:function(t){return t.content||t.id},listNodeName:"ol",itemNodeName:"li",handleNodeName:"div",contentNodeName:"span",rootClass:"dd",listClass:"dd-list",itemClass:"dd-item",dragClass:"dd-dragel",handleClass:"dd-handle",contentClass:"dd-content",collapsedClass:"dd-collapsed",expandedContentClass:"dd-expanded-content",placeClass:"dd-placeholder",noDragClass:"dd-nodrag",noChildrenClass:"dd-nochildren",emptyClass:"dd-empty",expandBtnHTML:'<button class="dd-expand" data-action="expand" type="button">Expand</button>',collapseBtnHTML:'<button class="dd-collapse" data-action="collapse" type="button">Collapse</button>',expandContentBtnHTML:'<button class="dd-expand-content" data-action="expand_content" type="button">Expand</button>',collapseContentBtnHTML:'<button class="dd-collapse-content" data-action="collapse_content" type="button">Collapse</button>',group:0,maxDepth:5,threshold:20,fixedDepth:!1,fixed:!1,includeContent:!1,scroll:!1,scrollSensitivity:1,scrollSpeed:5,scrollTriggers:{top:40,left:40,right:-40,bottom:-40},effect:{animation:"none",time:"slow"},callback:function(t,e,s){},onDragStart:function(t,e,s){},beforeDragStop:function(t,e,s){},listRenderer:function(t,e){var s="<"+e.listNodeName+' class="'+e.listClass+'">';return s+t+("</"+e.listNodeName+">")},itemRenderer:function(t,e,s,i,n){t=h.map(t,function(t,e){return" "+e+'="'+t+'"'}).join(" "),t="<"+i.itemNodeName+t+">";return(t+="<"+i.handleNodeName+' class="'+i.handleClass+'">')+("<"+i.contentNodeName+' class="'+i.contentClass+'">')+e+("</"+i.contentNodeName+">")+("</"+i.handleNodeName+">")+s+("</"+i.itemNodeName+">")}};function l(t,e){this.w=h(p),this.el=h(t),(e=e||s).rootClass!==i&&"dd"!==e.rootClass&&(e.listClass=e.listClass||e.rootClass+"-list",e.itemClass=e.itemClass||e.rootClass+"-item",e.dragClass=e.dragClass||e.rootClass+"-dragel",e.handleClass=e.handleClass||e.rootClass+"-handle",e.collapsedClass=e.collapsedClass||e.rootClass+"-collapsed",e.placeClass=e.placeClass||e.rootClass+"-placeholder",e.noDragClass=e.noDragClass||e.rootClass+"-nodrag",e.noChildrenClass=e.noChildrenClass||e.rootClass+"-nochildren",e.emptyClass=e.emptyClass||e.rootClass+"-empty"),this.options=h.extend({},s,e),this.options.json!==i&&this._build(),this.init()}l.prototype={init:function(){var i=this,t=(i.reset(),i.el.data("nestable-group",this.options.group),i.placeEl=h('<div class="'+i.options.placeClass+'"/>'),this.el.find(i.options.itemNodeName)),e=(h.each(t,function(t,e){var e=h(e),s=e.parent();i.setParent(e),s.hasClass(i.options.collapsedClass)&&i.collapseItem(s.parent())}),t.length||this.appendEmptyElement(this.el),i.el.on("click","button",function(t){var e;i.dragEl||(e=(t=h(t.currentTarget)).data("action"),t=t.parents(i.options.itemNodeName).eq(0),"collapse"===e&&i.collapseItem(t),"expand"===e&&i.expandItem(t),"collapse_content"===e&&i.collapseItemContent(t),"expand_content"===e&&i.expandItemContent(t))}),function(t){var e=h(t.target);if(!e.hasClass(i.options.handleClass)){if(e.closest("."+i.options.noDragClass).length)return;e=e.closest("."+i.options.handleClass)}!e.length||i.dragEl||(i.isTouch=/^touch/.test(t.type),i.isTouch&&1!==t.touches.length)||(t.preventDefault(),i.dragStart(t.touches?t.touches[0]:t))}),s=function(t){i.dragEl&&(t.preventDefault(),i.dragMove(t.touches?t.touches[0]:t))},n=function(t){i.dragEl&&(t.preventDefault(),i.dragStop(t.touches?t.changedTouches[0]:t))};o&&(i.el[0].addEventListener("touchstart",e,!1),c.addEventListener("touchmove",s,!1),c.addEventListener("touchend",n,!1),c.addEventListener("touchcancel",n,!1)),i.el.on("mousedown",e),i.w.on("mousemove",s),i.w.on("mouseup",n);i.el.bind("destroy-nestable",function(){o&&(i.el[0].removeEventListener("touchstart",e,!1),c.removeEventListener("touchmove",s,!1),c.removeEventListener("touchend",n,!1),c.removeEventListener("touchcancel",n,!1)),i.el.off("mousedown",e),i.w.off("mousemove",s),i.w.off("mouseup",n),i.el.off("click"),i.el.unbind("destroy-nestable"),i.el.data("nestable",null)})},destroy:function(){this.el.trigger("destroy-nestable")},add:function(t){var e="."+this.options.listClass,s=h(this.el).children(e);t.parent_id!==i&&(s=s.find('[data-id="'+t.parent_id+'"]'),delete t.parent_id,s=(s=0===s.children(e).length?s.append(this.options.listRenderer("",this.options)):s).find(e+":first"),this.setParent(s.parent())),s.append(this._buildItem(t,this.options))},replace:function(t){var e=this._buildItem(t,this.options);this._getItemById(t.id).replaceWith(e)},removeItem:function(t){var e=this.options,s=this.el,t=((t=t||this).remove(),"."+e.listClass+" ."+e.listClass+":not(:has(*))");h(s).find(t).remove();h(s).find('[data-action="expand"], [data-action="collapse"]').each(function(){0===h(this).siblings("."+e.listClass).length&&h(this).remove()})},remove:function(t,e){var s=this.options,i=this,n=this._getItemById(t),t=s.effect.animation||"fade",s=s.effect.time||"slow";"fade"===t?n.fadeOut(s,function(){i.removeItem(n)}):this.removeItem(n),e&&e()},removeAll:function(t){var e=this,s=this.options,i=e.el.find(s.listNodeName).first(),n=i.children(s.itemNodeName),o=s.effect.animation||"fade",s=s.effect.time||"slow";function l(){n.each(function(){e.removeItem(h(this))}),i.show(),t&&t()}"fade"===o?i.fadeOut(s,l):l()},_getItemById:function(t){return h(this.el).children("."+this.options.listClass).find('[data-id="'+t+'"]')},_build:function(){var t=this.options.json;"string"==typeof t&&(t=JSON.parse(t)),h(this.el).html(this._buildList(t,this.options))},_buildList:function(t,s){var i,n;return t?(i="",n=this,h.each(t,function(t,e){i+=n._buildItem(e,s)}),s.listRenderer(i,s)):""},_buildItem:function(t,e){s=t,delete(s=h.extend({},s)).children,delete s.classes,delete s.content,i={},h.each(s,function(t,e){var s;"object"==typeof e&&(e=JSON.stringify(e)),i["data-"+t]=(s={"&":"&","<":"<",">":">",'"':""","'":"'"},e+"".replace(/[&<>"']/g,function(t){return s[t]}))});var i,s=i,n=(s.class=(n=e,(o=function(t){var e,s={};for(e in t)s[t[e]]=t[e];return s}(o="string"==typeof(o=(o=t).classes||{})?[o]:o))[n.itemClass]=n.itemClass,h.map(o,function(t){return t}).join(" ")),e.contentCallback(t)),o=this._buildList(t.children,e),s=h(e.itemRenderer(s,n,o,e,t));return this.setParent(s),s[0].outerHTML},serialize:function(){var n=this,o=function(t){var i=[];return t.children(n.options.itemNodeName).each(function(){var t=h(this),e=h.extend({},t.data()),s=t.children(n.options.listNodeName);n.options.includeContent&&(t=t.find("."+n.options.contentClass).html())&&(e.content=t),s.length&&(e.children=o(s)),i.push(e)}),i};return o(n.el.find(n.options.listNodeName).first())},asNestedSet:function(){var l=this.options,a=[],t=1;return this.el.find(l.listNodeName).first().children(l.itemNodeName).each(function(){t=function t(e,s,i){var n,o=i+1;0<h(e).children(l.listNodeName).children(l.itemNodeName).length&&(s++,h(e).children(l.listNodeName).children(l.itemNodeName).each(function(){o=t(h(this),s,o)}),s--);n=h(e).attr("data-id");r(n)&&(n=parseInt(n));e=h(e).parent(l.listNodeName).parent(l.itemNodeName).attr("data-id")||"";r(e)&&(e=parseInt(e));n&&a.push({id:n,parent_id:e,depth:s,lft:i,rgt:o});i=o+1;return i}(this,0,t)}),a=a.sort(function(t,e){return t.lft-e.lft});function r(t){return h.isNumeric(t)&&Math.floor(t)==t}},returnOptions:function(){return this.options},serialise:function(){return this.serialize()},toHierarchy:function(t){var n=h.extend({},this.options,t),e=[];return h(this.element).children(n.items).each(function(){var t=function e(t){var s=(h(t).attr(n.attribute||"id")||"").match(n.expression||/(.+)[-=_](.+)/);{var i;if(s)return i={id:s[2]},0<h(t).children(n.listType).children(n.items).length&&(i.children=[],h(t).children(n.listType).children(n.items).each(function(){var t=e(this);i.children.push(t)})),i}}(this);e.push(t)}),e},toArray:function(){var l=h.extend({},this.options,this),a=l.startDepthCount||0,r=[],t=2;return this.el.find(this.options.listNodeName).first().children(this.options.itemNodeName).each(function(){t=function t(e,s,i){var n,o=i+1;0<e.children(l.options.listNodeName).children(l.options.itemNodeName).length&&(s++,e.children(l.options.listNodeName).children(l.options.itemNodeName).each(function(){o=t(h(this),s,o)}),s--);n=e.data().id;e=s===a+1?l.rootID:(e=e.parent(l.options.listNodeName).parent(l.options.itemNodeName).data(),e.id);n&&r.push({id:n,parent_id:e,depth:s,left:i,right:o});i=o+1;return i}(h(this),a+1,t)}),r=r.sort(function(t,e){return t.left-e.left})},reset:function(){this.mouse={offsetX:0,offsetY:0,startX:0,startY:0,lastX:0,lastY:0,nowX:0,nowY:0,distX:0,distY:0,dirAx:0,dirX:0,dirY:0,lastDirX:0,lastDirY:0,distAxX:0,distAxY:0},this.isTouch=!1,this.moving=!1,this.dragEl=null,this.dragRootEl=null,this.dragDepth=0,this.hasNewRoot=!1,this.pointEl=null},expandItemContent:function(t){t.addClass(this.options.expandedContentClass),t.find("> ."+this.options.contentClass+" ."+this.options.contentClass+"-more").slideDown()},collapseItemContent:function(t){t.removeClass(this.options.expandedContentClass),t.find("> ."+this.options.contentClass+" ."+this.options.contentClass+"-more").slideUp()},expandItem:function(t){t.removeClass(this.options.collapsedClass)},collapseItem:function(t){t.children(this.options.listNodeName).length&&t.addClass(this.options.collapsedClass)},expandAll:function(){var t=this;t.el.find(t.options.itemNodeName).each(function(){t.expandItem(h(this))})},collapseAll:function(){var t=this;t.el.find(t.options.itemNodeName).each(function(){t.collapseItem(h(this))})},setParent:function(t){0==t.find('> [data-action="expand_content"]').length&&(t.prepend(h(this.options.expandContentBtnHTML)),t.prepend(h(this.options.collapseContentBtnHTML))),t.is(this.options.itemNodeName)&&t.children(this.options.listNodeName).length&&(t.children('[data-action="collapse"]').remove(),t.children('[data-action="expand"]').remove(),t.prepend(h(this.options.expandBtnHTML)),t.prepend(h(this.options.collapseBtnHTML)))},unsetParent:function(t){t.removeClass(this.options.collapsedClass),t.children('[data-action="collapse"]').remove(),t.children('[data-action="expand"]').remove(),t.children(this.options.listNodeName).remove()},dragStart:function(t){var e=this.mouse,s=h(t.target).closest(this.options.itemNodeName),i={top:t.pageY,left:t.pageX},i=this.options.onDragStart.call(this,this.el,s,i);if(void 0===i||!1!==i){this.placeEl.css("height",s.height()),e.offsetX=t.pageX-s.offset().left,e.offsetY=t.pageY-s.offset().top,e.startX=e.lastX=t.pageX,e.startY=e.lastY=t.pageY,this.dragRootEl=this.el,this.dragEl=h(p.createElement(this.options.listNodeName)).addClass(this.options.listClass+" "+this.options.dragClass),this.dragEl.css("width",s.outerWidth()),this.setIndexOfItem(s),s.after(this.placeEl),s[0].parentNode.removeChild(s[0]),s.appendTo(this.dragEl),h(p.body).append(this.dragEl),this.dragEl.css({left:t.pageX-e.offsetX,top:t.pageY-e.offsetY});for(var n,o=this.dragEl.find(this.options.itemNodeName),l=0;l<o.length;l++)(n=h(o[l]).parents(this.options.listNodeName).length)>this.dragDepth&&(this.dragDepth=n)}},createSubLevel:function(t,e){var s=h("<"+this.options.listNodeName+"/>").addClass(this.options.listClass);return e&&s.append(e),t.append(s),this.setParent(t),s},setIndexOfItem:function(t,e){(e=e||[]).unshift(t.index()),h(t[0].parentNode)[0]!==this.dragRootEl[0]?this.setIndexOfItem(h(t[0].parentNode),e):this.dragEl.data("indexOfItem",e)},restoreItemAtIndex:function(t,e){var s=this.el,i=e.length-1;for(var n=0;n<e.length;n++){if(i===parseInt(n))return o=s,l=t,void(0===e[i]?h(o).prepend(l.clone(!0)):h(o.children[e[i]-1]).after(l.clone(!0)));var o=s[0]||s,l=o.children[e[n]],s=l||this.createSubLevel(h(o))}},dragStop:function(t){var t={top:t.pageY,left:t.pageX},e=this.dragEl.data("indexOfItem"),s=this.dragEl.children(this.options.itemNodeName).first(),i=(s[0].parentNode.removeChild(s[0]),this.dragEl.remove(),this.options.beforeDragStop.call(this,this.el,s,this.placeEl.parent()));void 0!==i&&!1===i?(i=this.placeEl.parent(),this.placeEl.remove(),i.children().length||this.unsetParent(i.parent()),this.restoreItemAtIndex(s,e)):(this.placeEl.replaceWith(s),this.hasNewRoot?(!0===this.options.fixed?this.restoreItemAtIndex(s,e):this.el.trigger("lostItem"),this.dragRootEl.trigger("gainedItem")):this.dragRootEl.trigger("change"),this.options.callback.call(this,this.dragRootEl,s,t)),this.reset()},dragMove:function(t){var e,s=this.options,i=this.mouse,n=(this.dragEl.css({left:t.pageX-i.offsetX,top:t.pageY-i.offsetY}),i.lastX=i.nowX,i.lastY=i.nowY,i.nowX=t.pageX,i.nowY=t.pageY,i.distX=i.nowX-i.lastX,i.distY=i.nowY-i.lastY,i.lastDirX=i.dirX,i.lastDirY=i.dirY,i.dirX=0===i.distX?0:0<i.distX?1:-1,i.dirY=0===i.distY?0:0<i.distY?1:-1,Math.abs(i.distX)>Math.abs(i.distY)?1:0);if(i.moving){s.scroll&&(void 0!==c.jQuery.fn.scrollParent?(o=!1,(r=this.el.scrollParent()[0])!==p&&"HTML"!==r.tagName?(s.scrollTriggers.bottom+r.offsetHeight-t.pageY<s.scrollSensitivity?r.scrollTop=o=r.scrollTop+s.scrollSpeed:t.pageY-s.scrollTriggers.top<s.scrollSensitivity&&(r.scrollTop=o=r.scrollTop-s.scrollSpeed),s.scrollTriggers.right+r.offsetWidth-t.pageX<s.scrollSensitivity?r.scrollLeft=o=r.scrollLeft+s.scrollSpeed:t.pageX-s.scrollTriggers.left<s.scrollSensitivity&&(r.scrollLeft=o=r.scrollLeft-s.scrollSpeed)):(t.pageY-h(p).scrollTop()<s.scrollSensitivity?o=h(p).scrollTop(h(p).scrollTop()-s.scrollSpeed):h(c).height()-(t.pageY-h(p).scrollTop())<s.scrollSensitivity&&(o=h(p).scrollTop(h(p).scrollTop()+s.scrollSpeed)),t.pageX-h(p).scrollLeft()<s.scrollSensitivity?o=h(p).scrollLeft(h(p).scrollLeft()-s.scrollSpeed):h(c).width()-(t.pageX-h(p).scrollLeft())<s.scrollSensitivity&&(o=h(p).scrollLeft(h(p).scrollLeft()+s.scrollSpeed)))):console.warn("To use scrolling you need to have scrollParent() function, check documentation for more information")),this.scrollTimer&&clearTimeout(this.scrollTimer),s.scroll&&o&&(this.scrollTimer=setTimeout(function(){h(c).trigger(t)},10)),i.dirAx!==n?(i.distAxX=0,i.distAxY=0):(i.distAxX+=Math.abs(i.distX),0!==i.dirX&&i.dirX!==i.lastDirX&&(i.distAxX=0),i.distAxY+=Math.abs(i.distY),0!==i.dirY&&i.dirY!==i.lastDirY&&(i.distAxY=0)),i.dirAx=n,i.dirAx&&i.distAxX>=s.threshold&&(i.distAxX=0,r=this.placeEl.prev(s.itemNodeName),0<i.distX&&r.length&&!r.hasClass(s.collapsedClass)&&!r.hasClass(s.noChildrenClass)&&(e=r.find(s.listNodeName).last(),this.placeEl.parents(s.listNodeName).length+this.dragDepth<=s.maxDepth)&&(e.length?(e=r.children(s.listNodeName).last()).append(this.placeEl):this.createSubLevel(r,this.placeEl)),i.distX<0)&&(this.placeEl.next(s.itemNodeName).length||(a=this.placeEl.parent(),this.placeEl.closest(s.itemNodeName).after(this.placeEl),a.children().length)||this.unsetParent(a.parent()));var o=!1;if(f||(this.dragEl[0].style.visibility="hidden"),this.pointEl=h(p.elementFromPoint(t.pageX-p.body.scrollLeft,t.pageY-(c.pageYOffset||p.documentElement.scrollTop))),f||(this.dragEl[0].style.visibility="visible"),this.pointEl.hasClass(s.handleClass)&&(this.pointEl=this.pointEl.closest(s.itemNodeName)),this.pointEl.hasClass(s.emptyClass))o=!0;else if(!this.pointEl.length||!this.pointEl.hasClass(s.itemClass))return;var l,a,r=this.pointEl.closest("."+s.rootClass),d=this.dragRootEl.data("nestable-id")!==r.data("nestable-id");i.dirAx&&!d&&!o||d&&s.group!==r.data("nestable-group")||this.options.fixedDepth&&this.dragDepth+1!==this.pointEl.parents(s.listNodeName).length||this.dragDepth-1+this.pointEl.parents(s.listNodeName).length>s.maxDepth||(l=t.pageY<this.pointEl.offset().top+this.pointEl.height()/2,a=this.placeEl.parent(),o?((e=h(p.createElement(s.listNodeName)).addClass(s.listClass)).append(this.placeEl),this.pointEl.replaceWith(e)):l?this.pointEl.before(this.placeEl):this.pointEl.after(this.placeEl),a.children().length||this.unsetParent(a.parent()),this.dragRootEl.find(s.itemNodeName).length||this.appendEmptyElement(this.dragRootEl),this.dragRootEl=r,d&&(this.hasNewRoot=this.el[0]!==this.dragRootEl[0]))}else i.dirAx=n,i.moving=!0},appendEmptyElement:function(t){t.append('<div class="'+this.options.emptyClass+'"/>')}},h.fn.nestable=function(i){var n=this,o=arguments;return"Nestable"in c||(c.Nestable={},Nestable.counter=0),this.each(function(){var t=h(this).data("nestable");if(t){if("string"==typeof i&&"function"==typeof t[i])if(1<o.length){for(var e=[],s=1;s<o.length;s++)e.push(o[s]);n=t[i].apply(t,e)}else n=t[i]()}else Nestable.counter++,h(this).data("nestable",new l(this,i)),h(this).data("nestable-id",Nestable.counter)}),n||this}}(window.jQuery||window.Zepto,window,document);