The source code

    /**
     * @ignore
     *  dd support for kissy, drag for dd
     * @author yiminghe@gmail.com
     */
    KISSY.add('dd/base/draggable', function (S, Node, RichBase, DDM, Event) {

        var UA = S.UA,
            each = S.each,
            DRAG_START_EVENT = Event.Gesture.start,
            ie = UA['ie'],
            Features = S.Features,
            NULL = null,
            PREFIX_CLS = DDM.PREFIX_CLS,
            doc = S.Env.host.document;

        /**
         * @class KISSY.DD.Draggable
         * @extends KISSY.RichBase
         * Provide abilities to make specified node draggable
         */

        var Draggable = RichBase.extend({

            initializer: function () {
                var self = this;
                self.addTarget(DDM);
                /**
                 * fired when need to compute draggable 's position during dragging
                 * @event dragalign
                 * @member KISSY.DD.DDM
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 */

                /**
                 * fired when need to get draggable 's position during dragging
                 * @event dragalign
                 * @member KISSY.DD.Draggable
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 */

                /**
                 * fired during dragging
                 * @event drag
                 * @member KISSY.DD.DDM
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 * @param e.left node 's supposed position left
                 * @param e.top node 's supposed position top
                 * @param e.pageX mouse position left
                 * @param e.pageY mouse position top
                 */

                /**
                 * fired during dragging
                 * @event drag
                 * @member KISSY.DD.Draggable
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 * @param e.left node 's supposed position left
                 * @param e.top node 's supposed position top
                 * @param e.pageX mouse position left
                 * @param e.pageY mouse position top
                 */

                /**
                 * fired after drop a draggable onto a droppable object
                 * @event dragdrophit
                 * @member KISSY.DD.DDM
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 * @param e.drop current droppable object
                 */

                /**
                 * fired after drop a draggable onto a droppable object
                 * @event dragdrophit
                 * @member KISSY.DD.Draggable
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 * @param e.drop current droppable object
                 */

                /**
                 * fired after drag
                 * @event dragend
                 * @member KISSY.DD.DDM
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 */

                /**
                 * fired after drag
                 * @event dragend
                 * @member KISSY.DD.Draggable
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 */

                /**
                 * fired after drop a draggable onto nothing
                 * @event dragdropmiss
                 * @member KISSY.DD.DDM
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 */

                /**
                 * fired after drop a draggable onto nothing
                 * @event dragdropmiss
                 * @member KISSY.DD.Draggable
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 */

                /**
                 * fired after a draggable leaves a droppable
                 * @event dragexit
                 * @member KISSY.DD.DDM
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 * @param e.drop current droppable object
                 */

                /**
                 * fired after a draggable leaves a droppable
                 * @event dragexit
                 * @member KISSY.DD.Draggable
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 * @param e.drop current droppable object
                 */

                /**
                 * fired after a draggable object mouseenter a droppable object
                 * @event dragenter
                 * @member KISSY.DD.DDM
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 * @param e.drop current droppable object
                 */

                /**
                 * fired after a draggable object mouseenter a droppable object
                 * @event dragenter
                 * @member KISSY.DD.Draggable
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 * @param e.drop current droppable object
                 */

                /**
                 * fired after a draggable object mouseover a droppable object
                 * @event dragover
                 * @member KISSY.DD.DDM
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 * @param e.drop current droppable object
                 */

                /**
                 * fired after a draggable object mouseover a droppable object
                 * @event dragover
                 * @member KISSY.DD.Draggable
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 * @param e.drop current droppable object
                 */

                /**
                 * fired after a draggable object start to drag
                 * @event dragstart
                 * @member KISSY.DD.DDM
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 */

                /**
                 * fired after a draggable object start to drag
                 * @event dragstart
                 * @member KISSY.DD.Draggable
                 * @param {KISSY.Event.CustomEventObject} e
                 * @param e.drag current draggable object
                 */
            },

            '_onSetNode': function (n) {
                var self=this;
                // dragNode is equal to node in single mode
                self.setInternal('dragNode', n);
                self.bindDragEvent();
            },

            bindDragEvent: function () {
                var self = this, node = self.get('node');
                node.on(DRAG_START_EVENT, handlePreDragStart, self)
                    .on('dragstart', self._fixDragStart);
            },

            detachDragEvent: function () {
                var self = this, node = self.get('node');
                node.detach(DRAG_START_EVENT, handlePreDragStart, self)
                    .detach('dragstart', self._fixDragStart);
            },

            /**
             * mousedown 1秒后自动开始拖的定时器
             * @ignore
             */
            _bufferTimer: NULL,

            _onSetDisabledChange: function (d) {
                this.get('dragNode')[d ? 'addClass' :
                    'removeClass'](PREFIX_CLS + '-disabled');
            },

            _fixDragStart: fixDragStart,

            _checkHandler: function (t) {
                var self = this,
                    handlers = self.get('handlers'),
                    ret = 0;
                each(handlers, function (handler) {
                    //子区域内点击也可以启动
                    if (handler.contains(t) ||
                        handler[0] == t) {
                        ret = 1;
                        self.setInternal('activeHandler', handler);
                        return false;
                    }
                });
                return ret;
            },

            _checkDragStartValid: function (ev) {
                var self = this;
                if (self.get('primaryButtonOnly') && ev.which != 1 ||
                    self.get('disabled')) {
                    return 0;
                }
                return 1;
            },

            _prepare: function (ev) {

                if (!ev) {
                    return;
                }

                var self = this;

                if (ie) {
                    fixIEMouseDown();
                }

                // http://blogs.msdn.com/b/ie/archive/2011/10/19/handling-multi-touch-and-mouse-input-in-all-browsers.aspx
                // stop panning and zooming so we can draw for win8?
    //            if (ev.originalEvent['preventManipulation']) {
    //                ev.originalEvent.preventManipulation();
    //            }

                // 防止 firefox/chrome 选中 text
                // 非 ie，阻止了 html dd 的默认行为
                if (self.get('halt')) {
                    ev.stopPropagation();
                }

                // in touch device
                // prevent touchdown
                // will prevent text selection and link click
                if (!Features.isTouchSupported()) {
                    ev.preventDefault();
                }

                var node = self.get('node'),
                    mx = ev.pageX,
                    my = ev.pageY,
                    nxy = node.offset();

                self.setInternal('startMousePos', self.mousePos = {
                    left: mx,
                    top: my
                });

                self.setInternal('startNodePos', nxy);

                self.setInternal('deltaPos', {
                    left: mx - nxy.left,
                    top: my - nxy.top
                });

                DDM._regToDrag(self);

                var bufferTime = self.get('bufferTime');

                // 是否中央管理，强制限制拖放延迟
                if (bufferTime) {
                    self._bufferTimer = setTimeout(function () {
                        // 事件到了，仍然是 mousedown 触发！
                        //S.log('drag start by timeout');
                        self._start();
                    }, bufferTime * 1000);
                }

            },

            _clearBufferTimer: function () {
                var self = this;
                if (self._bufferTimer) {
                    clearTimeout(self._bufferTimer);
                    self._bufferTimer = 0;
                }
            },

            _move: function (ev) {

                var self = this,
                    ret,
                    pageX = ev.pageX,
                    pageY = ev.pageY;

                if (!self.get('dragging')) {
                    var startMousePos = self.get('startMousePos'),
                        clickPixelThresh = self.get('clickPixelThresh');
                    // 鼠标经过了一定距离，立即开始
                    if (Math.abs(pageX - startMousePos.left) >= clickPixelThresh ||
                        Math.abs(pageY - startMousePos.top) >= clickPixelThresh) {
                        //S.log('start drag by pixel : ' + l1 + ' : ' + l2);
                        self._start();
                    }
                    // 开始后，下轮 move 开始触发 drag 事件
                    return;
                }

                var diff = self.get('deltaPos'),
                    left = pageX - diff.left,
                    top = pageY - diff.top;

                self.mousePos = {
                    left: pageX,
                    top: pageY
                };

                ret = {
                    left: left,
                    top: top,
                    pageX: pageX,
                    pageY: pageY,
                    drag: self
                };

                self.setInternal('actualPos', {
                    left: left,
                    top: top
                });

                self.fire('dragalign', ret);

                var def = 1;

                if (self.fire('drag', ret) === false) {
                    def = 0;
                }

                if (def && self.get('move')) {
                    // 取 'node' , 改 node 可能是代理哦
                    self.get('node').offset(self.get('actualPos'));
                }
            },

            /**
             * force to stop this drag operation
             * @member KISSY.DD.Draggable
             */
            stopDrag: function () {
                DDM._end();
            },

            _end: function () {
                var self = this,
                    activeDrop;

                // 否则清除定时器即可
                self._clearBufferTimer();
                if (ie) {
                    fixIEMouseUp();
                }
                // 如果已经开始，收尾工作
                if (self.get('dragging')) {
                    self.get('node')
                        .removeClass(PREFIX_CLS + 'drag-over');
                    if (activeDrop = DDM.get('activeDrop')) {
                        self.fire('dragdrophit', {
                            drag: self,
                            drop: activeDrop
                        });
                    } else {
                        self.fire('dragdropmiss', {
                            drag: self
                        });
                    }
                    self.setInternal('dragging', 0);
                    self.fire('dragend', {
                        drag: self
                    });
                }
            },

            _handleOut: function () {
                var self = this;
                self.get('node').removeClass(PREFIX_CLS + 'drag-over');

                // html5 => dragleave
                self.fire('dragexit', {
                    drag: self,
                    drop: DDM.get('activeDrop')
                });
            },

            _handleEnter: function (e) {
                var self = this;
                self.get('node').addClass(PREFIX_CLS + 'drag-over');
                //第一次先触发 dropenter, dragenter
                self.fire('dragenter', e);
            },

            _handleOver: function (e) {
                this.fire('dragover', e);
            },

            _start: function () {
                var self = this;
                self._clearBufferTimer();
                self.setInternal('dragging', 1);
                DDM._start();
                self.fire('dragstart', {
                    drag: self
                });
            },

            /**
             * make the drag node undraggable
             * @member KISSY.DD.Draggable
             * @private
             */
            destructor: function () {
                var self = this;
                self.detachDragEvent();
                self.detach();
            }

        }, {
            ATTRS: {

                /**
                 * the dragged node. maybe a proxy node.
                 * @property node
                 * @type {HTMLElement|KISSY.NodeList}
                 * @readonly
                 */

                /**
                 * the dragged node.
                 * @cfg {HTMLElement|KISSY.NodeList} node
                 */

                /**
                 * @ignore
                 */
                node: {
                    setter: function (v) {
                        if (!(v instanceof Node)) {
                            return Node.one(v);
                        }
                    }
                },

                /**
                 * the number of pixels to move to start a drag operation
                 *
                 * Defaults to: {@link KISSY.DD.DDM#clickPixelThresh}.
                 *
                 * @cfg {Number} clickPixelThresh
                 */
                /**
                 * @ignore
                 */
                clickPixelThresh: {
                    valueFn: function () {
                        return DDM.get('clickPixelThresh');
                    }
                },

                /**
                 * the number of milliseconds to start a drag operation after mousedown.
                 *
                 * Defaults to: {@link KISSY.DD.DDM#bufferTime}.
                 *
                 * @cfg {Number} bufferTime
                 */
                /**
                 * @ignore
                 */
                bufferTime: {
                    valueFn: function () {
                        return DDM.get('bufferTime');
                    }
                },

                /**
                 * the draggable element.
                 * @property dragNode
                 * @type {HTMLElement}
                 * @readonly
                 */
                /**
                 * @ignore
                 */
                dragNode: {},

                /**
                 * use protective shim to cross iframe.
                 *
                 * Defaults to: true
                 *
                 * @cfg {Boolean} shim
                 *
                 */
                /**
                 * @ignore
                 */
                shim: {
                    value: true
                },

                /**
                 * valid handlers to initiate a drag operation.
                 *
                 * Default same with {@link KISSY.DD.Draggable#cfg-node} config.
                 *
                 * @cfg {HTMLElement[]|Function[]|String[]} handlers
                 */
                /**
                 * @ignore
                 */
                handlers: {
                    value: [],
                    getter: function (vs) {
                        var self = this;
                        if (!vs.length) {
                            vs[0] = self.get('node');
                        }
                        each(vs, function (v, i) {
                            if (S.isFunction(v)) {
                                v = v.call(self);
                            }
                            // search inside node
                            if (typeof v == 'string') {
                                v = self.get('node').one(v);
                            }
                            if (v.nodeType) {
                                v = Node.one(v);
                            }
                            vs[i] = v;
                        });
                        self.setInternal('handlers', vs);
                        return vs;
                    }
                },

                /**
                 * the handler which fired the drag event.
                 * @type {KISSY.NodeList}
                 * @property activeHandler
                 * @readonly
                 */
                /**
                 * @ignore
                 */
                activeHandler: {},

                /**
                 * indicate whether this draggable object is being dragged
                 * @type {Boolean}
                 * @property dragging
                 * @readonly
                 */
                /**
                 * @ignore
                 */
                dragging: {
                    value: false,
                    setter: function (d) {
                        var self = this;
                        self.get('dragNode')[d ? 'addClass' : 'removeClass']
                            (PREFIX_CLS + 'dragging');
                    }
                },

                /**
                 * drop mode.
                 * @cfg {KISSY.DD.Draggable.DropMode} mode
                 */
                /**
                 * @ignore
                 */
                mode: {
                    value: 'point'
                },

                /**
                 * set to disable this draggable so that it can not be dragged.
                 *
                 * Defaults to: false
                 *
                 * @type {Boolean}
                 * @property disabled
                 */
                /**
                 * @ignore
                 */
                disabled: {
                    value: false
                },

                /**
                 * whether the drag node moves with cursor, can be used to resize element.
                 *
                 * Defaults to: false
                 *
                 * @cfg {Boolean} move
                 */
                /**
                 * @ignore
                 */
                move: {
                    value: false
                },

                /**
                 * whether a drag operation can only be trigged by primary(left) mouse button.
                 * Setting false will allow for all mousedown events to trigger drag.
                 * @cfg {Boolean} primaryButtonOnly
                 */
                /**
                 * @ignore
                 */
                primaryButtonOnly: {
                    value: true
                },

                /**
                 * whether halt mousedown event.
                 *
                 * Defaults to: true
                 *
                 * @cfg {Boolean} halt
                 */
                /**
                 * @ignore
                 */
                halt: {
                    value: true
                },

                /**
                 * groups this draggable object belongs to
                 * for example:
                 *      @example
                 *      {
                 *          'group1':1,
                 *          'group2':1
                 *      }
                 *
                 * @cfg {Object} groups
                 */
                /**
                 * @ignore
                 */
                groups: {
                    value: {}
                },

                /**
                 * mouse position at drag start
                 * for example:
                 *      @example
                 *      {
                 *          left: 100,
                 *          top: 200
                 *      }
                 *
                 * @property startMousePos
                 * @type {Object}
                 * @readonly
                 */
                /**
                 * @ignore
                 */
                startMousePos: {

                },

                /**
                 * node position ar drag start
                 * for example:
                 *      @example
                 *      {
                 *          left: 100,
                 *          top: 200
                 *      }
                 *
                 * @property startNodePos
                 * @type {Object}
                 * @readonly
                 */
                /**
                 * @ignore
                 */
                startNodePos: {

                },

                /**
                 * The offset of the mouse position to the element's position
                 * @property deltaPos
                 * @type {Object}
                 * @readonly
                 */
                /**
                 * @ignore
                 */
                deltaPos: {

                },

                /**
                 * The xy that the node will be set to. Changing this will alter the position as it's dragged.
                 * @property actualPos
                 * @type {Object}
                 * @readonly
                 */
                /**
                 * @ignore
                 */
                actualPos: {

                }
            }
        });

        /**
         * drag drop mode enum.
         * @enum {String} KISSY.DD.Draggable.DropMode
         */
        Draggable.DropMode = {
            /**
             * In point mode, a Drop is targeted by the cursor being over the Target
             */
            'POINT': 'point',
            /**
             * In intersect mode, a Drop is targeted by 'part' of the drag node being over the Target
             */
            INTERSECT: 'intersect',
            /**
             * In strict mode, a Drop is targeted by the 'entire' drag node being over the Target
             */
            STRICT: 'strict'
        };

        var _ieSelectBack;

        function fixIEMouseUp() {
            doc.body.onselectstart = _ieSelectBack;
        }

        // prevent select text in ie
        function fixIEMouseDown() {
            _ieSelectBack = doc.body.onselectstart;
            doc.body.onselectstart = fixIESelect;
        }

        /*
         1. keeps IE from blowing up on images as drag handlers.
         IE 在 img 上拖动时默认不能拖动（不触发 mousemove，mouseup 事件，mouseup 后接着触发 mousemove ...）
         2. 防止 html5 draggable 元素的拖放默认行为 (选中文字拖放)
         3. 防止默认的选择文本行为(??场景？)
         */
        function fixDragStart(e) {
            e.preventDefault();
        }

        /*
         keeps IE from selecting text
         */
        function fixIESelect() {
            return false;
        }

        /*
         鼠标按下时，查看触发源是否是属于 handler 集合，
         保存当前状态
         通知全局管理器开始作用
         */
        var handlePreDragStart = function (ev) {

            ev = DDM._normalEvent(ev);

            if (!ev) {
                return;
            }

            var self = this,
                t = ev.target;

            if (self._checkDragStartValid(ev)) {

                if (!self._checkHandler(t)) {
                    return;
                }

                self._prepare(ev);
            }
        };

        return Draggable;

    }, {
        requires: ['node', 'rich-base', './ddm', 'event']
    });