Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / dd / DragSource.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.dd.DragSource
17  * @extends Ext.dd.DDProxy
18  * A simple class that provides the basic implementation needed to make any element draggable.
19  */
20 Ext.define('Ext.dd.DragSource', {
21     extend: 'Ext.dd.DDProxy',
22     requires: [
23         'Ext.dd.StatusProxy',
24         'Ext.dd.DragDropManager'
25     ],
26
27     /**
28      * @cfg {String} ddGroup
29      * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
30      * interact with other drag drop objects in the same group (defaults to undefined).
31      */
32
33     /**
34      * @cfg {String} dropAllowed
35      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
36      */
37
38     dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok',
39     /**
40      * @cfg {String} dropNotAllowed
41      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
42      */
43     dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop',
44
45     /**
46      * @cfg {Boolean} animRepair
47      * Defaults to true. If true, animates the proxy element back to the position of the handle element used to trigger the drag.
48      */
49     animRepair: true,
50
51     /**
52      * @cfg {String} repairHighlightColor The color to use when visually highlighting the drag source in the afterRepair
53      * method after a failed drop (defaults to 'c3daf9' - light blue). The color must be a 6 digit hex value, without
54      * a preceding '#'.
55      */
56     repairHighlightColor: 'c3daf9',
57
58     /**
59      * Creates new drag-source.
60      * @constructor
61      * @param {Mixed} el The container element
62      * @param {Object} config (optional) Config object.
63      */
64     constructor: function(el, config) {
65         this.el = Ext.get(el);
66         if(!this.dragData){
67             this.dragData = {};
68         }
69
70         Ext.apply(this, config);
71
72         if(!this.proxy){
73             this.proxy = Ext.create('Ext.dd.StatusProxy', {
74                 animRepair: this.animRepair
75             });
76         }
77         this.callParent([this.el.dom, this.ddGroup || this.group,
78               {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true}]);
79
80         this.dragging = false;
81     },
82
83     /**
84      * Returns the data object associated with this drag source
85      * @return {Object} data An object containing arbitrary data
86      */
87     getDragData : function(e){
88         return this.dragData;
89     },
90
91     // private
92     onDragEnter : function(e, id){
93         var target = Ext.dd.DragDropManager.getDDById(id);
94         this.cachedTarget = target;
95         if (this.beforeDragEnter(target, e, id) !== false) {
96             if (target.isNotifyTarget) {
97                 var status = target.notifyEnter(this, e, this.dragData);
98                 this.proxy.setStatus(status);
99             } else {
100                 this.proxy.setStatus(this.dropAllowed);
101             }
102
103             if (this.afterDragEnter) {
104                 /**
105                  * An empty function by default, but provided so that you can perform a custom action
106                  * when the dragged item enters the drop target by providing an implementation.
107                  * @param {Ext.dd.DragDrop} target The drop target
108                  * @param {Event} e The event object
109                  * @param {String} id The id of the dragged element
110                  * @method afterDragEnter
111                  */
112                 this.afterDragEnter(target, e, id);
113             }
114         }
115     },
116
117     /**
118      * An empty function by default, but provided so that you can perform a custom action
119      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
120      * @param {Ext.dd.DragDrop} target The drop target
121      * @param {Event} e The event object
122      * @param {String} id The id of the dragged element
123      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
124      */
125     beforeDragEnter: function(target, e, id) {
126         return true;
127     },
128
129     // private
130     alignElWithMouse: function() {
131         this.callParent(arguments);
132         this.proxy.sync();
133     },
134
135     // private
136     onDragOver: function(e, id) {
137         var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id);
138         if (this.beforeDragOver(target, e, id) !== false) {
139             if(target.isNotifyTarget){
140                 var status = target.notifyOver(this, e, this.dragData);
141                 this.proxy.setStatus(status);
142             }
143
144             if (this.afterDragOver) {
145                 /**
146                  * An empty function by default, but provided so that you can perform a custom action
147                  * while the dragged item is over the drop target by providing an implementation.
148                  * @param {Ext.dd.DragDrop} target The drop target
149                  * @param {Event} e The event object
150                  * @param {String} id The id of the dragged element
151                  * @method afterDragOver
152                  */
153                 this.afterDragOver(target, e, id);
154             }
155         }
156     },
157
158     /**
159      * An empty function by default, but provided so that you can perform a custom action
160      * while the dragged item is over the drop target and optionally cancel the onDragOver.
161      * @param {Ext.dd.DragDrop} target The drop target
162      * @param {Event} e The event object
163      * @param {String} id The id of the dragged element
164      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
165      */
166     beforeDragOver: function(target, e, id) {
167         return true;
168     },
169
170     // private
171     onDragOut: function(e, id) {
172         var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id);
173         if (this.beforeDragOut(target, e, id) !== false) {
174             if (target.isNotifyTarget) {
175                 target.notifyOut(this, e, this.dragData);
176             }
177             this.proxy.reset();
178             if (this.afterDragOut) {
179                 /**
180                  * An empty function by default, but provided so that you can perform a custom action
181                  * after the dragged item is dragged out of the target without dropping.
182                  * @param {Ext.dd.DragDrop} target The drop target
183                  * @param {Event} e The event object
184                  * @param {String} id The id of the dragged element
185                  * @method afterDragOut
186                  */
187                 this.afterDragOut(target, e, id);
188             }
189         }
190         this.cachedTarget = null;
191     },
192
193     /**
194      * An empty function by default, but provided so that you can perform a custom action before the dragged
195      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
196      * @param {Ext.dd.DragDrop} target The drop target
197      * @param {Event} e The event object
198      * @param {String} id The id of the dragged element
199      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
200      */
201     beforeDragOut: function(target, e, id){
202         return true;
203     },
204
205     // private
206     onDragDrop: function(e, id){
207         var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id);
208         if (this.beforeDragDrop(target, e, id) !== false) {
209             if (target.isNotifyTarget) {
210                 if (target.notifyDrop(this, e, this.dragData) !== false) { // valid drop?
211                     this.onValidDrop(target, e, id);
212                 } else {
213                     this.onInvalidDrop(target, e, id);
214                 }
215             } else {
216                 this.onValidDrop(target, e, id);
217             }
218
219             if (this.afterDragDrop) {
220                 /**
221                  * An empty function by default, but provided so that you can perform a custom action
222                  * after a valid drag drop has occurred by providing an implementation.
223                  * @param {Ext.dd.DragDrop} target The drop target
224                  * @param {Event} e The event object
225                  * @param {String} id The id of the dropped element
226                  * @method afterDragDrop
227                  */
228                 this.afterDragDrop(target, e, id);
229             }
230         }
231         delete this.cachedTarget;
232     },
233
234     /**
235      * An empty function by default, but provided so that you can perform a custom action before the dragged
236      * item is dropped onto the target and optionally cancel the onDragDrop.
237      * @param {Ext.dd.DragDrop} target The drop target
238      * @param {Event} e The event object
239      * @param {String} id The id of the dragged element
240      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
241      */
242     beforeDragDrop: function(target, e, id){
243         return true;
244     },
245
246     // private
247     onValidDrop: function(target, e, id){
248         this.hideProxy();
249         if(this.afterValidDrop){
250             /**
251              * An empty function by default, but provided so that you can perform a custom action
252              * after a valid drop has occurred by providing an implementation.
253              * @param {Object} target The target DD
254              * @param {Event} e The event object
255              * @param {String} id The id of the dropped element
256              * @method afterInvalidDrop
257              */
258             this.afterValidDrop(target, e, id);
259         }
260     },
261
262     // private
263     getRepairXY: function(e, data){
264         return this.el.getXY();
265     },
266
267     // private
268     onInvalidDrop: function(target, e, id) {
269         this.beforeInvalidDrop(target, e, id);
270         if (this.cachedTarget) {
271             if(this.cachedTarget.isNotifyTarget){
272                 this.cachedTarget.notifyOut(this, e, this.dragData);
273             }
274             this.cacheTarget = null;
275         }
276         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
277
278         if (this.afterInvalidDrop) {
279             /**
280              * An empty function by default, but provided so that you can perform a custom action
281              * after an invalid drop has occurred by providing an implementation.
282              * @param {Event} e The event object
283              * @param {String} id The id of the dropped element
284              * @method afterInvalidDrop
285              */
286             this.afterInvalidDrop(e, id);
287         }
288     },
289
290     // private
291     afterRepair: function() {
292         var me = this;
293         if (Ext.enableFx) {
294             me.el.highlight(me.repairHighlightColor);
295         }
296         me.dragging = false;
297     },
298
299     /**
300      * An empty function by default, but provided so that you can perform a custom action after an invalid
301      * drop has occurred.
302      * @param {Ext.dd.DragDrop} target The drop target
303      * @param {Event} e The event object
304      * @param {String} id The id of the dragged element
305      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
306      */
307     beforeInvalidDrop: function(target, e, id) {
308         return true;
309     },
310
311     // private
312     handleMouseDown: function(e) {
313         if (this.dragging) {
314             return;
315         }
316         var data = this.getDragData(e);
317         if (data && this.onBeforeDrag(data, e) !== false) {
318             this.dragData = data;
319             this.proxy.stop();
320             this.callParent(arguments);
321         }
322     },
323
324     /**
325      * An empty function by default, but provided so that you can perform a custom action before the initial
326      * drag event begins and optionally cancel it.
327      * @param {Object} data An object containing arbitrary data to be shared with drop targets
328      * @param {Event} e The event object
329      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
330      */
331     onBeforeDrag: function(data, e){
332         return true;
333     },
334
335     /**
336      * An empty function by default, but provided so that you can perform a custom action once the initial
337      * drag event has begun.  The drag cannot be canceled from this function.
338      * @param {Number} x The x position of the click on the dragged object
339      * @param {Number} y The y position of the click on the dragged object
340      * @method
341      */
342     onStartDrag: Ext.emptyFn,
343
344     // private override
345     startDrag: function(x, y) {
346         this.proxy.reset();
347         this.dragging = true;
348         this.proxy.update("");
349         this.onInitDrag(x, y);
350         this.proxy.show();
351     },
352
353     // private
354     onInitDrag: function(x, y) {
355         var clone = this.el.dom.cloneNode(true);
356         clone.id = Ext.id(); // prevent duplicate ids
357         this.proxy.update(clone);
358         this.onStartDrag(x, y);
359         return true;
360     },
361
362     /**
363      * Returns the drag source's underlying {@link Ext.dd.StatusProxy}
364      * @return {Ext.dd.StatusProxy} proxy The StatusProxy
365      */
366     getProxy: function() {
367         return this.proxy;
368     },
369
370     /**
371      * Hides the drag source's {@link Ext.dd.StatusProxy}
372      */
373     hideProxy: function() {
374         this.proxy.hide();
375         this.proxy.reset(true);
376         this.dragging = false;
377     },
378
379     // private
380     triggerCacheRefresh: function() {
381         Ext.dd.DDM.refreshCache(this.groups);
382     },
383
384     // private - override to prevent hiding
385     b4EndDrag: function(e) {
386     },
387
388     // private - override to prevent moving
389     endDrag : function(e){
390         this.onEndDrag(this.dragData, e);
391     },
392
393     // private
394     onEndDrag : function(data, e){
395     },
396
397     // private - pin to cursor
398     autoOffset : function(x, y) {
399         this.setDelta(-12, -20);
400     },
401
402     destroy: function(){
403         this.callParent();
404         Ext.destroy(this.proxy);
405     }
406 });
407