    /**
     * @author Ed Spencer
     *
    <pre><code>
    Ext.create('Ext.view.View', {
        mixins: {
            draggable: 'Ext.ux.DataView.Draggable'
        },

        initComponent: function() {
            this.mixins.draggable.init(this, {
                ddConfig: {
                    ddGroup: 'someGroup'
                }
            });

            this.callParent(arguments);
        }
    });
    </code></pre>
     *
     */
    Ext.define('Ext.ux.DataView.Draggable', {
        requires: 'Ext.dd.DragZone',

        /**
         * @cfg {String} ghostCls The CSS class added to the outermost element of the created ghost proxy
         * (defaults to 'x-dataview-draggable-ghost')
         */
        ghostCls: 'x-dataview-draggable-ghost',

        /**
         * @cfg {Ext.XTemplate/Array} ghostTpl The template used in the ghost DataView
         */
        ghostTpl: [
            '<tpl for=".">',
                '{title}',
            '</tpl>'
        ],

        /**
         * @cfg {Object} ddConfig Config object that is applied to the internally created DragZone
         */

        /**
         * @cfg {String} ghostConfig Config object that is used to configure the internally created DataView
         */

        init: function(dataview, config) {
            /**
             * @property dataview
             * @type Ext.view.View
             * The Ext.view.View instance that this DragZone is attached to
             */
            this.dataview = dataview;

            dataview.on('render', this.onRender, this);

            Ext.apply(this, {
                itemSelector: dataview.itemSelector,
                ghostConfig : {}
            }, config || {});

            Ext.applyIf(this.ghostConfig, {
                itemSelector: 'img',
                cls: this.ghostCls,
                tpl: this.ghostTpl
            });
        },

        /**
         * @private
         * Called when the attached DataView is rendered. Sets up the internal DragZone
         */
        onRender: function() {
            var config = Ext.apply({}, this.ddConfig || {}, {
                dvDraggable: this,
                dataview   : this.dataview,
                getDragData: this.getDragData,
                getTreeNode: this.getTreeNode,
                afterRepair: this.afterRepair,
                getRepairXY: this.getRepairXY
            });

            /**
             * @property dragZone
             * @type Ext.dd.DragZone
             * The attached DragZone instane
             */
            this.dragZone = Ext.create('Ext.dd.DragZone', this.dataview.getEl(), config);
        },

        getDragData: function(e) {
            var draggable = this.dvDraggable,
                dataview  = this.dataview,
                selModel  = dataview.getSelectionModel(),
                target    = e.getTarget(draggable.itemSelector),
                selected, dragData;

            if (target) {
                if (!dataview.isSelected(target)) {
                    selModel.select(dataview.getRecord(target));
                }

                selected = dataview.getSelectedNodes();
                dragData = {
                    copy: true,
                    nodes: selected,
                    records: selModel.getSelection(),
                    item: true
                };

                if (selected.length == 1) {
                    dragData.single = true;
                    dragData.ddel = target;
                } else {
                    dragData.multi = true;
                    dragData.ddel = draggable.prepareGhost(selModel.getSelection()).dom;
                }

                return dragData;
            }

            return false;
        },

        getTreeNode: function() {
            // console.log('test');
        },

        afterRepair: function() {
            this.dragging = false;

            var nodes  = this.dragData.nodes,
                length = nodes.length,
                i;

            //FIXME: Ext.fly does not work here for some reason, only frames the last node
            for (i = 0; i < length; i++) {
                Ext.get(nodes[i]).frame('#8db2e3', 1);
            }
        },

        /**
         * @private
         * Returns the x and y co-ordinates that the dragged item should be animated back to if it was dropped on an
         * invalid drop target. If we're dragging more than one item we don't animate back and just allow afterRepair
         * to frame each dropped item.
         */
        getRepairXY: function(e) {
            if (this.dragData.multi) {
                return false;
            } else {
                var repairEl = Ext.get(this.dragData.ddel),
                    repairXY = repairEl.getXY();

                //take the item's margins and padding into account to make the repair animation line up perfectly
                repairXY[0] += repairEl.getPadding('t') + repairEl.getMargin('t');
                repairXY[1] += repairEl.getPadding('l') + repairEl.getMargin('l');

                return repairXY;
            }
        },

        /**
         * Updates the internal ghost DataView by ensuring it is rendered and contains the correct records
         * @param {Array} records The set of records that is currently selected in the parent DataView
         * @return {Ext.view.View} The Ghost DataView
         */
        prepareGhost: function(records) {
            var ghost = this.createGhost(records),
                store = ghost.store;

            store.removeAll();
            store.add(records);

            return ghost.getEl();
        },

        /**
         * @private
         * Creates the 'ghost' DataView that follows the mouse cursor during the drag operation. This div is usually a
         * lighter-weight representation of just the nodes that are selected in the parent DataView.
         */
        createGhost: function(records) {
            if (!this.ghost) {
                var ghostConfig = Ext.apply({}, this.ghostConfig, {
                    store: Ext.create('Ext.data.Store', {
                        model: records[0].modelName
                    })
                });

                this.ghost = Ext.create('Ext.view.View', ghostConfig);

                this.ghost.render(document.createElement('div'));
            }

            return this.ghost;
        }
    });