Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / examples / ux / DataView / DragSelector.js
1 /**
2  * @class Ext.ux.DataView.DragSelector
3  * @extends Object
4  * @author Ed Spencer
5  * 
6  */
7 Ext.define('Ext.ux.DataView.DragSelector', {
8     requires: ['Ext.dd.DragTracker', 'Ext.util.Region'],
9
10     /**
11      * Initializes the plugin by setting up the drag tracker
12      */
13     init: function(dataview) {
14         /**
15          * @property dataview
16          * @type Ext.view.View
17          * The DataView bound to this instance
18          */
19         this.dataview = dataview;
20         dataview.mon(dataview, {
21             beforecontainerclick: this.cancelClick,
22             scope: this,
23             render: {
24                 fn: this.onRender,
25                 scope: this,
26                 single: true
27             }
28         });
29     },
30
31     /**
32      * @private
33      * Called when the attached DataView is rendered. This sets up the DragTracker instance that will be used
34      * to created a dragged selection area
35      */
36     onRender: function() {
37         /**
38          * @property tracker
39          * @type Ext.dd.DragTracker
40          * The DragTracker attached to this instance. Note that the 4 on* functions are called in the scope of the 
41          * DragTracker ('this' refers to the DragTracker inside those functions), so we pass a reference to the 
42          * DragSelector so that we can call this class's functions.
43          */
44         this.tracker = Ext.create('Ext.dd.DragTracker', {
45             dataview: this.dataview,
46             el: this.dataview.el,
47             dragSelector: this,
48             onBeforeStart: this.onBeforeStart,
49             onStart: this.onStart,
50             onDrag : this.onDrag,
51             onEnd  : this.onEnd
52         });
53
54         /**
55          * @property dragRegion
56          * @type Ext.util.Region
57          * Represents the region currently dragged out by the user. This is used to figure out which dataview nodes are
58          * in the selected area and to set the size of the Proxy element used to highlight the current drag area
59          */
60         this.dragRegion = Ext.create('Ext.util.Region');
61     },
62
63     /**
64      * @private
65      * Listener attached to the DragTracker's onBeforeStart event. Returns false if the drag didn't start within the
66      * DataView's el
67      */
68     onBeforeStart: function(e) {
69         return e.target == this.dataview.getEl().dom;
70     },
71
72     /**
73      * @private
74      * Listener attached to the DragTracker's onStart event. Cancel's the DataView's containerclick event from firing
75      * and sets the start co-ordinates of the Proxy element. Clears any existing DataView selection
76      * @param {EventObject} e The click event
77      */
78     onStart: function(e) {
79         var dragSelector = this.dragSelector,
80             dataview     = this.dataview;
81
82         // Flag which controls whether the cancelClick method vetoes the processing of the DataView's containerclick event.
83         // On IE (where else), this needs to remain set for a millisecond after mouseup because even though the mouse has
84         // moved, the mouseup will still trigger a click event.
85         this.dragging = true;
86
87         //here we reset and show the selection proxy element and cache the regions each item in the dataview take up
88         dragSelector.fillRegions();
89         dragSelector.getProxy().show();
90         dataview.getSelectionModel().deselectAll();
91     },
92
93     /**
94      * @private
95      * Reusable handler that's used to cancel the container click event when dragging on the dataview. See onStart for
96      * details
97      */
98     cancelClick: function() {
99         return !this.tracker.dragging;
100     },
101
102     /**
103      * @private
104      * Listener attached to the DragTracker's onDrag event. Figures out how large the drag selection area should be and
105      * updates the proxy element's size to match. Then iterates over all of the rendered items and marks them selected
106      * if the drag region touches them
107      * @param {EventObject} e The drag event
108      */
109     onDrag: function(e) {
110         var dragSelector = this.dragSelector,
111             selModel     = dragSelector.dataview.getSelectionModel(),
112             dragRegion   = dragSelector.dragRegion,
113             bodyRegion   = dragSelector.bodyRegion,
114             proxy        = dragSelector.getProxy(),
115             regions      = dragSelector.regions,
116             length       = regions.length,
117
118             startXY   = this.startXY,
119             currentXY = this.getXY(),
120             minX      = Math.min(startXY[0], currentXY[0]),
121             minY      = Math.min(startXY[1], currentXY[1]),
122             width     = Math.abs(startXY[0] - currentXY[0]),
123             height    = Math.abs(startXY[1] - currentXY[1]),
124             region, selected, i;
125
126         Ext.apply(dragRegion, {
127             top: minY,
128             left: minX,
129             right: minX + width,
130             bottom: minY + height
131         });
132
133         dragRegion.constrainTo(bodyRegion);
134         proxy.setRegion(dragRegion);
135
136         for (i = 0; i < length; i++) {
137             region = regions[i];
138             selected = dragRegion.intersect(region);
139
140             if (selected) {
141                 selModel.select(i, true);
142             } else {
143                 selModel.deselect(i);
144             }
145         }
146     },
147
148     /**
149      * @private
150      * Listener attached to the DragTracker's onEnd event. This is a delayed function which executes 1
151      * millisecond after it has been called. This is because the dragging flag must remain active to cancel
152      * the containerclick event which the mouseup event will trigger.
153      * @param {EventObject} e The event object
154      */
155     onEnd: Ext.Function.createDelayed(function(e) {
156         var dataview = this.dataview,
157             selModel = dataview.getSelectionModel(),
158             dragSelector = this.dragSelector;
159
160         this.dragging = false;
161         dragSelector.getProxy().hide();
162     }, 1),
163
164     /**
165      * @private
166      * Creates a Proxy element that will be used to highlight the drag selection region
167      * @return {Ext.Element} The Proxy element
168      */
169     getProxy: function() {
170         if (!this.proxy) {
171             this.proxy = this.dataview.getEl().createChild({
172                 tag: 'div',
173                 cls: 'x-view-selector'
174             });
175         }
176         return this.proxy;
177     },
178
179     /**
180      * @private
181      * Gets the region taken up by each rendered node in the DataView. We use these regions to figure out which nodes
182      * to select based on the selector region the user has dragged out
183      */
184     fillRegions: function() {
185         var dataview = this.dataview,
186             regions  = this.regions = [];
187
188         dataview.all.each(function(node) {
189             regions.push(node.getRegion());
190         });
191         this.bodyRegion = dataview.getEl().getRegion();
192     }
193 });