/// 脚本引用
/// <reference path="/Scripts/jquery.1.11.0.min.js"/>

/// <summary>
/// 功能模块:启明js拖拽类
/// 作者:黄顺权
/// 编码日期:2014年09月23日
/// </summary>
(function ($) {
    /// <summary>
    /// jQuery对象的扩展方法
    /// </summary>
    $.fn.extend({
        /// 获得对象的绝对布局
        getLayout: function () {
            var pos = this.css("position") == "static" ? this.offset() : this.position();
            var w = this.width();
            var h = this.height()
            return { left: pos.left, top: pos.top, right: pos.left + w, bottom: pos.top + h, width: w, height: h };
        },

        /// 获得对象的偏移布局
        getOffsetLayout: function () {
            var pos = this.offset();
            var w = this.width();
            var h = this.height()
            return { left: pos.left, top: pos.top, right: pos.left + w, bottom: pos.top + h, width: w, height: h };
        }
    });

    /// 定义$.qm命名空间
    if (typeof ($.qm) != "object") $.qm = {};

    /// <summary>
    /// 定义拖放匿名类,最后将该类绑定到$.qm命名空间下
    /// </summary>
    (function () {

        /// 拖拽状态标志
        var _isDragging = false;

        /// 拖放对象
        var _dropObjects = [];

        /// 默认拖拽配置
        var _defaultDragOptions = {
            container: null,
            handle: null,
            proxy: null,
            revert: false,
            delay: 0,
            cursor: 'pointer',
            onStartDrag: function (e) { },
            onDrag: function (e) { },
            onStopDrag: function (e) { },
            onClick: function (e) { }
        };

        /// 默认拖放配置
        var _defaultDropOptions = {
            onDropOver: function (e) { },
            onDrop: function (e, source, target) { }
        };

        /// 单击事件代理函数
        var onclickHook = function (e) {
            if (_isDragging) return;
            var dragInfo = $.data(this, "qm_drag");
            if (dragInfo.onclick instanceof Function) dragInfo.onclick.call(this, e);
        };

        /// 双击事件代理函数
        var ondblclickHook = function (e) {
            if (_isDragging) return;
            var dragInfo = $.data(this, "qm_drag");
            if (dragInfo.ondblclick instanceof Function) dragInfo.ondblclick.call(this, e);
        };

        /// 获得鼠标位置
        var getMousePos = function (e) {
            //return { x: e.originalEvent.x || e.originalEvent.layerX || 0, y: e.originalEvent.y || e.originalEvent.layerY || 0 };
            return { x: e.pageX, y: e.pageY };
        };

        /// 判断坐标点是否在矩形内
        var ptInRect = function (pt, rect) {
            return pt.x > rect.left && pt.x < rect.right && pt.y > rect.top && pt.y < rect.bottom;
        };

        /// 判断第一个矩形是否在第二个矩形内
        var rectInRect = function (rect1, rect2) {
            return rect1.left > rect2.left && rect1.top > rect2.top && rect1.right < rect2.right && rect1.bottom < rect2.bottom;
        };

        /// 鼠标按下事件检测函数
        var mouseDownCheck = function (e) {
            //获得鼠标事件参数
            var eventArgs = $.extend(true, {}, e);
            //获得运行时参数
            var dragInfo = $.data(e.data.srcElement, "qm_drag");
            var opts = dragInfo.options;
            var source = this;
            //判断是否需要延时触发拖拽事件
            if (opts.delay > 0) { //延时触发
                this.setCapture();
                var theElementLayout = $(this).getLayout();
                var timerID = setTimeout(function () {
                    _isDragging = true;
                    $(document).unbind(".dragCheck");
                    mouseDownHandler.call(source, eventArgs);
                }, opts.delay);
                $(document).bind("mousemove.dragCheck", { srcElement: this }, function (e) {
                    var mousePos = getMousePos(e);
                    eventArgs = $.extend(true, {}, e); //获得鼠标事件参数
                    if (!ptInRect(mousePos, theElementLayout)) {
                        clearTimeout(timerID);
                        e.data.srcElement.releaseCapture();
                        $(document).unbind(".dragCheck");
                    }
                });
                $(document).bind("mouseup.dragCheck", { srcElement: this }, function (e) {
                    clearTimeout(timerID);
                    e.data.srcElement.releaseCapture();
                    $(document).unbind(".dragCheck");
                });
            }
            else {//立即触发拖拽事件
                _isDragging = true;
                mouseDownHandler.call(source, eventArgs);
            }
        };

        /// 鼠标按下事件处理函数
        var mouseDownHandler = function (e) {
            //获得运行时参数
            var dragInfo = $.data(this, "qm_drag");
            var source = dragInfo.source;
            var opts = dragInfo.options;

            //保存当前鼠标位置
            dragInfo.mousePos = getMousePos(e);

            //获得被拖拽对象的当前布局
            source.layout = source.getLayout();
            //获得容器的布局
            if (opts.container != null) {
                var container = $(opts.container);
                dragInfo.containerLayout = container.getLayout();
            }

            //获得实际被拖拽的代理对象
            if (opts.proxy == 'clone') {
                dragInfo.proxy = source.clone().insertAfter(this);
            } else {
                dragInfo.proxy = source;
            }

            var proxy = dragInfo.proxy;
            //获得拖拽对象的定位和zIndex
            source._position = source.css("position");
            source._zIndex = source.css("z-index");
            //设置被拖拽对象的起始位置
            proxy.css({ position: "absolute", left: source.layout.left + 5 + "px", top: source.layout.top + 5 + "px" });
            //设置新的zIndex
            proxy.css("z-index", 1002);
            //获得被拖拽对象的当前位置(移移5像素,以便增加视觉效果)
            proxy.currentPos = { left: source.layout.left + 5, top: source.layout.top + 5 };

            //设置鼠标抓取能力
            document.documentElement.setCapture();
            //绑定鼠标移动和抬起事件
            $(document).bind('mousemove.dragging', { srcElement: this }, mouseMoveHandler);
            $(document).bind('mouseup.dragging', { srcElement: this }, mouseUpHandler);

            //触发调用者的拖拽开始事件
            //opts.onStartDrag.call(this, e);
            opts.onStartDrag.call(proxy[0], e);
        };

        /// 鼠标移动事件处理
        var mouseMoveHandler = function (e) {
            //获得运行时参数
            var dragInfo = $.data(e.data.srcElement, "qm_drag");
            var source = dragInfo.source;
            var proxy = dragInfo.proxy;
            var opts = dragInfo.options;

            //获得当前鼠标位置
            var mousePos = getMousePos(e);
            //计算鼠标移动距离
            var distance = { x: mousePos.x - dragInfo.mousePos.x, y: mousePos.y - dragInfo.mousePos.y };
            //保存当前鼠标位置
            dragInfo.mousePos = mousePos;

            //获得被拖拽对象移到的位置
            proxy.currentPos.left += distance.x;
            proxy.currentPos.top += distance.y;
            //移动被拖拽对象
            if (opts.container != null) {
                //获得被拖拽对象的新的布局
                var rect = { left: proxy.currentPos.left, top: proxy.currentPos.top,
                    right: proxy.currentPos.left + source.layout.width,
                    bottom: proxy.currentPos.top + source.layout.height
                };
                var containerLayout = dragInfo.containerLayout;
                if (rectInRect(rect, containerLayout)) { //如果被拖拽对象在容器内部
                    //设置被拖拽对象的水平和垂直坐标
                    proxy.css({ left: proxy.currentPos.left + "px", top: proxy.currentPos.top + "px" });
                } else if (rect.left > containerLayout.left && rect.right < containerLayout.right) { //如果被拖拽象的水平坐标在容器内部
                    //设置被拖拽对象的水平坐标
                    proxy.css({ left: proxy.currentPos.left + "px" });
                } else if (rect.top > containerLayout.top && rect.bottom < containerLayout.bottom) { //如果被拖拽象的垂直坐标在容器内部
                    //设置被拖拽对象的垂直坐标
                    proxy.css({ top: proxy.currentPos.top + "px" });
                }
            }
            else {
                proxy.css({ left: proxy.currentPos.left + "px", top: proxy.currentPos.top + "px" });
            }

            //触发调用者的拖拽事件
            opts.onDrag.call(source[0], e);

            //触发拖放对象的onDropOver事件
            var eventArgs = $.extend(true, {}, e);
            for (var i = 0; i < _dropObjects.length; i++) {
                var objDrop = $(_dropObjects[i]);
                var layout = objDrop.getLayout();
                if (ptInRect(mousePos, layout)) {
                    var options = $.data(_dropObjects[i], "qm_drop").options;
                    options.onDropOver.call(_dropObjects[i], eventArgs);
                }
            }
        };

        /// 鼠标抬起事件处理
        var mouseUpHandler = function (e) {
            //获得运行时参数
            var dragInfo = $.data(e.data.srcElement, "qm_drag");
            var source = dragInfo.source;
            var proxy = dragInfo.proxy;
            var opts = dragInfo.options;

            //获得当前鼠标位置
            var mousePos = getMousePos(e);

            //取消鼠标抓取
            document.documentElement.releaseCapture();
            //取消事件绑定
            $(document).unbind('.dragging');

            //复制事件参数
            var eventArgs = $.extend(true, {}, e);

            //设置拖拽标志结束
            _isDragging = false;

            //触发调用者的拖拽结束事件
            opts.onStopDrag.call(source[0], e);

            //结束拖拽
            var stopDrag = function () {
                //处理实际被拖拽的对象
                if (opts.proxy == 'clone') {
                    proxy.remove(); //删除代理
                } else {
                    //恢复原来的定位方式和zIndex
                    source.css("position", source._position);
                    source.css("z-index", source._zIndex);
                }
            }

            //触发拖放对象的onDrop事件
            for (var i = 0; i < _dropObjects.length; i++) {
                var theElement = _dropObjects[i];
                if (theElement == e.data.srcElement) continue;
                var objDrop = $(theElement);
                var layout = objDrop.getLayout();
                if (ptInRect(mousePos, layout)) {
                    var options = $.data(theElement, "qm_drop").options;
                    if (options.onDrop.call(e.data.srcElement, eventArgs, e.data.srcElement, theElement) == false) {
                        stopDrag();
                        return;
                    }
                }
            }

            if (opts.revert) { //如果恢复到拖拽前的位置
                var layout = source.getLayout();
                proxy.animate({ left: layout.left + "px", top: layout.top + "px" }, stopDrag);
            } else {
                stopDrag();
            }
        };


        /// 拖拽方法
        this.drag = function (dragObjects, options) {
            //处理容器布局,以防拖拽超出了容器的范围
            if (options instanceof Object && options.container) {
                var container = $(options.container);
                $(window).bind("resize", { container: container }, function (e) { e.data.container[0].layout = e.data.container.getLayout(); });
            }
            //遍历每个对象注册鼠标事件
            $(dragObjects).each(function () {
                var opts;

                //获得运行时配置(以便fn.qm.drag可以多次调用)
                var dragInfo = $.data(this, "qm_drag");
                if (!(dragInfo instanceof Object)) {
                    opts = $.extend({}, _defaultDragOptions, options || {});
                } else {
                    opts = $.extend(dragInfo.options, options);
                }
                //保存运行时配置
                var objDrag = $(this);
                dragInfo = {
                    options: opts,
                    source: objDrag, //要拖拽的对象
                    handle: opts.handle ? $(opts.handle) : objDrag, //拖拽把手
                    onclick: this.onclick,
                    ondblclick: this.ondblclick
                };
                this.onclick = onclickHook;
                this.ondblclick = ondblclickHook;
                $.data(this, "qm_drag", dragInfo);

                //设置被拖拽对象的鼠标形状
                objDrag.css("cursor", opts.cursor);

                //检查鼠标按下事件
                $(dragInfo.handle).bind("mousedown", { srcElement: this }, mouseDownCheck);
            });
        };

        /// 拖放方法
        this.drop = function (dropObjects, options) {
            dropObjects = $(dropObjects);

            //遍历每个对象注册拖放事件
            $(dropObjects).each(function () {
                var opts;

                //保存拖放对象到全局数组中
                _dropObjects.push(this);

                //获得运行时配置
                var dropInfo = $.data(this, "qm_drop");
                if (!(dropInfo instanceof Object)) {
                    opts = $.extend({}, _defaultDropOptions, options || {});
                } else {
                    opts = $.extend(dropInfo.options, options);
                }
                //保存运行时配置
                var dropObj = $(this);
                dropInfo = {
                    options: opts,
                    source: dropObj //要拖放的对象
                };
                $.data(this, "qm_drop", dropInfo);
            });
        };

    }).call($.qm);
})(jQuery);