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