diff --git a/README.md b/README.md index b86e091..6947880 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,9 @@ Then activate with jQuery like so: The `change` event is fired when items are reordered. - $('.dd').on('change', function() { - /* on change event */ + $('.dd').on('change', function(e, elemDragged) { + //e: event + //elemDragged: elemento que foi movimentado }); ### Methods diff --git a/jquery.nestable.js b/jquery.nestable.js index 7323fb4..50138db 100644 --- a/jquery.nestable.js +++ b/jquery.nestable.js @@ -2,8 +2,7 @@ * Nestable jQuery Plugin - Copyright (c) 2012 David Bushell - http://dbushell.com/ * Dual-licensed under the BSD or MIT licenses */ -;(function($, window, document, undefined) -{ +; (function ($, window, document, undefined) { var hasTouch = 'ontouchstart' in document; /** @@ -11,9 +10,8 @@ * events are normally disabled on the dragging element to avoid conflicts * https://github.com/ausi/Feature-detection-technique-for-pointer-events/blob/master/modernizr-pointerevents.js */ - var hasPointerEvents = (function() - { - var el = document.createElement('div'), + var hasPointerEvents = (function () { + var el = document.createElement('div'), docEl = document.documentElement; if (!('pointerEvents' in el.style)) { return false; @@ -27,27 +25,28 @@ })(); var defaults = { - listNodeName : 'ol', - itemNodeName : 'li', - rootClass : 'dd', - listClass : 'dd-list', - itemClass : 'dd-item', - dragClass : 'dd-dragel', - handleClass : 'dd-handle', - collapsedClass : 'dd-collapsed', - placeClass : 'dd-placeholder', - noDragClass : 'dd-nodrag', - emptyClass : 'dd-empty', - expandBtnHTML : '', - collapseBtnHTML : '', - group : 0, - maxDepth : 5, - threshold : 20 - }; - - function Plugin(element, options) - { - this.w = $(document); + listNodeName: 'ol', + itemNodeName: 'li', + rootClass: 'dd', + listClass: 'dd-list', + itemClass: 'dd-item', + dragClass: 'dd-dragel', + handleClass: 'dd-handle', + collapsedClass: 'dd-collapsed', + placeClass: 'dd-placeholder', + noDragClass: 'dd-nodrag', + emptyClass: 'dd-empty', + expandBtnHTML: '', + collapseBtnHTML: '', + group: 0, + maxDepth: 5, + threshold: 20, + allowDecrease: true, + allowIncrease: true + }; + + function Plugin(element, options) { + this.w = $(document); this.el = $(element); this.options = $.extend({}, defaults, options); this.init(); @@ -55,8 +54,7 @@ Plugin.prototype = { - init: function() - { + init: function () { var list = this; list.reset(); @@ -65,17 +63,17 @@ list.placeEl = $('
'); - $.each(this.el.find(list.options.itemNodeName), function(k, el) { + $.each(this.el.find(list.options.itemNodeName), function (k, el) { list.setParent($(el)); }); - list.el.on('click', 'button', function(e) { + list.el.on('click', 'button', function (e) { if (list.dragEl) { return; } var target = $(e.currentTarget), action = target.data('action'), - item = target.parent(list.options.itemNodeName); + item = target.parent(list.options.itemNodeName); if (action === 'collapse') { list.collapseItem(item); } @@ -84,8 +82,7 @@ } }); - var onStartEvent = function(e) - { + var onStartEvent = function (e) { var handle = $(e.target); if (!handle.hasClass(list.options.handleClass)) { if (handle.closest('.' + list.options.noDragClass).length) { @@ -107,16 +104,14 @@ list.dragStart(e.touches ? e.touches[0] : e); }; - var onMoveEvent = function(e) - { + var onMoveEvent = function (e) { if (list.dragEl) { e.preventDefault(); list.dragMove(e.touches ? e.touches[0] : e); } }; - var onEndEvent = function(e) - { + var onEndEvent = function (e) { if (list.dragEl) { e.preventDefault(); list.dragStop(e.touches ? e.touches[0] : e); @@ -136,20 +131,17 @@ }, - serialize: function() - { + serialize: function () { var data, depth = 0, - list = this; - step = function(level, depth) - { - var array = [ ], + list = this, + step = function (level, depth) { + var array = [], items = level.children(list.options.itemNodeName); - items.each(function() - { - var li = $(this), + items.each(function () { + var li = $(this), item = $.extend({}, li.data()), - sub = li.children(list.options.listNodeName); + sub = li.children(list.options.listNodeName); if (sub.length) { item.children = step(sub, depth + 1); } @@ -161,51 +153,48 @@ return data; }, - serialise: function() - { + serialise: function () { return this.serialize(); }, - reset: function() - { + 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 + 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 = false; - this.moving = false; - this.dragEl = null; + this.isTouch = false; + this.moving = false; + this.dragEl = null; this.dragRootEl = null; - this.dragDepth = 0; + this.dragDepth = 0; + this.dragLevel = 0; this.hasNewRoot = false; - this.pointEl = null; + this.pointEl = null; }, - expandItem: function(li) - { + expandItem: function (li) { li.removeClass(this.options.collapsedClass); li.children('[data-action="expand"]').hide(); li.children('[data-action="collapse"]').show(); li.children(this.options.listNodeName).show(); }, - collapseItem: function(li) - { + collapseItem: function (li) { var lists = li.children(this.options.listNodeName); if (lists.length) { li.addClass(this.options.collapsedClass); @@ -215,42 +204,43 @@ } }, - expandAll: function() - { + expandAll: function () { var list = this; - list.el.find(list.options.itemNodeName).each(function() { + list.el.find(list.options.itemNodeName).each(function () { list.expandItem($(this)); }); }, - collapseAll: function() - { + collapseAll: function () { var list = this; - list.el.find(list.options.itemNodeName).each(function() { + list.el.find(list.options.itemNodeName).each(function () { list.collapseItem($(this)); }); }, - setParent: function(li) - { - if (li.children(this.options.listNodeName).length) { - li.prepend($(this.options.expandBtnHTML)); - li.prepend($(this.options.collapseBtnHTML)); + isParent: function (li) { + return (li.find('button[data-action]').length != 0); + }, + + setParent: function (li) { + if (!this.isParent(li)) { + if (li.children(this.options.listNodeName).length) { + li.prepend($(this.options.expandBtnHTML)); + li.prepend($(this.options.collapseBtnHTML)); + } + li.children('[data-action="expand"]').hide(); } - li.children('[data-action="expand"]').hide(); }, - unsetParent: function(li) - { + unsetParent: function (li) { li.removeClass(this.options.collapsedClass); li.children('[data-action]').remove(); li.children(this.options.listNodeName).remove(); }, - dragStart: function(e) - { - var mouse = this.mouse, - target = $(e.target), + dragStart: function (e) { + var mouse = this.mouse, + target = $(e.target), dragItem = target.closest(this.options.itemNodeName); this.placeEl.css('height', dragItem.height()); @@ -260,8 +250,8 @@ mouse.startX = mouse.lastX = e.pageX; mouse.startY = mouse.lastY = e.pageY; + this.dragLevel = dragItem.parents(this.options.listNodeName).length; this.dragRootEl = this.el; - this.dragEl = $(document.createElement(this.options.listNodeName)).addClass(this.options.listClass + ' ' + this.options.dragClass); this.dragEl.css('width', dragItem.width()); @@ -271,8 +261,8 @@ $(document.body).append(this.dragEl); this.dragEl.css({ - 'left' : e.pageX - mouse.offsetX, - 'top' : e.pageY - mouse.offsetY + 'left': e.pageX - mouse.offsetX, + 'top': e.pageY - mouse.offsetY }); // total depth of dragging item var i, depth, @@ -285,37 +275,35 @@ } }, - dragStop: function(e) - { + dragStop: function (e) { var el = this.dragEl.children(this.options.itemNodeName).first(); el[0].parentNode.removeChild(el[0]); this.placeEl.replaceWith(el); this.dragEl.remove(); - this.el.trigger('change'); + this.el.trigger('change', el); if (this.hasNewRoot) { this.dragRootEl.trigger('change'); } this.reset(); }, - dragMove: function(e) - { + dragMove: function (e) { var list, parent, prev, next, depth, - opt = this.options, + opt = this.options, mouse = this.mouse; this.dragEl.css({ - 'left' : e.pageX - mouse.offsetX, - 'top' : e.pageY - mouse.offsetY + 'left': e.pageX - mouse.offsetX, + 'top': e.pageY - mouse.offsetY }); // mouse position last events mouse.lastX = mouse.nowX; mouse.lastY = mouse.nowY; // mouse position this events - mouse.nowX = e.pageX; - mouse.nowY = e.pageY; + mouse.nowX = e.pageX; + mouse.nowY = e.pageY; // distance mouse moved between events mouse.distX = mouse.nowX - mouse.lastX; mouse.distY = mouse.nowY - mouse.lastY; @@ -326,11 +314,11 @@ mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1; mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1; // axis mouse is now moving on - var newAx = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0; + var newAx = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0; // do nothing on first move if (!mouse.moving) { - mouse.dirAx = newAx; + mouse.dirAx = newAx; mouse.moving = true; return; } @@ -358,8 +346,9 @@ // reset move distance on x-axis for new phase mouse.distAxX = 0; prev = this.placeEl.prev(opt.itemNodeName); + // increase horizontal level if previous sibling exists and is not collapsed - if (mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass)) { + if (opt.allowIncrease && mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass)) { // cannot increase level when item above is collapsed list = prev.find(opt.listNodeName).last(); // check if depth limit has reached @@ -378,8 +367,9 @@ } } } + // decrease horizontal level - if (mouse.distX < 0) { + if (opt.allowDecrease && mouse.distX < 0) { // we can't decrease a level if an item preceeds the current one next = this.placeEl.next(opt.itemNodeName); if (!next.length) { @@ -407,40 +397,70 @@ } if (this.pointEl.hasClass(opt.emptyClass)) { isEmpty = true; - } - else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) { + } else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) { return; } // find parent list of item under cursor var pointElRoot = this.pointEl.closest('.' + opt.rootClass), - isNewRoot = this.dragRootEl.data('nestable-id') !== pointElRoot.data('nestable-id'); + isNewRoot = this.dragRootEl.data('nestable-id') !== pointElRoot.data('nestable-id'); /** * move vertical */ if (!mouse.dirAx || isNewRoot || isEmpty) { + // get previously hovered element based on direction of mouse movement + if (mouse.dirY == 1) { + prev = this.pointEl.prev(opt.itemNodeName); + } else if (mouse.dirY == -1) { + prev = this.pointEl.next(opt.itemNodeName); + } + // check if groups match if dragging over new root if (isNewRoot && opt.group !== pointElRoot.data('nestable-group')) { return; } // check depth limit + var currentLevel = this.pointEl.parents(opt.listNodeName).length; depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length; + if (depth > opt.maxDepth) { return; } + if (!opt.allowIncrease && this.dragLevel < currentLevel) { + return; + } + if (!opt.allowDecrease && this.dragLevel > currentLevel && (this.dragLevel - currentLevel != 1)) { + return; + } + if ((!opt.allowDecrease || !opt.allowIncrease) && (this.dragLevel - currentLevel == 1)) { + // set parent for currently hovered element + list = this.pointEl.find(opt.listNodeName).last(); + if (!list.length) { + list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass); + list.append(this.placeEl); + this.pointEl.append(list); + this.setParent(this.pointEl); + } + + // unset parent for previously hovered element + if (prev != undefined && this.isParent(prev) && prev.find(opt.listNodeName).children().length == 0) { + this.unsetParent(prev); + } + + return; + } + var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2); - parent = this.placeEl.parent(); + parent = this.placeEl.parent(); // if empty create new list to replace empty placeholder if (isEmpty) { list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass); list.append(this.placeEl); this.pointEl.replaceWith(list); - } - else if (before) { + } else if (before) { this.pointEl.before(this.placeEl); - } - else { + } else { this.pointEl.after(this.placeEl); } if (!parent.children().length) { @@ -459,13 +479,11 @@ }; - $.fn.nestable = function(params) - { - var lists = this, + $.fn.nestable = function (params) { + var lists = this, retval = this; - lists.each(function() - { + lists.each(function () { var plugin = $(this).data("nestable"); if (!plugin) { @@ -481,4 +499,4 @@ return retval || lists; }; -})(window.jQuery || window.Zepto, window, document); +})(window.jQuery || window.Zepto, window, document); \ No newline at end of file