/**
 * DDSort: drag and drop sorting.
 * Documentation: https://github.com/Barrior/DDSort
 */
+function ($) {
    var defaultOptions = {
        down: $.noop,
        move: $.noop,
        up: $.noop,
        target: 'li',
        delay: 100,
        cloneStyle: {
            'background-color': '#eee'
        },
        floatStyle: {
            // 用固定定位可以防止定位父级不是Body的情况的兼容处理,表示不兼容IE6,无妨
            'position': 'fixed',
            'box-shadow': '10px 10px 20px 0 #eee',
            'webkitTransform': 'rotate(4deg)',
            'mozTransform': 'rotate(4deg)',
            'msTransform': 'rotate(4deg)',
            'transform': 'rotate(4deg)'
        }
    };

    $.fn.DDSort = function (options) {
        var $doc = $(document);
        var settings = $.extend(true, {}, defaultOptions, options);

        return this.each(function () {

            var that = $(this);
            var height = 'height';
            var width = 'width';

            if (that.css('box-sizing') == 'border-box') {
                height = 'outerHeight';
                width = 'outerWidth';
            }

            that.on('mousedown.DDSort touchstart.DDSort', settings.target, function (e) {

                var startTime = new Date().getTime();

                // 桌面端只允许鼠标左键拖动
                if (e.type == 'mousedown' && e.which != 1) return;

                // 防止表单元素,a 链接,可编辑元素失效
                var tagName = e.target.tagName.toLowerCase();
                if (tagName == 'input' || tagName == 'textarea' || tagName == 'select' ||
                    tagName == 'a' || $(e.target).prop('contenteditable') == 'true') {
                    return;
                }

                var self = this;
                var $this = $(self);
                // 鼠标按下时的元素偏移
                var offset = $this.offset();

                // 鼠标按下时的光标坐标
                // 桌面端
                var pageX = e.pageX;
                var pageY = e.pageY;

                // 移动端
                var targetTouches = e.originalEvent.targetTouches;
                if (e.type == 'touchstart' && targetTouches) {
                    pageX = targetTouches[0].pageX;
                    pageY = targetTouches[0].pageY;
                }

                var clone = $this.clone()
                        .css(settings.cloneStyle)
                        .css('height', $this[height]())
                        .empty();

                var hasClone = 1;

                // 缓存计算
                var thisOuterHeight = $this.outerHeight(),
                    thisOuterWidth = $this.outerWidth(),
                    thatOuterHeight = that.outerHeight(),
                    thatOuterWidth = that.outerWidth();

                // 滚动速度
                var upSpeed = thisOuterHeight,
                    downSpeed = thisOuterHeight,
                    leftSpeed = thisOuterWidth,
                    rightSpeed = thisOuterWidth,
                    maxSpeed = thisOuterHeight * 3;

                settings.down.call(self);

                $doc.on('mousemove.DDSort touchmove.DDSort', function (e) {

                    // 鼠标移动时的光标坐标
                    // 桌面端
                    var _pageX = e.pageX;
                    var _pageY = e.pageY;

                    // 移动端
                    var targetTouches = e.originalEvent.targetTouches;
                    if (e.type == 'touchmove' && targetTouches) {
                        _pageX = targetTouches[0].pageX;
                        _pageY = targetTouches[0].pageY;
                    }

                    if (new Date().getTime() - startTime < settings.delay) {
                        return;
                    }

                    if (hasClone) {
                        $this.before(clone)
                            .css('width', $this[width]())
                            .css(settings.floatStyle)
                            .appendTo($this.parent());

                        hasClone = 0;
                    }

                    var disX = pageX - _pageX;
                    var disY = pageY - _pageY;
                    var left = offset.left - disX;
                    var top = offset.top - disY;
                    
                    $this.offset({
                        left: left,
                        top: top 
                    });

                    var $left = getLeft(clone),
                        $right = getRight(clone, $this),
                        $top = getTop(clone),
                        $under = getUnder(clone, $this);


                    if ($top && $top.length && top < $top.offset().top + $top.outerHeight(true) / 2) {
                        // 向上排序
                        $top.before(clone);
                        
                    } else if ($under && $under.length && top + thisOuterHeight > $under.offset().top + $under.outerHeight(true) / 2) {
                        // 向下排序
                        $under.after(clone);

                    } else if($left && $left.length && left < $left.offset().left + $left.outerWidth(true) / 2) {
                        //向左排序
                        $left.before(clone);
                        
                    } else if($right && $right.length && left + thisOuterWidth > $right.offset().left + $right.outerWidth(true) / 2) {
                        //向右排序
                        $right.after(clone);
                        
                    }

                    // 处理滚动条,that 是带着滚动条的元素,这里默认以为 that 元素是这样的元素(正常情况就是这样),
                    // 如果使用者事件委托的元素不是这样的元素,那么需要提供接口出来
                    var thatScrollTop = that.scrollTop();
                    var thatOffsetTop = that.offset().top;
                    if (top < thatOffsetTop) {
                        // 向上滚动
                        downSpeed = thisOuterHeight;
                        upSpeed = ++upSpeed > maxSpeed ? maxSpeed : upSpeed;
                        var scrollVal = thatScrollTop - upSpeed;
                        that.scrollTop(scrollVal);
                    } else if (top + thisOuterHeight - thatOffsetTop > thatOuterHeight) {
                        // 向下滚动
                        upSpeed = thisOuterHeight;
                        downSpeed = ++downSpeed > maxSpeed ? maxSpeed : downSpeed;
                        var scrollVal = thatScrollTop + downSpeed;
                        that.scrollTop(scrollVal);
                    }

                    var thatScrollLeft = that.scrollLeft();
                    var thatOffsetLeft = that.offset().left;
                    if (left < that.offset().left) {
                        // 向左滚动
                        rightSpeed = thisOuterWidth;
                        leftSpeed = ++leftSpeed > maxSpeed ? maxSpeed : leftSpeed;
                        var scrollVal = thatScrollLeft - leftSpeed;
                        that.scrollLeft(scrollVal);
                    } else if (left + thisOuterWidth - thatOffsetLeft > thatOuterWidth) {
                        // 向右滚动
                        leftSpeed = thisOuterWidth;
                        rightSpeed = ++rightSpeed > maxSpeed ? maxSpeed : rightSpeed;
                        var scrollVal = thatScrollLeft + rightSpeed;
                        that.scrollLeft(scrollVal);
                    }

                    settings.move.call(self, left, top);
                })
                .on('mouseup.DDSort touchend.DDSort', function () {

                    $doc.off('mousemove.DDSort mouseup.DDSort touchmove.DDSort touchend.DDSort');

                    // click 的时候也会触发 mouseup 事件,加上判断阻止这种情况
                    if (!hasClone) {
                        clone.before($this.removeAttr('style')).remove();
                        settings.up.call(self);
                    }
                });

                return false;
            });
        });
    };
    
    
    //允许计算误差
    var deviation = 5;
    
    var getLeft = function (clone) {
        var left = clone.prev();
        if(left.length && clone.offset().top==left.offset().top) {
            var _dev = Math.abs(clone.offset().left - (left.offset().left + left.outerWidth(true)));
            if(_dev <= deviation) {
                return left;
            }
        }
        return undefined;
    }
    var getTop = function (clone, prev) {
        if(!prev){
            prev = clone.prev();
        }
        if(!prev.length) {
            return undefined;
        }
        if(clone.offset().left==prev.offset().left) {
            var _dev = Math.abs(clone.offset().top - (prev.offset().top+prev.outerHeight(true)));
            if(_dev <= deviation) {
                return prev;
            }
        }
        return getTop(clone, prev.prev());
    }
    var getRight = function (clone, $this) {
        var rigth = clone.next().not($this);
        if(rigth.length && clone.offset().top==rigth.offset().top) {
            var _dev = Math.abs(clone.offset().left - (rigth.offset().left-clone.outerWidth(true)));
            if(_dev <= deviation) {
                return rigth;
            }
        }
        return undefined;
    }
    var getUnder = function (clone, $this, next) {
        if(!next){
            next = clone.next().not($this);
        }
        if(!next.length) {
            return undefined;
        }
        if(clone.offset().left==next.offset().left) {
            var _dev = Math.abs(clone.offset().top - (next.offset().top-clone.outerHeight(true)));
            if(_dev <= deviation) {
                return next;
            }
        }
        return getUnder(clone, $this, next.next().not($this));
    }
}(jQuery);