    /**
     * @class Ext.ux.DataView.DragSelector
     * @extends Object
     * @author Ed Spencer
     *
     */
    Ext.define('Ext.ux.DataView.DragSelector', {
        requires: ['Ext.dd.DragTracker', 'Ext.util.Region'],

        /**
         * Initializes the plugin by setting up the drag tracker
         */
        init: function(dataview) {
            /**
             * @property dataview
             * @type Ext.view.View
             * The DataView bound to this instance
             */
            this.dataview = dataview;
            dataview.mon(dataview, {
                beforecontainerclick: this.cancelClick,
                scope: this,
                render: {
                    fn: this.onRender,
                    scope: this,
                    single: true
                }
            });
        },

        /**
         * @private
         * Called when the attached DataView is rendered. This sets up the DragTracker instance that will be used
         * to created a dragged selection area
         */
        onRender: function() {
            /**
             * @property tracker
             * @type Ext.dd.DragTracker
             * The DragTracker attached to this instance. Note that the 4 on* functions are called in the scope of the
             * DragTracker ('this' refers to the DragTracker inside those functions), so we pass a reference to the
             * DragSelector so that we can call this class's functions.
             */
            this.tracker = Ext.create('Ext.dd.DragTracker', {
                dataview: this.dataview,
                el: this.dataview.el,
                dragSelector: this,
                onBeforeStart: this.onBeforeStart,
                onStart: this.onStart,
                onDrag : this.onDrag,
                onEnd  : this.onEnd
            });

            /**
             * @property dragRegion
             * @type Ext.util.Region
             * Represents the region currently dragged out by the user. This is used to figure out which dataview nodes are
             * in the selected area and to set the size of the Proxy element used to highlight the current drag area
             */
            this.dragRegion = Ext.create('Ext.util.Region');
        },

        /**
         * @private
         * Listener attached to the DragTracker's onBeforeStart event. Returns false if the drag didn't start within the
         * DataView's el
         */
        onBeforeStart: function(e) {
            return e.target == this.dataview.getEl().dom;
        },

        /**
         * @private
         * Listener attached to the DragTracker's onStart event. Cancel's the DataView's containerclick event from firing
         * and sets the start co-ordinates of the Proxy element. Clears any existing DataView selection
         * @param {Ext.EventObject} e The click event
         */
        onStart: function(e) {
            var dragSelector = this.dragSelector,
                dataview     = this.dataview;

            // Flag which controls whether the cancelClick method vetoes the processing of the DataView's containerclick event.
            // On IE (where else), this needs to remain set for a millisecond after mouseup because even though the mouse has
            // moved, the mouseup will still trigger a click event.
            this.dragging = true;

            //here we reset and show the selection proxy element and cache the regions each item in the dataview take up
            dragSelector.fillRegions();
            dragSelector.getProxy().show();
            dataview.getSelectionModel().deselectAll();
        },

        /**
         * @private
         * Reusable handler that's used to cancel the container click event when dragging on the dataview. See onStart for
         * details
         */
        cancelClick: function() {
            return !this.tracker.dragging;
        },

        /**
         * @private
         * Listener attached to the DragTracker's onDrag event. Figures out how large the drag selection area should be and
         * updates the proxy element's size to match. Then iterates over all of the rendered items and marks them selected
         * if the drag region touches them
         * @param {Ext.EventObject} e The drag event
         */
        onDrag: function(e) {
            var dragSelector = this.dragSelector,
                selModel     = dragSelector.dataview.getSelectionModel(),
                dragRegion   = dragSelector.dragRegion,
                bodyRegion   = dragSelector.bodyRegion,
                proxy        = dragSelector.getProxy(),
                regions      = dragSelector.regions,
                length       = regions.length,

                startXY   = this.startXY,
                currentXY = this.getXY(),
                minX      = Math.min(startXY[0], currentXY[0]),
                minY      = Math.min(startXY[1], currentXY[1]),
                width     = Math.abs(startXY[0] - currentXY[0]),
                height    = Math.abs(startXY[1] - currentXY[1]),
                region, selected, i;

            Ext.apply(dragRegion, {
                top: minY,
                left: minX,
                right: minX + width,
                bottom: minY + height
            });

            dragRegion.constrainTo(bodyRegion);
            proxy.setRegion(dragRegion);

            for (i = 0; i < length; i++) {
                region = regions[i];
                selected = dragRegion.intersect(region);

                if (selected) {
                    selModel.select(i, true);
                } else {
                    selModel.deselect(i);
                }
            }
        },

        /**
         * @private
         * Listener attached to the DragTracker's onEnd event. This is a delayed function which executes 1
         * millisecond after it has been called. This is because the dragging flag must remain active to cancel
         * the containerclick event which the mouseup event will trigger.
         * @param {Ext.EventObject} e The event object
         */
        onEnd: Ext.Function.createDelayed(function(e) {
            var dataview = this.dataview,
                selModel = dataview.getSelectionModel(),
                dragSelector = this.dragSelector;

            this.dragging = false;
            dragSelector.getProxy().hide();
        }, 1),

        /**
         * @private
         * Creates a Proxy element that will be used to highlight the drag selection region
         * @return {Ext.Element} The Proxy element
         */
        getProxy: function() {
            if (!this.proxy) {
                this.proxy = this.dataview.getEl().createChild({
                    tag: 'div',
                    cls: 'x-view-selector'
                });
            }
            return this.proxy;
        },

        /**
         * @private
         * Gets the region taken up by each rendered node in the DataView. We use these regions to figure out which nodes
         * to select based on the selector region the user has dragged out
         */
        fillRegions: function() {
            var dataview = this.dataview,
                regions  = this.regions = [];

            dataview.all.each(function(node) {
                regions.push(node.getRegion());
            });
            this.bodyRegion = dataview.getEl().getRegion();
        }
    });