| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484 | /*! * Nestable jQuery Plugin - Copyright (c) 2012 David Bushell - http://dbushell.com/ * Dual-licensed under the BSD or MIT licenses */;(function($, window, document, undefined){    var hasTouch = 'ontouchstart' in document;    /**     * Detect CSS pointer-events property     * 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'),            docEl = document.documentElement;        if (!('pointerEvents' in el.style)) {            return false;        }        el.style.pointerEvents = 'auto';        el.style.pointerEvents = 'x';        docEl.appendChild(el);        var supports = window.getComputedStyle && window.getComputedStyle(el, '').pointerEvents === 'auto';        docEl.removeChild(el);        return !!supports;    })();    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   : '<button data-action="expand" type="button">展开</button>',        collapseBtnHTML : '<button data-action="collapse" type="button">关闭</button>',        group           : 0,        maxDepth        : 5,        threshold       : 20    };    function Plugin(element, options)    {        this.w  = $(document);        this.el = $(element);        this.options = $.extend({}, defaults, options);        this.init();    }    Plugin.prototype = {        init: function()        {            var list = this;            list.reset();            list.el.data('nestable-group', this.options.group);            list.placeEl = $('<div class="' + list.options.placeClass + '"/>');            $.each(this.el.find(list.options.itemNodeName), function(k, el) {                list.setParent($(el));            });            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);                if (action === 'collapse') {                    list.collapseItem(item);                }                if (action === 'expand') {                    list.expandItem(item);                }            });            var onStartEvent = function(e)            {                var handle = $(e.target);                if (!handle.hasClass(list.options.handleClass)) {                    if (handle.closest('.' + list.options.noDragClass).length) {                        return;                    }                    handle = handle.closest('.' + list.options.handleClass);                }                if (!handle.length || list.dragEl) {                    return;                }                list.isTouch = /^touch/.test(e.type);                if (list.isTouch && e.touches.length !== 1) {                    return;                }                e.preventDefault();                list.dragStart(e.touches ? e.touches[0] : e);            };            var onMoveEvent = function(e)            {                if (list.dragEl) {                    e.preventDefault();                    list.dragMove(e.touches ? e.touches[0] : e);                }            };            var onEndEvent = function(e)            {                if (list.dragEl) {                    e.preventDefault();                    list.dragStop(e.touches ? e.touches[0] : e);                }            };            if (hasTouch) {                list.el[0].addEventListener('touchstart', onStartEvent, false);                window.addEventListener('touchmove', onMoveEvent, false);                window.addEventListener('touchend', onEndEvent, false);                window.addEventListener('touchcancel', onEndEvent, false);            }            list.el.on('mousedown', onStartEvent);            list.w.on('mousemove', onMoveEvent);            list.w.on('mouseup', onEndEvent);        },        serialize: function()        {            var data,                depth = 0,                list  = this;            step  = function(level, depth)            {                var array = [ ],                    items = level.children(list.options.itemNodeName);                items.each(function()                {                    var li   = $(this),                        item = $.extend({}, li.data()),                        sub  = li.children(list.options.listNodeName);                    if (sub.length) {                        item.children = step(sub, depth + 1);                    }                    array.push(item);                });                return array;            };            data = step(list.el.find(list.options.listNodeName).first(), depth);            return data;        },        serialise: function()        {            return this.serialize();        },        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    = false;            this.moving     = false;            this.dragEl     = null;            this.dragRootEl = null;            this.dragDepth  = 0;            this.hasNewRoot = false;            this.pointEl    = null;        },        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)        {            var lists = li.children(this.options.listNodeName);            if (lists.length) {                li.addClass(this.options.collapsedClass);                li.children('[data-action="collapse"]').hide();                li.children('[data-action="expand"]').show();                li.children(this.options.listNodeName).hide();            }        },        expandAll: function()        {            var list = this;            list.el.find(list.options.itemNodeName).each(function() {                list.expandItem($(this));            });        },        collapseAll: function()        {            var list = this;            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));            }            li.children('[data-action="expand"]').hide();        },        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),                dragItem = target.closest(this.options.itemNodeName);            this.placeEl.css('height', dragItem.height());            mouse.offsetX = e.offsetX !== undefined ? e.offsetX : e.pageX - target.offset().left;            mouse.offsetY = e.offsetY !== undefined ? e.offsetY : e.pageY - target.offset().top;            mouse.startX = mouse.lastX = e.pageX;            mouse.startY = mouse.lastY = e.pageY;            this.dragRootEl = this.el;            this.dragEl = $(document.createElement(this.options.listNodeName)).addClass(this.options.listClass + ' ' + this.options.dragClass);            this.dragEl.css('width', dragItem.width());            dragItem.after(this.placeEl);            dragItem[0].parentNode.removeChild(dragItem[0]);            dragItem.appendTo(this.dragEl);            $(document.body).append(this.dragEl);            this.dragEl.css({                'left' : e.pageX - mouse.offsetX,                'top'  : e.pageY - mouse.offsetY            });            // total depth of dragging item            var i, depth,                items = this.dragEl.find(this.options.itemNodeName);            for (i = 0; i < items.length; i++) {                depth = $(items[i]).parents(this.options.listNodeName).length;                if (depth > this.dragDepth) {                    this.dragDepth = depth;                }            }        },        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');            if (this.hasNewRoot) {                this.dragRootEl.trigger('change');            }            this.reset();        },        dragMove: function(e)        {            var list, parent, prev, next, depth,                opt   = this.options,                mouse = this.mouse;            this.dragEl.css({                '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;            // distance mouse moved between events            mouse.distX = mouse.nowX - mouse.lastX;            mouse.distY = mouse.nowY - mouse.lastY;            // direction mouse was moving            mouse.lastDirX = mouse.dirX;            mouse.lastDirY = mouse.dirY;            // direction mouse is now moving (on both axis)            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;            // do nothing on first move            if (!mouse.moving) {                mouse.dirAx  = newAx;                mouse.moving = true;                return;            }            // calc distance moved on this axis (and direction)            if (mouse.dirAx !== newAx) {                mouse.distAxX = 0;                mouse.distAxY = 0;            } else {                mouse.distAxX += Math.abs(mouse.distX);                if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) {                    mouse.distAxX = 0;                }                mouse.distAxY += Math.abs(mouse.distY);                if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) {                    mouse.distAxY = 0;                }            }            mouse.dirAx = newAx;            /**             * move horizontal             */            if (mouse.dirAx && mouse.distAxX >= opt.threshold) {                // 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)) {                    // cannot increase level when item above is collapsed                    list = prev.find(opt.listNodeName).last();                    // check if depth limit has reached                    depth = this.placeEl.parents(opt.listNodeName).length;                    if (depth + this.dragDepth <= opt.maxDepth) {                        // create new sub-level if one doesn't exist                        if (!list.length) {                            list = $('<' + opt.listNodeName + '/>').addClass(opt.listClass);                            list.append(this.placeEl);                            prev.append(list);                            this.setParent(prev);                        } else {                            // else append to next level up                            list = prev.children(opt.listNodeName).last();                            list.append(this.placeEl);                        }                    }                }                // decrease horizontal level                if (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) {                        parent = this.placeEl.parent();                        this.placeEl.closest(opt.itemNodeName).after(this.placeEl);                        if (!parent.children().length) {                            this.unsetParent(parent.parent());                        }                    }                }            }            var isEmpty = false;            // find list item under cursor            if (!hasPointerEvents) {                this.dragEl[0].style.visibility = 'hidden';            }            this.pointEl = $(document.elementFromPoint(e.pageX - document.body.scrollLeft, e.pageY - (window.pageYOffset || document.documentElement.scrollTop)));            if (!hasPointerEvents) {                this.dragEl[0].style.visibility = 'visible';            }            if (this.pointEl.hasClass(opt.handleClass)) {                this.pointEl = this.pointEl.parent(opt.itemNodeName);            }            if (this.pointEl.hasClass(opt.emptyClass)) {                isEmpty = true;            }            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');            /**             * move vertical             */            if (!mouse.dirAx || isNewRoot || isEmpty) {                // check if groups match if dragging over new root                if (isNewRoot && opt.group !== pointElRoot.data('nestable-group')) {                    return;                }                // check depth limit                depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length;                if (depth > opt.maxDepth) {                    return;                }                var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2);                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) {                    this.pointEl.before(this.placeEl);                }                else {                    this.pointEl.after(this.placeEl);                }                if (!parent.children().length) {                    this.unsetParent(parent.parent());                }                if (!this.dragRootEl.find(opt.itemNodeName).length) {                    this.dragRootEl.append('<div class="' + opt.emptyClass + '"/>');                }                // parent root list has changed                if (isNewRoot) {                    this.dragRootEl = pointElRoot;                    this.hasNewRoot = this.el[0] !== this.dragRootEl[0];                }            }        }    };    $.fn.nestable = function(params)    {        var lists  = this,            retval = this;        lists.each(function()        {            var plugin = $(this).data("nestable");            if (!plugin) {                $(this).data("nestable", new Plugin(this, params));                $(this).data("nestable-id", new Date().getTime());            } else {                if (typeof params === 'string' && typeof plugin[params] === 'function') {                    retval = plugin[params]();                }            }        });        return retval || lists;    };})(window.jQuery || window.Zepto, window, document);
 |