Path: blob/master/web-gui/buildyourownbotnet/assets/js/jquery.nestable.js
1292 views
/*!1* Nestable jQuery Plugin - Copyright (c) 2012 David Bushell - http://dbushell.com/2* Dual-licensed under the BSD or MIT licenses3*/4;(function($, window, document, undefined)5{6var hasTouch = 'ontouchstart' in window;78/**9* Detect CSS pointer-events property10* events are normally disabled on the dragging element to avoid conflicts11* https://github.com/ausi/Feature-detection-technique-for-pointer-events/blob/master/modernizr-pointerevents.js12*/13var hasPointerEvents = (function()14{15var el = document.createElement('div'),16docEl = document.documentElement;17if (!('pointerEvents' in el.style)) {18return false;19}20el.style.pointerEvents = 'auto';21el.style.pointerEvents = 'x';22docEl.appendChild(el);23var supports = window.getComputedStyle && window.getComputedStyle(el, '').pointerEvents === 'auto';24docEl.removeChild(el);25return !!supports;26})();2728var eStart = hasTouch ? 'touchstart' : 'mousedown',29eMove = hasTouch ? 'touchmove' : 'mousemove',30eEnd = hasTouch ? 'touchend' : 'mouseup';31eCancel = hasTouch ? 'touchcancel' : 'mouseup';3233var defaults = {34listNodeName : 'ul',35itemNodeName : 'li',36rootClass : 'dd',37listClass : 'dd-list',38itemClass : 'dd-item',39dragClass : 'dd-dragel',40handleClass : 'dd-handle',41collapsedClass : 'dd-collapsed',42placeClass : 'dd-placeholder',43noDragClass : 'dd-nodrag',44emptyClass : 'dd-empty',45expandBtnHTML : '<button data-action="expand" type="button">Expand</button>',46collapseBtnHTML : '<button data-action="collapse" type="button">Collapse</button>',47group : 0,48maxDepth : 5,49threshold : 2050};5152function Plugin(element, options)53{54this.w = $(window);55this.el = $(element);56this.options = $.extend({}, defaults, options);57this.init();58}5960Plugin.prototype = {6162init: function()63{64var list = this;6566list.reset();6768list.el.data('nestable-group', this.options.group);6970list.placeEl = $('<div class="' + list.options.placeClass + '"/>');7172$.each(this.el.find(list.options.itemNodeName), function(k, el) {73list.setParent($(el));74});7576list.el.on('click', 'button', function(e) {77if (list.dragEl || (!hasTouch && e.button !== 0)) {78return;79}80var target = $(e.currentTarget),81action = target.data('action'),82item = target.parent(list.options.itemNodeName);83if (action === 'collapse') {84list.collapseItem(item);85}86if (action === 'expand') {87list.expandItem(item);88}89});9091var onStartEvent = function(e)92{93var handle = $(e.target);94if (!handle.hasClass(list.options.handleClass)) {95if (handle.closest('.' + list.options.noDragClass).length) {96return;97}98handle = handle.closest('.' + list.options.handleClass);99}100if (!handle.length || list.dragEl || (!hasTouch && e.button !== 0) || (hasTouch && e.touches.length !== 1)) {101return;102}103e.preventDefault();104list.dragStart(hasTouch ? e.touches[0] : e);105};106107var onMoveEvent = function(e)108{109if (list.dragEl) {110e.preventDefault();111list.dragMove(hasTouch ? e.touches[0] : e);112}113};114115var onEndEvent = function(e)116{117if (list.dragEl) {118e.preventDefault();119list.dragStop(hasTouch ? e.touches[0] : e);120}121};122123if (hasTouch) {124list.el[0].addEventListener(eStart, onStartEvent, false);125window.addEventListener(eMove, onMoveEvent, false);126window.addEventListener(eEnd, onEndEvent, false);127window.addEventListener(eCancel, onEndEvent, false);128} else {129list.el.on(eStart, onStartEvent);130list.w.on(eMove, onMoveEvent);131list.w.on(eEnd, onEndEvent);132}133134},135136serialize: function()137{138var data,139depth = 0,140list = this;141step = function(level, depth)142{143var array = [ ],144items = level.children(list.options.itemNodeName);145items.each(function()146{147var li = $(this),148item = $.extend({}, li.data()),149sub = li.children(list.options.listNodeName);150if (sub.length) {151item.children = step(sub, depth + 1);152}153array.push(item);154});155return array;156};157data = step(list.el.find(list.options.listNodeName).first(), depth);158return data;159},160161serialise: function()162{163return this.serialize();164},165166reset: function()167{168this.mouse = {169offsetX : 0,170offsetY : 0,171startX : 0,172startY : 0,173lastX : 0,174lastY : 0,175nowX : 0,176nowY : 0,177distX : 0,178distY : 0,179dirAx : 0,180dirX : 0,181dirY : 0,182lastDirX : 0,183lastDirY : 0,184distAxX : 0,185distAxY : 0186};187this.moving = false;188this.dragEl = null;189this.dragRootEl = null;190this.dragDepth = 0;191this.hasNewRoot = false;192this.pointEl = null;193},194195expandItem: function(li)196{197li.removeClass(this.options.collapsedClass);198li.children('[data-action="expand"]').hide();199li.children('[data-action="collapse"]').show();200li.children(this.options.listNodeName).show();201},202203collapseItem: function(li)204{205var lists = li.children(this.options.listNodeName);206if (lists.length) {207li.addClass(this.options.collapsedClass);208li.children('[data-action="collapse"]').hide();209li.children('[data-action="expand"]').show();210li.children(this.options.listNodeName).hide();211}212},213214expandAll: function()215{216var list = this;217list.el.find(list.options.itemNodeName).each(function() {218list.expandItem($(this));219});220},221222collapseAll: function()223{224var list = this;225list.el.find(list.options.itemNodeName).each(function() {226list.collapseItem($(this));227});228},229230setParent: function(li)231{232if (li.children(this.options.listNodeName).length) {233li.prepend($(this.options.expandBtnHTML));234li.prepend($(this.options.collapseBtnHTML));235}236li.children('[data-action="expand"]').hide();237},238239unsetParent: function(li)240{241li.removeClass(this.options.collapsedClass);242li.children('[data-action]').remove();243li.children(this.options.listNodeName).remove();244},245246dragStart: function(e)247{248var mouse = this.mouse,249target = $(e.target),250dragItem = target.closest(this.options.itemNodeName);251252this.placeEl.css('height', dragItem.height());253254mouse.offsetX = e.offsetX !== undefined ? e.offsetX : e.pageX - target.offset().left;255mouse.offsetY = e.offsetY !== undefined ? e.offsetY : e.pageY - target.offset().top;256mouse.startX = mouse.lastX = e.pageX;257mouse.startY = mouse.lastY = e.pageY;258259this.dragRootEl = this.el;260261this.dragEl = $(document.createElement(this.options.listNodeName)).addClass(this.options.listClass + ' ' + this.options.dragClass + ($(this.el).hasClass('custom-drag-button') ? ' custom-handler' : ''));262this.dragEl.css('width', dragItem.width());263264// fix for zepto.js265//dragItem.after(this.placeEl).detach().appendTo(this.dragEl);266dragItem.after(this.placeEl);267dragItem[0].parentNode.removeChild(dragItem[0]);268dragItem.appendTo(this.dragEl);269270$(document.body).append(this.dragEl);271this.dragEl.css({272'left' : e.pageX - mouse.offsetX,273'top' : e.pageY - mouse.offsetY274});275// total depth of dragging item276var i, depth,277items = this.dragEl.find(this.options.itemNodeName);278for (i = 0; i < items.length; i++) {279depth = $(items[i]).parents(this.options.listNodeName).length;280if (depth > this.dragDepth) {281this.dragDepth = depth;282}283}284285},286287dragStop: function(e)288{289// fix for zepto.js290//this.placeEl.replaceWith(this.dragEl.children(this.options.itemNodeName + ':first').detach());291var el = this.dragEl.children(this.options.itemNodeName).first();292el[0].parentNode.removeChild(el[0]);293this.placeEl.replaceWith(el);294295this.dragEl.remove();296this.el.trigger('change');297if (this.hasNewRoot) {298this.dragRootEl.trigger('change');299}300this.reset();301},302303dragMove: function(e)304{305var list, parent, prev, next, depth,306opt = this.options,307mouse = this.mouse;308309this.dragEl.css({310'left' : e.pageX - mouse.offsetX,311'top' : e.pageY - mouse.offsetY312});313314// mouse position last events315mouse.lastX = mouse.nowX;316mouse.lastY = mouse.nowY;317// mouse position this events318mouse.nowX = e.pageX;319mouse.nowY = e.pageY;320// distance mouse moved between events321mouse.distX = mouse.nowX - mouse.lastX;322mouse.distY = mouse.nowY - mouse.lastY;323// direction mouse was moving324mouse.lastDirX = mouse.dirX;325mouse.lastDirY = mouse.dirY;326// direction mouse is now moving (on both axis)327mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1;328mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1;329// axis mouse is now moving on330var newAx = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0;331332// do nothing on first move333if (!mouse.moving) {334mouse.dirAx = newAx;335mouse.moving = true;336return;337}338339// calc distance moved on this axis (and direction)340if (mouse.dirAx !== newAx) {341mouse.distAxX = 0;342mouse.distAxY = 0;343} else {344mouse.distAxX += Math.abs(mouse.distX);345if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) {346mouse.distAxX = 0;347}348mouse.distAxY += Math.abs(mouse.distY);349if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) {350mouse.distAxY = 0;351}352}353mouse.dirAx = newAx;354355/**356* move horizontal357*/358if (mouse.dirAx && mouse.distAxX >= opt.threshold) {359// reset move distance on x-axis for new phase360mouse.distAxX = 0;361prev = this.placeEl.prev(opt.itemNodeName);362// increase horizontal level if previous sibling exists and is not collapsed363if (mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass)) {364// cannot increase level when item above is collapsed365list = prev.find(opt.listNodeName).last();366// check if depth limit has reached367depth = this.placeEl.parents(opt.listNodeName).length;368if (depth + this.dragDepth <= opt.maxDepth) {369// create new sub-level if one doesn't exist370if (!list.length) {371list = $('<' + opt.listNodeName + '/>').addClass(opt.listClass);372list.append(this.placeEl);373prev.append(list);374this.setParent(prev);375} else {376// else append to next level up377list = prev.children(opt.listNodeName).last();378list.append(this.placeEl);379}380}381}382// decrease horizontal level383if (mouse.distX < 0) {384// we can't decrease a level if an item preceeds the current one385next = this.placeEl.next(opt.itemNodeName);386if (!next.length) {387parent = this.placeEl.parent();388this.placeEl.closest(opt.itemNodeName).after(this.placeEl);389if (!parent.children().length) {390this.unsetParent(parent.parent());391}392}393}394}395396var isEmpty = false;397398// find list item under cursor399if (!hasPointerEvents) {400this.dragEl[0].style.visibility = 'hidden';401}402this.pointEl = $(document.elementFromPoint(e.pageX - document.body.scrollLeft, e.pageY - (window.pageYOffset || document.documentElement.scrollTop)));403if (!hasPointerEvents) {404this.dragEl[0].style.visibility = 'visible';405}406if (this.pointEl.hasClass(opt.handleClass)) {407this.pointEl = this.pointEl.parent(opt.itemNodeName);408}409if (this.pointEl.hasClass(opt.emptyClass)) {410isEmpty = true;411}412else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) {413return;414}415416// find parent list of item under cursor417var pointElRoot = this.pointEl.closest('.' + opt.rootClass),418isNewRoot = this.dragRootEl.data('nestable-id') !== pointElRoot.data('nestable-id');419420/**421* move vertical422*/423if (!mouse.dirAx || isNewRoot || isEmpty) {424// check if groups match if dragging over new root425if (isNewRoot && opt.group !== pointElRoot.data('nestable-group')) {426return;427}428// check depth limit429depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length;430if (depth > opt.maxDepth) {431return;432}433var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2);434parent = this.placeEl.parent();435// if empty create new list to replace empty placeholder436if (isEmpty) {437list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass);438list.append(this.placeEl);439this.pointEl.replaceWith(list);440}441else if (before) {442this.pointEl.before(this.placeEl);443}444else {445this.pointEl.after(this.placeEl);446}447if (!parent.children().length) {448this.unsetParent(parent.parent());449}450if (!this.dragRootEl.find(opt.itemNodeName).length) {451this.dragRootEl.append('<div class="' + opt.emptyClass + '"/>');452}453// parent root list has changed454if (isNewRoot) {455this.dragRootEl = pointElRoot;456this.hasNewRoot = this.el[0] !== this.dragRootEl[0];457}458}459}460461};462463$.fn.nestable = function(params)464{465var lists = this,466retval = this;467468lists.each(function()469{470var plugin = $(this).data("nestable");471472if (!plugin) {473$(this).data("nestable", new Plugin(this, params));474$(this).data("nestable-id", new Date().getTime());475} else {476if (typeof params === 'string' && typeof plugin[params] === 'function') {477retval = plugin[params]();478}479}480});481482return retval || lists;483};484485})(window.jQuery || window.Zepto, window, document);486487488