initial import
[roojs1] / Roo / dd / DragDropMgr.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * The drag and drop utility provides a framework for building drag and drop
15  * applications.  In addition to enabling drag and drop for specific elements,
16  * the drag and drop elements are tracked by the manager class, and the
17  * interactions between the various elements are tracked during the drag and
18  * the implementing code is notified about these important moments.
19  */
20
21 // Only load the library once.  Rewriting the manager class would orphan
22 // existing drag and drop instances.
23 if (!Roo.dd.DragDropMgr) {
24
25 /**
26  * @class Roo.dd.DragDropMgr
27  * DragDropMgr is a singleton that tracks the element interaction for
28  * all DragDrop items in the window.  Generally, you will not call
29  * this class directly, but it does have helper methods that could
30  * be useful in your DragDrop implementations.
31  * @singleton
32  */
33 Roo.dd.DragDropMgr = function() {
34
35     var Event = Roo.EventManager;
36
37     return {
38
39         /**
40          * Two dimensional Array of registered DragDrop objects.  The first
41          * dimension is the DragDrop item group, the second the DragDrop
42          * object.
43          * @property ids
44          * @type {string: string}
45          * @private
46          * @static
47          */
48         ids: {},
49
50         /**
51          * Array of element ids defined as drag handles.  Used to determine
52          * if the element that generated the mousedown event is actually the
53          * handle and not the html element itself.
54          * @property handleIds
55          * @type {string: string}
56          * @private
57          * @static
58          */
59         handleIds: {},
60
61         /**
62          * the DragDrop object that is currently being dragged
63          * @property dragCurrent
64          * @type DragDrop
65          * @private
66          * @static
67          **/
68         dragCurrent: null,
69
70         /**
71          * the DragDrop object(s) that are being hovered over
72          * @property dragOvers
73          * @type Array
74          * @private
75          * @static
76          */
77         dragOvers: {},
78
79         /**
80          * the X distance between the cursor and the object being dragged
81          * @property deltaX
82          * @type int
83          * @private
84          * @static
85          */
86         deltaX: 0,
87
88         /**
89          * the Y distance between the cursor and the object being dragged
90          * @property deltaY
91          * @type int
92          * @private
93          * @static
94          */
95         deltaY: 0,
96
97         /**
98          * Flag to determine if we should prevent the default behavior of the
99          * events we define. By default this is true, but this can be set to
100          * false if you need the default behavior (not recommended)
101          * @property preventDefault
102          * @type boolean
103          * @static
104          */
105         preventDefault: true,
106
107         /**
108          * Flag to determine if we should stop the propagation of the events
109          * we generate. This is true by default but you may want to set it to
110          * false if the html element contains other features that require the
111          * mouse click.
112          * @property stopPropagation
113          * @type boolean
114          * @static
115          */
116         stopPropagation: true,
117
118         /**
119          * Internal flag that is set to true when drag and drop has been
120          * intialized
121          * @property initialized
122          * @private
123          * @static
124          */
125         initalized: false,
126
127         /**
128          * All drag and drop can be disabled.
129          * @property locked
130          * @private
131          * @static
132          */
133         locked: false,
134
135         /**
136          * Called the first time an element is registered.
137          * @method init
138          * @private
139          * @static
140          */
141         init: function() {
142             this.initialized = true;
143         },
144
145         /**
146          * In point mode, drag and drop interaction is defined by the
147          * location of the cursor during the drag/drop
148          * @property POINT
149          * @type int
150          * @static
151          */
152         POINT: 0,
153
154         /**
155          * In intersect mode, drag and drop interactio nis defined by the
156          * overlap of two or more drag and drop objects.
157          * @property INTERSECT
158          * @type int
159          * @static
160          */
161         INTERSECT: 1,
162
163         /**
164          * The current drag and drop mode.  Default: POINT
165          * @property mode
166          * @type int
167          * @static
168          */
169         mode: 0,
170
171         /**
172          * Runs method on all drag and drop objects
173          * @method _execOnAll
174          * @private
175          * @static
176          */
177         _execOnAll: function(sMethod, args) {
178             for (var i in this.ids) {
179                 for (var j in this.ids[i]) {
180                     var oDD = this.ids[i][j];
181                     if (! this.isTypeOfDD(oDD)) {
182                         continue;
183                     }
184                     oDD[sMethod].apply(oDD, args);
185                 }
186             }
187         },
188
189         /**
190          * Drag and drop initialization.  Sets up the global event handlers
191          * @method _onLoad
192          * @private
193          * @static
194          */
195         _onLoad: function() {
196
197             this.init();
198
199
200             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
201             Event.on(document, "mousemove", this.handleMouseMove, this, true);
202             Event.on(window,   "unload",    this._onUnload, this, true);
203             Event.on(window,   "resize",    this._onResize, this, true);
204             // Event.on(window,   "mouseout",    this._test);
205
206         },
207
208         /**
209          * Reset constraints on all drag and drop objs
210          * @method _onResize
211          * @private
212          * @static
213          */
214         _onResize: function(e) {
215             this._execOnAll("resetConstraints", []);
216         },
217
218         /**
219          * Lock all drag and drop functionality
220          * @method lock
221          * @static
222          */
223         lock: function() { this.locked = true; },
224
225         /**
226          * Unlock all drag and drop functionality
227          * @method unlock
228          * @static
229          */
230         unlock: function() { this.locked = false; },
231
232         /**
233          * Is drag and drop locked?
234          * @method isLocked
235          * @return {boolean} True if drag and drop is locked, false otherwise.
236          * @static
237          */
238         isLocked: function() { return this.locked; },
239
240         /**
241          * Location cache that is set for all drag drop objects when a drag is
242          * initiated, cleared when the drag is finished.
243          * @property locationCache
244          * @private
245          * @static
246          */
247         locationCache: {},
248
249         /**
250          * Set useCache to false if you want to force object the lookup of each
251          * drag and drop linked element constantly during a drag.
252          * @property useCache
253          * @type boolean
254          * @static
255          */
256         useCache: true,
257
258         /**
259          * The number of pixels that the mouse needs to move after the
260          * mousedown before the drag is initiated.  Default=3;
261          * @property clickPixelThresh
262          * @type int
263          * @static
264          */
265         clickPixelThresh: 3,
266
267         /**
268          * The number of milliseconds after the mousedown event to initiate the
269          * drag if we don't get a mouseup event. Default=1000
270          * @property clickTimeThresh
271          * @type int
272          * @static
273          */
274         clickTimeThresh: 350,
275
276         /**
277          * Flag that indicates that either the drag pixel threshold or the
278          * mousdown time threshold has been met
279          * @property dragThreshMet
280          * @type boolean
281          * @private
282          * @static
283          */
284         dragThreshMet: false,
285
286         /**
287          * Timeout used for the click time threshold
288          * @property clickTimeout
289          * @type Object
290          * @private
291          * @static
292          */
293         clickTimeout: null,
294
295         /**
296          * The X position of the mousedown event stored for later use when a
297          * drag threshold is met.
298          * @property startX
299          * @type int
300          * @private
301          * @static
302          */
303         startX: 0,
304
305         /**
306          * The Y position of the mousedown event stored for later use when a
307          * drag threshold is met.
308          * @property startY
309          * @type int
310          * @private
311          * @static
312          */
313         startY: 0,
314
315         /**
316          * Each DragDrop instance must be registered with the DragDropMgr.
317          * This is executed in DragDrop.init()
318          * @method regDragDrop
319          * @param {DragDrop} oDD the DragDrop object to register
320          * @param {String} sGroup the name of the group this element belongs to
321          * @static
322          */
323         regDragDrop: function(oDD, sGroup) {
324             if (!this.initialized) { this.init(); }
325
326             if (!this.ids[sGroup]) {
327                 this.ids[sGroup] = {};
328             }
329             this.ids[sGroup][oDD.id] = oDD;
330         },
331
332         /**
333          * Removes the supplied dd instance from the supplied group. Executed
334          * by DragDrop.removeFromGroup, so don't call this function directly.
335          * @method removeDDFromGroup
336          * @private
337          * @static
338          */
339         removeDDFromGroup: function(oDD, sGroup) {
340             if (!this.ids[sGroup]) {
341                 this.ids[sGroup] = {};
342             }
343
344             var obj = this.ids[sGroup];
345             if (obj && obj[oDD.id]) {
346                 delete obj[oDD.id];
347             }
348         },
349
350         /**
351          * Unregisters a drag and drop item.  This is executed in
352          * DragDrop.unreg, use that method instead of calling this directly.
353          * @method _remove
354          * @private
355          * @static
356          */
357         _remove: function(oDD) {
358             for (var g in oDD.groups) {
359                 if (g && this.ids[g][oDD.id]) {
360                     delete this.ids[g][oDD.id];
361                 }
362             }
363             delete this.handleIds[oDD.id];
364         },
365
366         /**
367          * Each DragDrop handle element must be registered.  This is done
368          * automatically when executing DragDrop.setHandleElId()
369          * @method regHandle
370          * @param {String} sDDId the DragDrop id this element is a handle for
371          * @param {String} sHandleId the id of the element that is the drag
372          * handle
373          * @static
374          */
375         regHandle: function(sDDId, sHandleId) {
376             if (!this.handleIds[sDDId]) {
377                 this.handleIds[sDDId] = {};
378             }
379             this.handleIds[sDDId][sHandleId] = sHandleId;
380         },
381
382         /**
383          * Utility function to determine if a given element has been
384          * registered as a drag drop item.
385          * @method isDragDrop
386          * @param {String} id the element id to check
387          * @return {boolean} true if this element is a DragDrop item,
388          * false otherwise
389          * @static
390          */
391         isDragDrop: function(id) {
392             return ( this.getDDById(id) ) ? true : false;
393         },
394
395         /**
396          * Returns the drag and drop instances that are in all groups the
397          * passed in instance belongs to.
398          * @method getRelated
399          * @param {DragDrop} p_oDD the obj to get related data for
400          * @param {boolean} bTargetsOnly if true, only return targetable objs
401          * @return {DragDrop[]} the related instances
402          * @static
403          */
404         getRelated: function(p_oDD, bTargetsOnly) {
405             var oDDs = [];
406             for (var i in p_oDD.groups) {
407                 for (j in this.ids[i]) {
408                     var dd = this.ids[i][j];
409                     if (! this.isTypeOfDD(dd)) {
410                         continue;
411                     }
412                     if (!bTargetsOnly || dd.isTarget) {
413                         oDDs[oDDs.length] = dd;
414                     }
415                 }
416             }
417
418             return oDDs;
419         },
420
421         /**
422          * Returns true if the specified dd target is a legal target for
423          * the specifice drag obj
424          * @method isLegalTarget
425          * @param {DragDrop} the drag obj
426          * @param {DragDrop} the target
427          * @return {boolean} true if the target is a legal target for the
428          * dd obj
429          * @static
430          */
431         isLegalTarget: function (oDD, oTargetDD) {
432             var targets = this.getRelated(oDD, true);
433             for (var i=0, len=targets.length;i<len;++i) {
434                 if (targets[i].id == oTargetDD.id) {
435                     return true;
436                 }
437             }
438
439             return false;
440         },
441
442         /**
443          * My goal is to be able to transparently determine if an object is
444          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
445          * returns "object", oDD.constructor.toString() always returns
446          * "DragDrop" and not the name of the subclass.  So for now it just
447          * evaluates a well-known variable in DragDrop.
448          * @method isTypeOfDD
449          * @param {Object} the object to evaluate
450          * @return {boolean} true if typeof oDD = DragDrop
451          * @static
452          */
453         isTypeOfDD: function (oDD) {
454             return (oDD && oDD.__ygDragDrop);
455         },
456
457         /**
458          * Utility function to determine if a given element has been
459          * registered as a drag drop handle for the given Drag Drop object.
460          * @method isHandle
461          * @param {String} id the element id to check
462          * @return {boolean} true if this element is a DragDrop handle, false
463          * otherwise
464          * @static
465          */
466         isHandle: function(sDDId, sHandleId) {
467             return ( this.handleIds[sDDId] &&
468                             this.handleIds[sDDId][sHandleId] );
469         },
470
471         /**
472          * Returns the DragDrop instance for a given id
473          * @method getDDById
474          * @param {String} id the id of the DragDrop object
475          * @return {DragDrop} the drag drop object, null if it is not found
476          * @static
477          */
478         getDDById: function(id) {
479             for (var i in this.ids) {
480                 if (this.ids[i][id]) {
481                     return this.ids[i][id];
482                 }
483             }
484             return null;
485         },
486
487         /**
488          * Fired after a registered DragDrop object gets the mousedown event.
489          * Sets up the events required to track the object being dragged
490          * @method handleMouseDown
491          * @param {Event} e the event
492          * @param oDD the DragDrop object being dragged
493          * @private
494          * @static
495          */
496         handleMouseDown: function(e, oDD) {
497             if(Roo.QuickTips){
498                 Roo.QuickTips.disable();
499             }
500             this.currentTarget = e.getTarget();
501
502             this.dragCurrent = oDD;
503
504             var el = oDD.getEl();
505
506             // track start position
507             this.startX = e.getPageX();
508             this.startY = e.getPageY();
509
510             this.deltaX = this.startX - el.offsetLeft;
511             this.deltaY = this.startY - el.offsetTop;
512
513             this.dragThreshMet = false;
514
515             this.clickTimeout = setTimeout(
516                     function() {
517                         var DDM = Roo.dd.DDM;
518                         DDM.startDrag(DDM.startX, DDM.startY);
519                     },
520                     this.clickTimeThresh );
521         },
522
523         /**
524          * Fired when either the drag pixel threshol or the mousedown hold
525          * time threshold has been met.
526          * @method startDrag
527          * @param x {int} the X position of the original mousedown
528          * @param y {int} the Y position of the original mousedown
529          * @static
530          */
531         startDrag: function(x, y) {
532             clearTimeout(this.clickTimeout);
533             if (this.dragCurrent) {
534                 this.dragCurrent.b4StartDrag(x, y);
535                 this.dragCurrent.startDrag(x, y);
536             }
537             this.dragThreshMet = true;
538         },
539
540         /**
541          * Internal function to handle the mouseup event.  Will be invoked
542          * from the context of the document.
543          * @method handleMouseUp
544          * @param {Event} e the event
545          * @private
546          * @static
547          */
548         handleMouseUp: function(e) {
549
550             if(Roo.QuickTips){
551                 Roo.QuickTips.enable();
552             }
553             if (! this.dragCurrent) {
554                 return;
555             }
556
557             clearTimeout(this.clickTimeout);
558
559             if (this.dragThreshMet) {
560                 this.fireEvents(e, true);
561             } else {
562             }
563
564             this.stopDrag(e);
565
566             this.stopEvent(e);
567         },
568
569         /**
570          * Utility to stop event propagation and event default, if these
571          * features are turned on.
572          * @method stopEvent
573          * @param {Event} e the event as returned by this.getEvent()
574          * @static
575          */
576         stopEvent: function(e){
577             if(this.stopPropagation) {
578                 e.stopPropagation();
579             }
580
581             if (this.preventDefault) {
582                 e.preventDefault();
583             }
584         },
585
586         /**
587          * Internal function to clean up event handlers after the drag
588          * operation is complete
589          * @method stopDrag
590          * @param {Event} e the event
591          * @private
592          * @static
593          */
594         stopDrag: function(e) {
595             // Fire the drag end event for the item that was dragged
596             if (this.dragCurrent) {
597                 if (this.dragThreshMet) {
598                     this.dragCurrent.b4EndDrag(e);
599                     this.dragCurrent.endDrag(e);
600                 }
601
602                 this.dragCurrent.onMouseUp(e);
603             }
604
605             this.dragCurrent = null;
606             this.dragOvers = {};
607         },
608
609         /**
610          * Internal function to handle the mousemove event.  Will be invoked
611          * from the context of the html element.
612          *
613          * @TODO figure out what we can do about mouse events lost when the
614          * user drags objects beyond the window boundary.  Currently we can
615          * detect this in internet explorer by verifying that the mouse is
616          * down during the mousemove event.  Firefox doesn't give us the
617          * button state on the mousemove event.
618          * @method handleMouseMove
619          * @param {Event} e the event
620          * @private
621          * @static
622          */
623         handleMouseMove: function(e) {
624             if (! this.dragCurrent) {
625                 return true;
626             }
627
628             // var button = e.which || e.button;
629
630             // check for IE mouseup outside of page boundary
631             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
632                 this.stopEvent(e);
633                 return this.handleMouseUp(e);
634             }
635
636             if (!this.dragThreshMet) {
637                 var diffX = Math.abs(this.startX - e.getPageX());
638                 var diffY = Math.abs(this.startY - e.getPageY());
639                 if (diffX > this.clickPixelThresh ||
640                             diffY > this.clickPixelThresh) {
641                     this.startDrag(this.startX, this.startY);
642                 }
643             }
644
645             if (this.dragThreshMet) {
646                 this.dragCurrent.b4Drag(e);
647                 this.dragCurrent.onDrag(e);
648                 if(!this.dragCurrent.moveOnly){
649                     this.fireEvents(e, false);
650                 }
651             }
652
653             this.stopEvent(e);
654
655             return true;
656         },
657
658         /**
659          * Iterates over all of the DragDrop elements to find ones we are
660          * hovering over or dropping on
661          * @method fireEvents
662          * @param {Event} e the event
663          * @param {boolean} isDrop is this a drop op or a mouseover op?
664          * @private
665          * @static
666          */
667         fireEvents: function(e, isDrop) {
668             var dc = this.dragCurrent;
669
670             // If the user did the mouse up outside of the window, we could
671             // get here even though we have ended the drag.
672             if (!dc || dc.isLocked()) {
673                 return;
674             }
675
676             var pt = e.getPoint();
677
678             // cache the previous dragOver array
679             var oldOvers = [];
680
681             var outEvts   = [];
682             var overEvts  = [];
683             var dropEvts  = [];
684             var enterEvts = [];
685
686             // Check to see if the object(s) we were hovering over is no longer
687             // being hovered over so we can fire the onDragOut event
688             for (var i in this.dragOvers) {
689
690                 var ddo = this.dragOvers[i];
691
692                 if (! this.isTypeOfDD(ddo)) {
693                     continue;
694                 }
695
696                 if (! this.isOverTarget(pt, ddo, this.mode)) {
697                     outEvts.push( ddo );
698                 }
699
700                 oldOvers[i] = true;
701                 delete this.dragOvers[i];
702             }
703
704             for (var sGroup in dc.groups) {
705
706                 if ("string" != typeof sGroup) {
707                     continue;
708                 }
709
710                 for (i in this.ids[sGroup]) {
711                     var oDD = this.ids[sGroup][i];
712                     if (! this.isTypeOfDD(oDD)) {
713                         continue;
714                     }
715
716                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
717                         if (this.isOverTarget(pt, oDD, this.mode)) {
718                             // look for drop interactions
719                             if (isDrop) {
720                                 dropEvts.push( oDD );
721                             // look for drag enter and drag over interactions
722                             } else {
723
724                                 // initial drag over: dragEnter fires
725                                 if (!oldOvers[oDD.id]) {
726                                     enterEvts.push( oDD );
727                                 // subsequent drag overs: dragOver fires
728                                 } else {
729                                     overEvts.push( oDD );
730                                 }
731
732                                 this.dragOvers[oDD.id] = oDD;
733                             }
734                         }
735                     }
736                 }
737             }
738
739             if (this.mode) {
740                 if (outEvts.length) {
741                     dc.b4DragOut(e, outEvts);
742                     dc.onDragOut(e, outEvts);
743                 }
744
745                 if (enterEvts.length) {
746                     dc.onDragEnter(e, enterEvts);
747                 }
748
749                 if (overEvts.length) {
750                     dc.b4DragOver(e, overEvts);
751                     dc.onDragOver(e, overEvts);
752                 }
753
754                 if (dropEvts.length) {
755                     dc.b4DragDrop(e, dropEvts);
756                     dc.onDragDrop(e, dropEvts);
757                 }
758
759             } else {
760                 // fire dragout events
761                 var len = 0;
762                 for (i=0, len=outEvts.length; i<len; ++i) {
763                     dc.b4DragOut(e, outEvts[i].id);
764                     dc.onDragOut(e, outEvts[i].id);
765                 }
766
767                 // fire enter events
768                 for (i=0,len=enterEvts.length; i<len; ++i) {
769                     // dc.b4DragEnter(e, oDD.id);
770                     dc.onDragEnter(e, enterEvts[i].id);
771                 }
772
773                 // fire over events
774                 for (i=0,len=overEvts.length; i<len; ++i) {
775                     dc.b4DragOver(e, overEvts[i].id);
776                     dc.onDragOver(e, overEvts[i].id);
777                 }
778
779                 // fire drop events
780                 for (i=0, len=dropEvts.length; i<len; ++i) {
781                     dc.b4DragDrop(e, dropEvts[i].id);
782                     dc.onDragDrop(e, dropEvts[i].id);
783                 }
784
785             }
786
787             // notify about a drop that did not find a target
788             if (isDrop && !dropEvts.length) {
789                 dc.onInvalidDrop(e);
790             }
791
792         },
793
794         /**
795          * Helper function for getting the best match from the list of drag
796          * and drop objects returned by the drag and drop events when we are
797          * in INTERSECT mode.  It returns either the first object that the
798          * cursor is over, or the object that has the greatest overlap with
799          * the dragged element.
800          * @method getBestMatch
801          * @param  {DragDrop[]} dds The array of drag and drop objects
802          * targeted
803          * @return {DragDrop}       The best single match
804          * @static
805          */
806         getBestMatch: function(dds) {
807             var winner = null;
808             // Return null if the input is not what we expect
809             //if (!dds || !dds.length || dds.length == 0) {
810                // winner = null;
811             // If there is only one item, it wins
812             //} else if (dds.length == 1) {
813
814             var len = dds.length;
815
816             if (len == 1) {
817                 winner = dds[0];
818             } else {
819                 // Loop through the targeted items
820                 for (var i=0; i<len; ++i) {
821                     var dd = dds[i];
822                     // If the cursor is over the object, it wins.  If the
823                     // cursor is over multiple matches, the first one we come
824                     // to wins.
825                     if (dd.cursorIsOver) {
826                         winner = dd;
827                         break;
828                     // Otherwise the object with the most overlap wins
829                     } else {
830                         if (!winner ||
831                             winner.overlap.getArea() < dd.overlap.getArea()) {
832                             winner = dd;
833                         }
834                     }
835                 }
836             }
837
838             return winner;
839         },
840
841         /**
842          * Refreshes the cache of the top-left and bottom-right points of the
843          * drag and drop objects in the specified group(s).  This is in the
844          * format that is stored in the drag and drop instance, so typical
845          * usage is:
846          * <code>
847          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
848          * </code>
849          * Alternatively:
850          * <code>
851          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
852          * </code>
853          * @TODO this really should be an indexed array.  Alternatively this
854          * method could accept both.
855          * @method refreshCache
856          * @param {Object} groups an associative array of groups to refresh
857          * @static
858          */
859         refreshCache: function(groups) {
860             for (var sGroup in groups) {
861                 if ("string" != typeof sGroup) {
862                     continue;
863                 }
864                 for (var i in this.ids[sGroup]) {
865                     var oDD = this.ids[sGroup][i];
866
867                     if (this.isTypeOfDD(oDD)) {
868                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
869                         var loc = this.getLocation(oDD);
870                         if (loc) {
871                             this.locationCache[oDD.id] = loc;
872                         } else {
873                             delete this.locationCache[oDD.id];
874                             // this will unregister the drag and drop object if
875                             // the element is not in a usable state
876                             // oDD.unreg();
877                         }
878                     }
879                 }
880             }
881         },
882
883         /**
884          * This checks to make sure an element exists and is in the DOM.  The
885          * main purpose is to handle cases where innerHTML is used to remove
886          * drag and drop objects from the DOM.  IE provides an 'unspecified
887          * error' when trying to access the offsetParent of such an element
888          * @method verifyEl
889          * @param {HTMLElement} el the element to check
890          * @return {boolean} true if the element looks usable
891          * @static
892          */
893         verifyEl: function(el) {
894             if (el) {
895                 var parent;
896                 if(Roo.isIE){
897                     try{
898                         parent = el.offsetParent;
899                     }catch(e){}
900                 }else{
901                     parent = el.offsetParent;
902                 }
903                 if (parent) {
904                     return true;
905                 }
906             }
907
908             return false;
909         },
910
911         /**
912          * Returns a Region object containing the drag and drop element's position
913          * and size, including the padding configured for it
914          * @method getLocation
915          * @param {DragDrop} oDD the drag and drop object to get the
916          *                       location for
917          * @return {Roo.lib.Region} a Region object representing the total area
918          *                             the element occupies, including any padding
919          *                             the instance is configured for.
920          * @static
921          */
922         getLocation: function(oDD) {
923             if (! this.isTypeOfDD(oDD)) {
924                 return null;
925             }
926
927             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
928
929             try {
930                 pos= Roo.lib.Dom.getXY(el);
931             } catch (e) { }
932
933             if (!pos) {
934                 return null;
935             }
936
937             x1 = pos[0];
938             x2 = x1 + el.offsetWidth;
939             y1 = pos[1];
940             y2 = y1 + el.offsetHeight;
941
942             t = y1 - oDD.padding[0];
943             r = x2 + oDD.padding[1];
944             b = y2 + oDD.padding[2];
945             l = x1 - oDD.padding[3];
946
947             return new Roo.lib.Region( t, r, b, l );
948         },
949
950         /**
951          * Checks the cursor location to see if it over the target
952          * @method isOverTarget
953          * @param {Roo.lib.Point} pt The point to evaluate
954          * @param {DragDrop} oTarget the DragDrop object we are inspecting
955          * @return {boolean} true if the mouse is over the target
956          * @private
957          * @static
958          */
959         isOverTarget: function(pt, oTarget, intersect) {
960             // use cache if available
961             var loc = this.locationCache[oTarget.id];
962             if (!loc || !this.useCache) {
963                 loc = this.getLocation(oTarget);
964                 this.locationCache[oTarget.id] = loc;
965
966             }
967
968             if (!loc) {
969                 return false;
970             }
971
972             oTarget.cursorIsOver = loc.contains( pt );
973
974             // DragDrop is using this as a sanity check for the initial mousedown
975             // in this case we are done.  In POINT mode, if the drag obj has no
976             // contraints, we are also done. Otherwise we need to evaluate the
977             // location of the target as related to the actual location of the
978             // dragged element.
979             var dc = this.dragCurrent;
980             if (!dc || !dc.getTargetCoord ||
981                     (!intersect && !dc.constrainX && !dc.constrainY)) {
982                 return oTarget.cursorIsOver;
983             }
984
985             oTarget.overlap = null;
986
987             // Get the current location of the drag element, this is the
988             // location of the mouse event less the delta that represents
989             // where the original mousedown happened on the element.  We
990             // need to consider constraints and ticks as well.
991             var pos = dc.getTargetCoord(pt.x, pt.y);
992
993             var el = dc.getDragEl();
994             var curRegion = new Roo.lib.Region( pos.y,
995                                                    pos.x + el.offsetWidth,
996                                                    pos.y + el.offsetHeight,
997                                                    pos.x );
998
999             var overlap = curRegion.intersect(loc);
1000
1001             if (overlap) {
1002                 oTarget.overlap = overlap;
1003                 return (intersect) ? true : oTarget.cursorIsOver;
1004             } else {
1005                 return false;
1006             }
1007         },
1008
1009         /**
1010          * unload event handler
1011          * @method _onUnload
1012          * @private
1013          * @static
1014          */
1015         _onUnload: function(e, me) {
1016             Roo.dd.DragDropMgr.unregAll();
1017         },
1018
1019         /**
1020          * Cleans up the drag and drop events and objects.
1021          * @method unregAll
1022          * @private
1023          * @static
1024          */
1025         unregAll: function() {
1026
1027             if (this.dragCurrent) {
1028                 this.stopDrag();
1029                 this.dragCurrent = null;
1030             }
1031
1032             this._execOnAll("unreg", []);
1033
1034             for (i in this.elementCache) {
1035                 delete this.elementCache[i];
1036             }
1037
1038             this.elementCache = {};
1039             this.ids = {};
1040         },
1041
1042         /**
1043          * A cache of DOM elements
1044          * @property elementCache
1045          * @private
1046          * @static
1047          */
1048         elementCache: {},
1049
1050         /**
1051          * Get the wrapper for the DOM element specified
1052          * @method getElWrapper
1053          * @param {String} id the id of the element to get
1054          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
1055          * @private
1056          * @deprecated This wrapper isn't that useful
1057          * @static
1058          */
1059         getElWrapper: function(id) {
1060             var oWrapper = this.elementCache[id];
1061             if (!oWrapper || !oWrapper.el) {
1062                 oWrapper = this.elementCache[id] =
1063                     new this.ElementWrapper(Roo.getDom(id));
1064             }
1065             return oWrapper;
1066         },
1067
1068         /**
1069          * Returns the actual DOM element
1070          * @method getElement
1071          * @param {String} id the id of the elment to get
1072          * @return {Object} The element
1073          * @deprecated use Roo.getDom instead
1074          * @static
1075          */
1076         getElement: function(id) {
1077             return Roo.getDom(id);
1078         },
1079
1080         /**
1081          * Returns the style property for the DOM element (i.e.,
1082          * document.getElById(id).style)
1083          * @method getCss
1084          * @param {String} id the id of the elment to get
1085          * @return {Object} The style property of the element
1086          * @deprecated use Roo.getDom instead
1087          * @static
1088          */
1089         getCss: function(id) {
1090             var el = Roo.getDom(id);
1091             return (el) ? el.style : null;
1092         },
1093
1094         /**
1095          * Inner class for cached elements
1096          * @class DragDropMgr.ElementWrapper
1097          * @for DragDropMgr
1098          * @private
1099          * @deprecated
1100          */
1101         ElementWrapper: function(el) {
1102                 /**
1103                  * The element
1104                  * @property el
1105                  */
1106                 this.el = el || null;
1107                 /**
1108                  * The element id
1109                  * @property id
1110                  */
1111                 this.id = this.el && el.id;
1112                 /**
1113                  * A reference to the style property
1114                  * @property css
1115                  */
1116                 this.css = this.el && el.style;
1117             },
1118
1119         /**
1120          * Returns the X position of an html element
1121          * @method getPosX
1122          * @param el the element for which to get the position
1123          * @return {int} the X coordinate
1124          * @for DragDropMgr
1125          * @deprecated use Roo.lib.Dom.getX instead
1126          * @static
1127          */
1128         getPosX: function(el) {
1129             return Roo.lib.Dom.getX(el);
1130         },
1131
1132         /**
1133          * Returns the Y position of an html element
1134          * @method getPosY
1135          * @param el the element for which to get the position
1136          * @return {int} the Y coordinate
1137          * @deprecated use Roo.lib.Dom.getY instead
1138          * @static
1139          */
1140         getPosY: function(el) {
1141             return Roo.lib.Dom.getY(el);
1142         },
1143
1144         /**
1145          * Swap two nodes.  In IE, we use the native method, for others we
1146          * emulate the IE behavior
1147          * @method swapNode
1148          * @param n1 the first node to swap
1149          * @param n2 the other node to swap
1150          * @static
1151          */
1152         swapNode: function(n1, n2) {
1153             if (n1.swapNode) {
1154                 n1.swapNode(n2);
1155             } else {
1156                 var p = n2.parentNode;
1157                 var s = n2.nextSibling;
1158
1159                 if (s == n1) {
1160                     p.insertBefore(n1, n2);
1161                 } else if (n2 == n1.nextSibling) {
1162                     p.insertBefore(n2, n1);
1163                 } else {
1164                     n1.parentNode.replaceChild(n2, n1);
1165                     p.insertBefore(n1, s);
1166                 }
1167             }
1168         },
1169
1170         /**
1171          * Returns the current scroll position
1172          * @method getScroll
1173          * @private
1174          * @static
1175          */
1176         getScroll: function () {
1177             var t, l, dde=document.documentElement, db=document.body;
1178             if (dde && (dde.scrollTop || dde.scrollLeft)) {
1179                 t = dde.scrollTop;
1180                 l = dde.scrollLeft;
1181             } else if (db) {
1182                 t = db.scrollTop;
1183                 l = db.scrollLeft;
1184             } else {
1185
1186             }
1187             return { top: t, left: l };
1188         },
1189
1190         /**
1191          * Returns the specified element style property
1192          * @method getStyle
1193          * @param {HTMLElement} el          the element
1194          * @param {string}      styleProp   the style property
1195          * @return {string} The value of the style property
1196          * @deprecated use Roo.lib.Dom.getStyle
1197          * @static
1198          */
1199         getStyle: function(el, styleProp) {
1200             return Roo.fly(el).getStyle(styleProp);
1201         },
1202
1203         /**
1204          * Gets the scrollTop
1205          * @method getScrollTop
1206          * @return {int} the document's scrollTop
1207          * @static
1208          */
1209         getScrollTop: function () { return this.getScroll().top; },
1210
1211         /**
1212          * Gets the scrollLeft
1213          * @method getScrollLeft
1214          * @return {int} the document's scrollTop
1215          * @static
1216          */
1217         getScrollLeft: function () { return this.getScroll().left; },
1218
1219         /**
1220          * Sets the x/y position of an element to the location of the
1221          * target element.
1222          * @method moveToEl
1223          * @param {HTMLElement} moveEl      The element to move
1224          * @param {HTMLElement} targetEl    The position reference element
1225          * @static
1226          */
1227         moveToEl: function (moveEl, targetEl) {
1228             var aCoord = Roo.lib.Dom.getXY(targetEl);
1229             Roo.lib.Dom.setXY(moveEl, aCoord);
1230         },
1231
1232         /**
1233          * Numeric array sort function
1234          * @method numericSort
1235          * @static
1236          */
1237         numericSort: function(a, b) { return (a - b); },
1238
1239         /**
1240          * Internal counter
1241          * @property _timeoutCount
1242          * @private
1243          * @static
1244          */
1245         _timeoutCount: 0,
1246
1247         /**
1248          * Trying to make the load order less important.  Without this we get
1249          * an error if this file is loaded before the Event Utility.
1250          * @method _addListeners
1251          * @private
1252          * @static
1253          */
1254         _addListeners: function() {
1255             var DDM = Roo.dd.DDM;
1256             if ( Roo.lib.Event && document ) {
1257                 DDM._onLoad();
1258             } else {
1259                 if (DDM._timeoutCount > 2000) {
1260                 } else {
1261                     setTimeout(DDM._addListeners, 10);
1262                     if (document && document.body) {
1263                         DDM._timeoutCount += 1;
1264                     }
1265                 }
1266             }
1267         },
1268
1269         /**
1270          * Recursively searches the immediate parent and all child nodes for
1271          * the handle element in order to determine wheter or not it was
1272          * clicked.
1273          * @method handleWasClicked
1274          * @param node the html element to inspect
1275          * @static
1276          */
1277         handleWasClicked: function(node, id) {
1278             if (this.isHandle(id, node.id)) {
1279                 return true;
1280             } else {
1281                 // check to see if this is a text node child of the one we want
1282                 var p = node.parentNode;
1283
1284                 while (p) {
1285                     if (this.isHandle(id, p.id)) {
1286                         return true;
1287                     } else {
1288                         p = p.parentNode;
1289                     }
1290                 }
1291             }
1292
1293             return false;
1294         }
1295
1296     };
1297
1298 }();
1299
1300 // shorter alias, save a few bytes
1301 Roo.dd.DDM = Roo.dd.DragDropMgr;
1302 Roo.dd.DDM._addListeners();
1303
1304 }