--- /dev/null
+/**
+ * @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 {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 {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 {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();
+ }
+});
\ No newline at end of file