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