Bug 7356 - Fix various typos and mis-spellings
[koha.git] / koha-tmpl / intranet-tmpl / prog / en / lib / yui / dragdrop / dragdrop-debug.js
1 /*
2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 2.8.0r4
6 */
7 /**
8  * The drag and drop utility provides a framework for building drag and drop
9  * applications.  In addition to enabling drag and drop for specific elements,
10  * the drag and drop elements are tracked by the manager class, and the
11  * interactions between the various elements are tracked during the drag and
12  * the implementing code is notified about these important moments.
13  * @module dragdrop
14  * @title Drag and Drop
15  * @requires yahoo,dom,event
16  * @namespace YAHOO.util
17  */
18
19 // Only load the library once.  Rewriting the manager class would orphan 
20 // existing drag and drop instances.
21 if (!YAHOO.util.DragDropMgr) {
22
23 /**
24  * DragDropMgr is a singleton that tracks the element interaction for 
25  * all DragDrop items in the window.  Generally, you will not call 
26  * this class directly, but it does have helper methods that could 
27  * be useful in your DragDrop implementations.
28  * @class DragDropMgr
29  * @static
30  */
31 YAHOO.util.DragDropMgr = function() {
32
33     var Event = YAHOO.util.Event,
34         Dom = YAHOO.util.Dom;
35
36     return {
37         /**
38         * This property is used to turn on global use of the shim element on all DragDrop instances, defaults to false for backcompat. (Use: YAHOO.util.DDM.useShim = true)
39         * @property useShim
40         * @type Boolean
41         * @static
42         */
43         useShim: false,
44         /**
45         * This property is used to determine if the shim is active over the screen, default false.
46         * @private
47         * @property _shimActive
48         * @type Boolean
49         * @static
50         */
51         _shimActive: false,
52         /**
53         * This property is used when useShim is set on a DragDrop object to store the current state of DDM.useShim so it can be reset when a drag operation is done.
54         * @private
55         * @property _shimState
56         * @type Boolean
57         * @static
58         */
59         _shimState: false,
60         /**
61         * This property is used when useShim is set to true, it will set the opacity on the shim to .5 for debugging. Use: (YAHOO.util.DDM._debugShim = true;)
62         * @private
63         * @property _debugShim
64         * @type Boolean
65         * @static
66         */
67         _debugShim: false,
68         /**
69         * This method will create a shim element (giving it the id of yui-ddm-shim), it also attaches the mousemove and mouseup listeners to it and attaches a scroll listener on the window
70         * @private
71         * @method _sizeShim
72         * @static
73         */
74         _createShim: function() {
75             YAHOO.log('Creating Shim Element', 'info', 'DragDropMgr');
76             var s = document.createElement('div');
77             s.id = 'yui-ddm-shim';
78             if (document.body.firstChild) {
79                 document.body.insertBefore(s, document.body.firstChild);
80             } else {
81                 document.body.appendChild(s);
82             }
83             s.style.display = 'none';
84             s.style.backgroundColor = 'red';
85             s.style.position = 'absolute';
86             s.style.zIndex = '99999';
87             Dom.setStyle(s, 'opacity', '0');
88             this._shim = s;
89             Event.on(s, "mouseup",   this.handleMouseUp, this, true);
90             Event.on(s, "mousemove", this.handleMouseMove, this, true);
91             Event.on(window, 'scroll', this._sizeShim, this, true);
92         },
93         /**
94         * This method will size the shim, called from activate and on window scroll event
95         * @private
96         * @method _sizeShim
97         * @static
98         */
99         _sizeShim: function() {
100             if (this._shimActive) {
101                 YAHOO.log('Sizing Shim', 'info', 'DragDropMgr');
102                 var s = this._shim;
103                 s.style.height = Dom.getDocumentHeight() + 'px';
104                 s.style.width = Dom.getDocumentWidth() + 'px';
105                 s.style.top = '0';
106                 s.style.left = '0';
107             }
108         },
109         /**
110         * This method will create the shim element if needed, then show the shim element, size the element and set the _shimActive property to true
111         * @private
112         * @method _activateShim
113         * @static
114         */
115         _activateShim: function() {
116             if (this.useShim) {
117                 YAHOO.log('Activating Shim', 'info', 'DragDropMgr');
118                 if (!this._shim) {
119                     this._createShim();
120                 }
121                 this._shimActive = true;
122                 var s = this._shim,
123                     o = '0';
124                 if (this._debugShim) {
125                     o = '.5';
126                 }
127                 Dom.setStyle(s, 'opacity', o);
128                 this._sizeShim();
129                 s.style.display = 'block';
130             }
131         },
132         /**
133         * This method will hide the shim element and set the _shimActive property to false
134         * @private
135         * @method _deactivateShim
136         * @static
137         */
138         _deactivateShim: function() {
139             YAHOO.log('Deactivating Shim', 'info', 'DragDropMgr');
140             this._shim.style.display = 'none';
141             this._shimActive = false;
142         },
143         /**
144         * The HTML element created to use as a shim over the document to track mouse movements
145         * @private
146         * @property _shim
147         * @type HTMLElement
148         * @static
149         */
150         _shim: null,
151         /**
152          * Two dimensional Array of registered DragDrop objects.  The first 
153          * dimension is the DragDrop item group, the second the DragDrop 
154          * object.
155          * @property ids
156          * @type {string: string}
157          * @private
158          * @static
159          */
160         ids: {},
161
162         /**
163          * Array of element ids defined as drag handles.  Used to determine 
164          * if the element that generated the mousedown event is actually the 
165          * handle and not the html element itself.
166          * @property handleIds
167          * @type {string: string}
168          * @private
169          * @static
170          */
171         handleIds: {},
172
173         /**
174          * the DragDrop object that is currently being dragged
175          * @property dragCurrent
176          * @type DragDrop
177          * @private
178          * @static
179          **/
180         dragCurrent: null,
181
182         /**
183          * the DragDrop object(s) that are being hovered over
184          * @property dragOvers
185          * @type Array
186          * @private
187          * @static
188          */
189         dragOvers: {},
190
191         /**
192          * the X distance between the cursor and the object being dragged
193          * @property deltaX
194          * @type int
195          * @private
196          * @static
197          */
198         deltaX: 0,
199
200         /**
201          * the Y distance between the cursor and the object being dragged
202          * @property deltaY
203          * @type int
204          * @private
205          * @static
206          */
207         deltaY: 0,
208
209         /**
210          * Flag to determine if we should prevent the default behavior of the
211          * events we define. By default this is true, but this can be set to 
212          * false if you need the default behavior (not recommended)
213          * @property preventDefault
214          * @type boolean
215          * @static
216          */
217         preventDefault: true,
218
219         /**
220          * Flag to determine if we should stop the propagation of the events 
221          * we generate. This is true by default but you may want to set it to
222          * false if the html element contains other features that require the
223          * mouse click.
224          * @property stopPropagation
225          * @type boolean
226          * @static
227          */
228         stopPropagation: true,
229
230         /**
231          * Internal flag that is set to true when drag and drop has been
232          * initialized
233          * @property initialized
234          * @private
235          * @static
236          */
237         initialized: false,
238
239         /**
240          * All drag and drop can be disabled.
241          * @property locked
242          * @private
243          * @static
244          */
245         locked: false,
246
247         /**
248          * Provides additional information about the current set of
249          * interactions.  Can be accessed from the event handlers. It
250          * contains the following properties:
251          *
252          *       out:       onDragOut interactions
253          *       enter:     onDragEnter interactions
254          *       over:      onDragOver interactions
255          *       drop:      onDragDrop interactions
256          *       point:     The location of the cursor
257          *       draggedRegion: The location of dragged element at the time
258          *                      of the interaction
259          *       sourceRegion: The location of the source elemtn at the time
260          *                     of the interaction
261          *       validDrop: boolean
262          * @property interactionInfo
263          * @type object
264          * @static
265          */
266         interactionInfo: null,
267
268         /**
269          * Called the first time an element is registered.
270          * @method init
271          * @private
272          * @static
273          */
274         init: function() {
275             this.initialized = true;
276         },
277
278         /**
279          * In point mode, drag and drop interaction is defined by the 
280          * location of the cursor during the drag/drop
281          * @property POINT
282          * @type int
283          * @static
284          * @final
285          */
286         POINT: 0,
287
288         /**
289          * In intersect mode, drag and drop interaction is defined by the 
290          * cursor position or the amount of overlap of two or more drag and 
291          * drop objects.
292          * @property INTERSECT
293          * @type int
294          * @static
295          * @final
296          */
297         INTERSECT: 1,
298
299         /**
300          * In intersect mode, drag and drop interaction is defined only by the 
301          * overlap of two or more drag and drop objects.
302          * @property STRICT_INTERSECT
303          * @type int
304          * @static
305          * @final
306          */
307         STRICT_INTERSECT: 2,
308
309         /**
310          * The current drag and drop mode.  Default: POINT
311          * @property mode
312          * @type int
313          * @static
314          */
315         mode: 0,
316
317         /**
318          * Runs method on all drag and drop objects
319          * @method _execOnAll
320          * @private
321          * @static
322          */
323         _execOnAll: function(sMethod, args) {
324             for (var i in this.ids) {
325                 for (var j in this.ids[i]) {
326                     var oDD = this.ids[i][j];
327                     if (! this.isTypeOfDD(oDD)) {
328                         continue;
329                     }
330                     oDD[sMethod].apply(oDD, args);
331                 }
332             }
333         },
334
335         /**
336          * Drag and drop initialization.  Sets up the global event handlers
337          * @method _onLoad
338          * @private
339          * @static
340          */
341         _onLoad: function() {
342
343             this.init();
344
345             YAHOO.log("DragDropMgr onload", "info", "DragDropMgr");
346             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
347             Event.on(document, "mousemove", this.handleMouseMove, this, true);
348             Event.on(window,   "unload",    this._onUnload, this, true);
349             Event.on(window,   "resize",    this._onResize, this, true);
350             // Event.on(window,   "mouseout",    this._test);
351
352         },
353
354         /**
355          * Reset constraints on all drag and drop objs
356          * @method _onResize
357          * @private
358          * @static
359          */
360         _onResize: function(e) {
361             YAHOO.log("window resize", "info", "DragDropMgr");
362             this._execOnAll("resetConstraints", []);
363         },
364
365         /**
366          * Lock all drag and drop functionality
367          * @method lock
368          * @static
369          */
370         lock: function() { this.locked = true; },
371
372         /**
373          * Unlock all drag and drop functionality
374          * @method unlock
375          * @static
376          */
377         unlock: function() { this.locked = false; },
378
379         /**
380          * Is drag and drop locked?
381          * @method isLocked
382          * @return {boolean} True if drag and drop is locked, false otherwise.
383          * @static
384          */
385         isLocked: function() { return this.locked; },
386
387         /**
388          * Location cache that is set for all drag drop objects when a drag is
389          * initiated, cleared when the drag is finished.
390          * @property locationCache
391          * @private
392          * @static
393          */
394         locationCache: {},
395
396         /**
397          * Set useCache to false if you want to force object the lookup of each
398          * drag and drop linked element constantly during a drag.
399          * @property useCache
400          * @type boolean
401          * @static
402          */
403         useCache: true,
404
405         /**
406          * The number of pixels that the mouse needs to move after the 
407          * mousedown before the drag is initiated.  Default=3;
408          * @property clickPixelThresh
409          * @type int
410          * @static
411          */
412         clickPixelThresh: 3,
413
414         /**
415          * The number of milliseconds after the mousedown event to initiate the
416          * drag if we don't get a mouseup event. Default=1000
417          * @property clickTimeThresh
418          * @type int
419          * @static
420          */
421         clickTimeThresh: 1000,
422
423         /**
424          * Flag that indicates that either the drag pixel threshold or the 
425          * mousdown time threshold has been met
426          * @property dragThreshMet
427          * @type boolean
428          * @private
429          * @static
430          */
431         dragThreshMet: false,
432
433         /**
434          * Timeout used for the click time threshold
435          * @property clickTimeout
436          * @type Object
437          * @private
438          * @static
439          */
440         clickTimeout: null,
441
442         /**
443          * The X position of the mousedown event stored for later use when a 
444          * drag threshold is met.
445          * @property startX
446          * @type int
447          * @private
448          * @static
449          */
450         startX: 0,
451
452         /**
453          * The Y position of the mousedown event stored for later use when a 
454          * drag threshold is met.
455          * @property startY
456          * @type int
457          * @private
458          * @static
459          */
460         startY: 0,
461
462         /**
463          * Flag to determine if the drag event was fired from the click timeout and
464          * not the mouse move threshold.
465          * @property fromTimeout
466          * @type boolean
467          * @private
468          * @static
469          */
470         fromTimeout: false,
471
472         /**
473          * Each DragDrop instance must be registered with the DragDropMgr.  
474          * This is executed in DragDrop.init()
475          * @method regDragDrop
476          * @param {DragDrop} oDD the DragDrop object to register
477          * @param {String} sGroup the name of the group this element belongs to
478          * @static
479          */
480         regDragDrop: function(oDD, sGroup) {
481             if (!this.initialized) { this.init(); }
482             
483             if (!this.ids[sGroup]) {
484                 this.ids[sGroup] = {};
485             }
486             this.ids[sGroup][oDD.id] = oDD;
487         },
488
489         /**
490          * Removes the supplied dd instance from the supplied group. Executed
491          * by DragDrop.removeFromGroup, so don't call this function directly.
492          * @method removeDDFromGroup
493          * @private
494          * @static
495          */
496         removeDDFromGroup: function(oDD, sGroup) {
497             if (!this.ids[sGroup]) {
498                 this.ids[sGroup] = {};
499             }
500
501             var obj = this.ids[sGroup];
502             if (obj && obj[oDD.id]) {
503                 delete obj[oDD.id];
504             }
505         },
506
507         /**
508          * Unregisters a drag and drop item.  This is executed in 
509          * DragDrop.unreg, use that method instead of calling this directly.
510          * @method _remove
511          * @private
512          * @static
513          */
514         _remove: function(oDD) {
515             for (var g in oDD.groups) {
516                 if (g) {
517                     var item = this.ids[g];
518                     if (item && item[oDD.id]) {
519                         delete item[oDD.id];
520                     }
521                 }
522                 
523             }
524             delete this.handleIds[oDD.id];
525         },
526
527         /**
528          * Each DragDrop handle element must be registered.  This is done
529          * automatically when executing DragDrop.setHandleElId()
530          * @method regHandle
531          * @param {String} sDDId the DragDrop id this element is a handle for
532          * @param {String} sHandleId the id of the element that is the drag 
533          * handle
534          * @static
535          */
536         regHandle: function(sDDId, sHandleId) {
537             if (!this.handleIds[sDDId]) {
538                 this.handleIds[sDDId] = {};
539             }
540             this.handleIds[sDDId][sHandleId] = sHandleId;
541         },
542
543         /**
544          * Utility function to determine if a given element has been 
545          * registered as a drag drop item.
546          * @method isDragDrop
547          * @param {String} id the element id to check
548          * @return {boolean} true if this element is a DragDrop item, 
549          * false otherwise
550          * @static
551          */
552         isDragDrop: function(id) {
553             return ( this.getDDById(id) ) ? true : false;
554         },
555
556         /**
557          * Returns the drag and drop instances that are in all groups the
558          * passed in instance belongs to.
559          * @method getRelated
560          * @param {DragDrop} p_oDD the obj to get related data for
561          * @param {boolean} bTargetsOnly if true, only return targetable objs
562          * @return {DragDrop[]} the related instances
563          * @static
564          */
565         getRelated: function(p_oDD, bTargetsOnly) {
566             var oDDs = [];
567             for (var i in p_oDD.groups) {
568                 for (var j in this.ids[i]) {
569                     var dd = this.ids[i][j];
570                     if (! this.isTypeOfDD(dd)) {
571                         continue;
572                     }
573                     if (!bTargetsOnly || dd.isTarget) {
574                         oDDs[oDDs.length] = dd;
575                     }
576                 }
577             }
578
579             return oDDs;
580         },
581
582         /**
583          * Returns true if the specified dd target is a legal target for 
584          * the specifice drag obj
585          * @method isLegalTarget
586          * @param {DragDrop} the drag obj
587          * @param {DragDrop} the target
588          * @return {boolean} true if the target is a legal target for the 
589          * dd obj
590          * @static
591          */
592         isLegalTarget: function (oDD, oTargetDD) {
593             var targets = this.getRelated(oDD, true);
594             for (var i=0, len=targets.length;i<len;++i) {
595                 if (targets[i].id == oTargetDD.id) {
596                     return true;
597                 }
598             }
599
600             return false;
601         },
602
603         /**
604          * My goal is to be able to transparently determine if an object is
605          * typeof DragDrop, and the exact subclass of DragDrop.  typeof 
606          * returns "object", oDD.constructor.toString() always returns
607          * "DragDrop" and not the name of the subclass.  So for now it just
608          * evaluates a well-known variable in DragDrop.
609          * @method isTypeOfDD
610          * @param {Object} the object to evaluate
611          * @return {boolean} true if typeof oDD = DragDrop
612          * @static
613          */
614         isTypeOfDD: function (oDD) {
615             return (oDD && oDD.__ygDragDrop);
616         },
617
618         /**
619          * Utility function to determine if a given element has been 
620          * registered as a drag drop handle for the given Drag Drop object.
621          * @method isHandle
622          * @param {String} id the element id to check
623          * @return {boolean} true if this element is a DragDrop handle, false 
624          * otherwise
625          * @static
626          */
627         isHandle: function(sDDId, sHandleId) {
628             return ( this.handleIds[sDDId] && 
629                             this.handleIds[sDDId][sHandleId] );
630         },
631
632         /**
633          * Returns the DragDrop instance for a given id
634          * @method getDDById
635          * @param {String} id the id of the DragDrop object
636          * @return {DragDrop} the drag drop object, null if it is not found
637          * @static
638          */
639         getDDById: function(id) {
640             for (var i in this.ids) {
641                 if (this.ids[i][id]) {
642                     return this.ids[i][id];
643                 }
644             }
645             return null;
646         },
647
648         /**
649          * Fired after a registered DragDrop object gets the mousedown event.
650          * Sets up the events required to track the object being dragged
651          * @method handleMouseDown
652          * @param {Event} e the event
653          * @param oDD the DragDrop object being dragged
654          * @private
655          * @static
656          */
657         handleMouseDown: function(e, oDD) {
658             //this._activateShim();
659
660             this.currentTarget = YAHOO.util.Event.getTarget(e);
661
662             this.dragCurrent = oDD;
663
664             var el = oDD.getEl();
665
666             // track start position
667             this.startX = YAHOO.util.Event.getPageX(e);
668             this.startY = YAHOO.util.Event.getPageY(e);
669
670             this.deltaX = this.startX - el.offsetLeft;
671             this.deltaY = this.startY - el.offsetTop;
672
673             this.dragThreshMet = false;
674
675             this.clickTimeout = setTimeout( 
676                     function() { 
677                         var DDM = YAHOO.util.DDM;
678                         DDM.startDrag(DDM.startX, DDM.startY);
679                         DDM.fromTimeout = true;
680                     }, 
681                     this.clickTimeThresh );
682         },
683
684         /**
685          * Fired when either the drag pixel threshold or the mousedown hold 
686          * time threshold has been met.
687          * @method startDrag
688          * @param x {int} the X position of the original mousedown
689          * @param y {int} the Y position of the original mousedown
690          * @static
691          */
692         startDrag: function(x, y) {
693             if (this.dragCurrent && this.dragCurrent.useShim) {
694                 this._shimState = this.useShim;
695                 this.useShim = true;
696             }
697             this._activateShim();
698             YAHOO.log("firing drag start events", "info", "DragDropMgr");
699             clearTimeout(this.clickTimeout);
700             var dc = this.dragCurrent;
701             if (dc && dc.events.b4StartDrag) {
702                 dc.b4StartDrag(x, y);
703                 dc.fireEvent('b4StartDragEvent', { x: x, y: y });
704             }
705             if (dc && dc.events.startDrag) {
706                 dc.startDrag(x, y);
707                 dc.fireEvent('startDragEvent', { x: x, y: y });
708             }
709             this.dragThreshMet = true;
710         },
711
712         /**
713          * Internal function to handle the mouseup event.  Will be invoked 
714          * from the context of the document.
715          * @method handleMouseUp
716          * @param {Event} e the event
717          * @private
718          * @static
719          */
720         handleMouseUp: function(e) {
721             if (this.dragCurrent) {
722                 clearTimeout(this.clickTimeout);
723
724                 if (this.dragThreshMet) {
725                     YAHOO.log("mouseup detected - completing drag", "info", "DragDropMgr");
726                     if (this.fromTimeout) {
727                         YAHOO.log('fromTimeout is true (mouse didn\'t move), call handleMouseMove so we can get the dragOver event', 'info', 'DragDropMgr');
728                         this.fromTimeout = false;
729                         this.handleMouseMove(e);
730                     }
731                     this.fromTimeout = false;
732                     this.fireEvents(e, true);
733                 } else {
734                     YAHOO.log("drag threshold not met", "info", "DragDropMgr");
735                 }
736
737                 this.stopDrag(e);
738
739                 this.stopEvent(e);
740             }
741         },
742
743         /**
744          * Utility to stop event propagation and event default, if these 
745          * features are turned on.
746          * @method stopEvent
747          * @param {Event} e the event as returned by this.getEvent()
748          * @static
749          */
750         stopEvent: function(e) {
751             if (this.stopPropagation) {
752                 YAHOO.util.Event.stopPropagation(e);
753             }
754
755             if (this.preventDefault) {
756                 YAHOO.util.Event.preventDefault(e);
757             }
758         },
759
760         /** 
761          * Ends the current drag, cleans up the state, and fires the endDrag
762          * and mouseUp events.  Called internally when a mouseup is detected
763          * during the drag.  Can be fired manually during the drag by passing
764          * either another event (such as the mousemove event received in onDrag)
765          * or a fake event with pageX and pageY defined (so that endDrag and
766          * onMouseUp have usable position data.).  Alternatively, pass true
767          * for the silent parameter so that the endDrag and onMouseUp events
768          * are skipped (so no event data is needed.)
769          *
770          * @method stopDrag
771          * @param {Event} e the mouseup event, another event (or a fake event) 
772          *                  with pageX and pageY defined, or nothing if the 
773          *                  silent parameter is true
774          * @param {boolean} silent skips the enddrag and mouseup events if true
775          * @static
776          */
777         stopDrag: function(e, silent) {
778             // YAHOO.log("mouseup - removing event handlers");
779             var dc = this.dragCurrent;
780             // Fire the drag end event for the item that was dragged
781             if (dc && !silent) {
782                 if (this.dragThreshMet) {
783                     YAHOO.log("firing endDrag events", "info", "DragDropMgr");
784                     if (dc.events.b4EndDrag) {
785                         dc.b4EndDrag(e);
786                         dc.fireEvent('b4EndDragEvent', { e: e });
787                     }
788                     if (dc.events.endDrag) {
789                         dc.endDrag(e);
790                         dc.fireEvent('endDragEvent', { e: e });
791                     }
792                 }
793                 if (dc.events.mouseUp) {
794                     YAHOO.log("firing dragdrop onMouseUp event", "info", "DragDropMgr");
795                     dc.onMouseUp(e);
796                     dc.fireEvent('mouseUpEvent', { e: e });
797                 }
798             }
799
800             if (this._shimActive) {
801                 this._deactivateShim();
802                 if (this.dragCurrent && this.dragCurrent.useShim) {
803                     this.useShim = this._shimState;
804                     this._shimState = false;
805                 }
806             }
807
808             this.dragCurrent = null;
809             this.dragOvers = {};
810         },
811
812         /** 
813          * Internal function to handle the mousemove event.  Will be invoked 
814          * from the context of the html element.
815          *
816          * @TODO figure out what we can do about mouse events lost when the 
817          * user drags objects beyond the window boundary.  Currently we can 
818          * detect this in internet explorer by verifying that the mouse is 
819          * down during the mousemove event.  Firefox doesn't give us the 
820          * button state on the mousemove event.
821          * @method handleMouseMove
822          * @param {Event} e the event
823          * @private
824          * @static
825          */
826         handleMouseMove: function(e) {
827             //YAHOO.log("handlemousemove");
828
829             var dc = this.dragCurrent;
830             if (dc) {
831                 // YAHOO.log("no current drag obj");
832
833                 // var button = e.which || e.button;
834                 // YAHOO.log("which: " + e.which + ", button: "+ e.button);
835
836                 // check for IE mouseup outside of page boundary
837                 if (YAHOO.util.Event.isIE && !e.button) {
838                     YAHOO.log("button failure", "info", "DragDropMgr");
839                     this.stopEvent(e);
840                     return this.handleMouseUp(e);
841                 } else {
842                     if (e.clientX < 0 || e.clientY < 0) {
843                         //This will stop the element from leaving the viewport in FF, Opera & Safari
844                         //Not turned on yet
845                         //YAHOO.log("Either clientX or clientY is negative, stop the event.", "info", "DragDropMgr");
846                         //this.stopEvent(e);
847                         //return false;
848                     }
849                 }
850
851                 if (!this.dragThreshMet) {
852                     var diffX = Math.abs(this.startX - YAHOO.util.Event.getPageX(e));
853                     var diffY = Math.abs(this.startY - YAHOO.util.Event.getPageY(e));
854                     // YAHOO.log("diffX: " + diffX + "diffY: " + diffY);
855                     if (diffX > this.clickPixelThresh || 
856                                 diffY > this.clickPixelThresh) {
857                         YAHOO.log("pixel threshold met", "info", "DragDropMgr");
858                         this.startDrag(this.startX, this.startY);
859                     }
860                 }
861
862                 if (this.dragThreshMet) {
863                     if (dc && dc.events.b4Drag) {
864                         dc.b4Drag(e);
865                         dc.fireEvent('b4DragEvent', { e: e});
866                     }
867                     if (dc && dc.events.drag) {
868                         dc.onDrag(e);
869                         dc.fireEvent('dragEvent', { e: e});
870                     }
871                     if (dc) {
872                         this.fireEvents(e, false);
873                     }
874                 }
875
876                 this.stopEvent(e);
877             }
878         },
879         
880         /**
881          * Iterates over all of the DragDrop elements to find ones we are 
882          * hovering over or dropping on
883          * @method fireEvents
884          * @param {Event} e the event
885          * @param {boolean} isDrop is this a drop op or a mouseover op?
886          * @private
887          * @static
888          */
889         fireEvents: function(e, isDrop) {
890             var dc = this.dragCurrent;
891
892             // If the user did the mouse up outside of the window, we could 
893             // get here even though we have ended the drag.
894             // If the config option dragOnly is true, bail out and don't fire the events
895             if (!dc || dc.isLocked() || dc.dragOnly) {
896                 return;
897             }
898
899             var x = YAHOO.util.Event.getPageX(e),
900                 y = YAHOO.util.Event.getPageY(e),
901                 pt = new YAHOO.util.Point(x,y),
902                 pos = dc.getTargetCoord(pt.x, pt.y),
903                 el = dc.getDragEl(),
904                 events = ['out', 'over', 'drop', 'enter'],
905                 curRegion = new YAHOO.util.Region( pos.y, 
906                                                pos.x + el.offsetWidth,
907                                                pos.y + el.offsetHeight, 
908                                                pos.x ),
909             
910                 oldOvers = [], // cache the previous dragOver array
911                 inGroupsObj  = {},
912                 inGroups  = [],
913                 data = {
914                     outEvts: [],
915                     overEvts: [],
916                     dropEvts: [],
917                     enterEvts: []
918                 };
919
920
921             // Check to see if the object(s) we were hovering over is no longer 
922             // being hovered over so we can fire the onDragOut event
923             for (var i in this.dragOvers) {
924
925                 var ddo = this.dragOvers[i];
926
927                 if (! this.isTypeOfDD(ddo)) {
928                     continue;
929                 }
930                 if (! this.isOverTarget(pt, ddo, this.mode, curRegion)) {
931                     data.outEvts.push( ddo );
932                 }
933
934                 oldOvers[i] = true;
935                 delete this.dragOvers[i];
936             }
937
938             for (var sGroup in dc.groups) {
939                 // YAHOO.log("Processing group " + sGroup);
940                 
941                 if ("string" != typeof sGroup) {
942                     continue;
943                 }
944
945                 for (i in this.ids[sGroup]) {
946                     var oDD = this.ids[sGroup][i];
947                     if (! this.isTypeOfDD(oDD)) {
948                         continue;
949                     }
950
951                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
952                         if (this.isOverTarget(pt, oDD, this.mode, curRegion)) {
953                             inGroupsObj[sGroup] = true;
954                             // look for drop interactions
955                             if (isDrop) {
956                                 data.dropEvts.push( oDD );
957                             // look for drag enter and drag over interactions
958                             } else {
959
960                                 // initial drag over: dragEnter fires
961                                 if (!oldOvers[oDD.id]) {
962                                     data.enterEvts.push( oDD );
963                                 // subsequent drag overs: dragOver fires
964                                 } else {
965                                     data.overEvts.push( oDD );
966                                 }
967
968                                 this.dragOvers[oDD.id] = oDD;
969                             }
970                         }
971                     }
972                 }
973             }
974
975             this.interactionInfo = {
976                 out:       data.outEvts,
977                 enter:     data.enterEvts,
978                 over:      data.overEvts,
979                 drop:      data.dropEvts,
980                 point:     pt,
981                 draggedRegion:    curRegion,
982                 sourceRegion: this.locationCache[dc.id],
983                 validDrop: isDrop
984             };
985
986             
987             for (var inG in inGroupsObj) {
988                 inGroups.push(inG);
989             }
990
991             // notify about a drop that did not find a target
992             if (isDrop && !data.dropEvts.length) {
993                 YAHOO.log(dc.id + " dropped, but not on a target", "info", "DragDropMgr");
994                 this.interactionInfo.validDrop = false;
995                 if (dc.events.invalidDrop) {
996                     dc.onInvalidDrop(e);
997                     dc.fireEvent('invalidDropEvent', { e: e });
998                 }
999             }
1000             for (i = 0; i < events.length; i++) {
1001                 var tmp = null;
1002                 if (data[events[i] + 'Evts']) {
1003                     tmp = data[events[i] + 'Evts'];
1004                 }
1005                 if (tmp && tmp.length) {
1006                     var type = events[i].charAt(0).toUpperCase() + events[i].substr(1),
1007                         ev = 'onDrag' + type,
1008                         b4 = 'b4Drag' + type,
1009                         cev = 'drag' + type + 'Event',
1010                         check = 'drag' + type;
1011                     if (this.mode) {
1012                         YAHOO.log(dc.id + ' ' + ev + ': ' + tmp, "info", "DragDropMgr");
1013                         if (dc.events[b4]) {
1014                             dc[b4](e, tmp, inGroups);
1015                             dc.fireEvent(b4 + 'Event', { event: e, info: tmp, group: inGroups });
1016                             
1017                         }
1018                         if (dc.events[check]) {
1019                             dc[ev](e, tmp, inGroups);
1020                             dc.fireEvent(cev, { event: e, info: tmp, group: inGroups });
1021                         }
1022                     } else {
1023                         for (var b = 0, len = tmp.length; b < len; ++b) {
1024                             YAHOO.log(dc.id + ' ' + ev + ': ' + tmp[b].id, "info", "DragDropMgr");
1025                             if (dc.events[b4]) {
1026                                 dc[b4](e, tmp[b].id, inGroups[0]);
1027                                 dc.fireEvent(b4 + 'Event', { event: e, info: tmp[b].id, group: inGroups[0] });
1028                             }
1029                             if (dc.events[check]) {
1030                                 dc[ev](e, tmp[b].id, inGroups[0]);
1031                                 dc.fireEvent(cev, { event: e, info: tmp[b].id, group: inGroups[0] });
1032                             }
1033                         }
1034                     }
1035                 }
1036             }
1037         },
1038
1039         /**
1040          * Helper function for getting the best match from the list of drag 
1041          * and drop objects returned by the drag and drop events when we are 
1042          * in INTERSECT mode.  It returns either the first object that the 
1043          * cursor is over, or the object that has the greatest overlap with 
1044          * the dragged element.
1045          * @method getBestMatch
1046          * @param  {DragDrop[]} dds The array of drag and drop objects 
1047          * targeted
1048          * @return {DragDrop}       The best single match
1049          * @static
1050          */
1051         getBestMatch: function(dds) {
1052             var winner = null;
1053
1054             var len = dds.length;
1055
1056             if (len == 1) {
1057                 winner = dds[0];
1058             } else {
1059                 // Loop through the targeted items
1060                 for (var i=0; i<len; ++i) {
1061                     var dd = dds[i];
1062                     // If the cursor is over the object, it wins.  If the 
1063                     // cursor is over multiple matches, the first one we come
1064                     // to wins.
1065                     if (this.mode == this.INTERSECT && dd.cursorIsOver) {
1066                         winner = dd;
1067                         break;
1068                     // Otherwise the object with the most overlap wins
1069                     } else {
1070                         if (!winner || !winner.overlap || (dd.overlap &&
1071                             winner.overlap.getArea() < dd.overlap.getArea())) {
1072                             winner = dd;
1073                         }
1074                     }
1075                 }
1076             }
1077
1078             return winner;
1079         },
1080
1081         /**
1082          * Refreshes the cache of the top-left and bottom-right points of the 
1083          * drag and drop objects in the specified group(s).  This is in the
1084          * format that is stored in the drag and drop instance, so typical 
1085          * usage is:
1086          * <code>
1087          * YAHOO.util.DragDropMgr.refreshCache(ddinstance.groups);
1088          * </code>
1089          * Alternatively:
1090          * <code>
1091          * YAHOO.util.DragDropMgr.refreshCache({group1:true, group2:true});
1092          * </code>
1093          * @TODO this really should be an indexed array.  Alternatively this
1094          * method could accept both.
1095          * @method refreshCache
1096          * @param {Object} groups an associative array of groups to refresh
1097          * @static
1098          */
1099         refreshCache: function(groups) {
1100             YAHOO.log("refreshing element location cache", "info", "DragDropMgr");
1101
1102             // refresh everything if group array is not provided
1103             var g = groups || this.ids;
1104
1105             for (var sGroup in g) {
1106                 if ("string" != typeof sGroup) {
1107                     continue;
1108                 }
1109                 for (var i in this.ids[sGroup]) {
1110                     var oDD = this.ids[sGroup][i];
1111
1112                     if (this.isTypeOfDD(oDD)) {
1113                         var loc = this.getLocation(oDD);
1114                         if (loc) {
1115                             this.locationCache[oDD.id] = loc;
1116                         } else {
1117                             delete this.locationCache[oDD.id];
1118 YAHOO.log("Could not get the loc for " + oDD.id, "warn", "DragDropMgr");
1119                         }
1120                     }
1121                 }
1122             }
1123         },
1124
1125         /**
1126          * This checks to make sure an element exists and is in the DOM.  The
1127          * main purpose is to handle cases where innerHTML is used to remove
1128          * drag and drop objects from the DOM.  IE provides an 'unspecified
1129          * error' when trying to access the offsetParent of such an element
1130          * @method verifyEl
1131          * @param {HTMLElement} el the element to check
1132          * @return {boolean} true if the element looks usable
1133          * @static
1134          */
1135         verifyEl: function(el) {
1136             try {
1137                 if (el) {
1138                     var parent = el.offsetParent;
1139                     if (parent) {
1140                         return true;
1141                     }
1142                 }
1143             } catch(e) {
1144                 YAHOO.log("detected problem with an element", "info", "DragDropMgr");
1145             }
1146
1147             return false;
1148         },
1149         
1150         /**
1151          * Returns a Region object containing the drag and drop element's position
1152          * and size, including the padding configured for it
1153          * @method getLocation
1154          * @param {DragDrop} oDD the drag and drop object to get the 
1155          *                       location for
1156          * @return {YAHOO.util.Region} a Region object representing the total area
1157          *                             the element occupies, including any padding
1158          *                             the instance is configured for.
1159          * @static
1160          */
1161         getLocation: function(oDD) {
1162             if (! this.isTypeOfDD(oDD)) {
1163                 YAHOO.log(oDD + " is not a DD obj", "info", "DragDropMgr");
1164                 return null;
1165             }
1166
1167             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
1168
1169             try {
1170                 pos= YAHOO.util.Dom.getXY(el);
1171             } catch (e) { }
1172
1173             if (!pos) {
1174                 YAHOO.log("getXY failed", "info", "DragDropMgr");
1175                 return null;
1176             }
1177
1178             x1 = pos[0];
1179             x2 = x1 + el.offsetWidth;
1180             y1 = pos[1];
1181             y2 = y1 + el.offsetHeight;
1182
1183             t = y1 - oDD.padding[0];
1184             r = x2 + oDD.padding[1];
1185             b = y2 + oDD.padding[2];
1186             l = x1 - oDD.padding[3];
1187
1188             return new YAHOO.util.Region( t, r, b, l );
1189         },
1190
1191         /**
1192          * Checks the cursor location to see if it over the target
1193          * @method isOverTarget
1194          * @param {YAHOO.util.Point} pt The point to evaluate
1195          * @param {DragDrop} oTarget the DragDrop object we are inspecting
1196          * @param {boolean} intersect true if we are in intersect mode
1197          * @param {YAHOO.util.Region} pre-cached location of the dragged element
1198          * @return {boolean} true if the mouse is over the target
1199          * @private
1200          * @static
1201          */
1202         isOverTarget: function(pt, oTarget, intersect, curRegion) {
1203             // use cache if available
1204             var loc = this.locationCache[oTarget.id];
1205             if (!loc || !this.useCache) {
1206                 YAHOO.log("cache not populated", "info", "DragDropMgr");
1207                 loc = this.getLocation(oTarget);
1208                 this.locationCache[oTarget.id] = loc;
1209
1210                 YAHOO.log("cache: " + loc, "info", "DragDropMgr");
1211             }
1212
1213             if (!loc) {
1214                 YAHOO.log("could not get the location of the element", "info", "DragDropMgr");
1215                 return false;
1216             }
1217
1218             //YAHOO.log("loc: " + loc + ", pt: " + pt);
1219             oTarget.cursorIsOver = loc.contains( pt );
1220
1221             // DragDrop is using this as a sanity check for the initial mousedown
1222             // in this case we are done.  In POINT mode, if the drag obj has no
1223             // contraints, we are done. Otherwise we need to evaluate the 
1224             // region the target as occupies to determine if the dragged element
1225             // overlaps with it.
1226             
1227             var dc = this.dragCurrent;
1228             if (!dc || (!intersect && !dc.constrainX && !dc.constrainY)) {
1229
1230                 //if (oTarget.cursorIsOver) {
1231                     //YAHOO.log("over " + oTarget + ", " + loc + ", " + pt, "warn");
1232                 //}
1233                 return oTarget.cursorIsOver;
1234             }
1235
1236             oTarget.overlap = null;
1237
1238
1239             // Get the current location of the drag element, this is the
1240             // location of the mouse event less the delta that represents
1241             // where the original mousedown happened on the element.  We
1242             // need to consider constraints and ticks as well.
1243
1244             if (!curRegion) {
1245                 var pos = dc.getTargetCoord(pt.x, pt.y);
1246                 var el = dc.getDragEl();
1247                 curRegion = new YAHOO.util.Region( pos.y, 
1248                                                    pos.x + el.offsetWidth,
1249                                                    pos.y + el.offsetHeight, 
1250                                                    pos.x );
1251             }
1252
1253             var overlap = curRegion.intersect(loc);
1254
1255             if (overlap) {
1256                 oTarget.overlap = overlap;
1257                 return (intersect) ? true : oTarget.cursorIsOver;
1258             } else {
1259                 return false;
1260             }
1261         },
1262
1263         /**
1264          * unload event handler
1265          * @method _onUnload
1266          * @private
1267          * @static
1268          */
1269         _onUnload: function(e, me) {
1270             this.unregAll();
1271         },
1272
1273         /**
1274          * Cleans up the drag and drop events and objects.
1275          * @method unregAll
1276          * @private
1277          * @static
1278          */
1279         unregAll: function() {
1280             YAHOO.log("unregister all", "info", "DragDropMgr");
1281
1282             if (this.dragCurrent) {
1283                 this.stopDrag();
1284                 this.dragCurrent = null;
1285             }
1286
1287             this._execOnAll("unreg", []);
1288
1289             //for (var i in this.elementCache) {
1290                 //delete this.elementCache[i];
1291             //}
1292             //this.elementCache = {};
1293
1294             this.ids = {};
1295         },
1296
1297         /**
1298          * A cache of DOM elements
1299          * @property elementCache
1300          * @private
1301          * @static
1302          * @deprecated elements are not cached now
1303          */
1304         elementCache: {},
1305         
1306         /**
1307          * Get the wrapper for the DOM element specified
1308          * @method getElWrapper
1309          * @param {String} id the id of the element to get
1310          * @return {YAHOO.util.DDM.ElementWrapper} the wrapped element
1311          * @private
1312          * @deprecated This wrapper isn't that useful
1313          * @static
1314          */
1315         getElWrapper: function(id) {
1316             var oWrapper = this.elementCache[id];
1317             if (!oWrapper || !oWrapper.el) {
1318                 oWrapper = this.elementCache[id] = 
1319                     new this.ElementWrapper(YAHOO.util.Dom.get(id));
1320             }
1321             return oWrapper;
1322         },
1323
1324         /**
1325          * Returns the actual DOM element
1326          * @method getElement
1327          * @param {String} id the id of the elment to get
1328          * @return {Object} The element
1329          * @deprecated use YAHOO.util.Dom.get instead
1330          * @static
1331          */
1332         getElement: function(id) {
1333             return YAHOO.util.Dom.get(id);
1334         },
1335         
1336         /**
1337          * Returns the style property for the DOM element (i.e., 
1338          * document.getElById(id).style)
1339          * @method getCss
1340          * @param {String} id the id of the elment to get
1341          * @return {Object} The style property of the element
1342          * @deprecated use YAHOO.util.Dom instead
1343          * @static
1344          */
1345         getCss: function(id) {
1346             var el = YAHOO.util.Dom.get(id);
1347             return (el) ? el.style : null;
1348         },
1349
1350         /**
1351          * Inner class for cached elements
1352          * @class DragDropMgr.ElementWrapper
1353          * @for DragDropMgr
1354          * @private
1355          * @deprecated
1356          */
1357         ElementWrapper: function(el) {
1358                 /**
1359                  * The element
1360                  * @property el
1361                  */
1362                 this.el = el || null;
1363                 /**
1364                  * The element id
1365                  * @property id
1366                  */
1367                 this.id = this.el && el.id;
1368                 /**
1369                  * A reference to the style property
1370                  * @property css
1371                  */
1372                 this.css = this.el && el.style;
1373             },
1374
1375         /**
1376          * Returns the X position of an html element
1377          * @method getPosX
1378          * @param el the element for which to get the position
1379          * @return {int} the X coordinate
1380          * @for DragDropMgr
1381          * @deprecated use YAHOO.util.Dom.getX instead
1382          * @static
1383          */
1384         getPosX: function(el) {
1385             return YAHOO.util.Dom.getX(el);
1386         },
1387
1388         /**
1389          * Returns the Y position of an html element
1390          * @method getPosY
1391          * @param el the element for which to get the position
1392          * @return {int} the Y coordinate
1393          * @deprecated use YAHOO.util.Dom.getY instead
1394          * @static
1395          */
1396         getPosY: function(el) {
1397             return YAHOO.util.Dom.getY(el); 
1398         },
1399
1400         /**
1401          * Swap two nodes.  In IE, we use the native method, for others we 
1402          * emulate the IE behavior
1403          * @method swapNode
1404          * @param n1 the first node to swap
1405          * @param n2 the other node to swap
1406          * @static
1407          */
1408         swapNode: function(n1, n2) {
1409             if (n1.swapNode) {
1410                 n1.swapNode(n2);
1411             } else {
1412                 var p = n2.parentNode;
1413                 var s = n2.nextSibling;
1414
1415                 if (s == n1) {
1416                     p.insertBefore(n1, n2);
1417                 } else if (n2 == n1.nextSibling) {
1418                     p.insertBefore(n2, n1);
1419                 } else {
1420                     n1.parentNode.replaceChild(n2, n1);
1421                     p.insertBefore(n1, s);
1422                 }
1423             }
1424         },
1425
1426         /**
1427          * Returns the current scroll position
1428          * @method getScroll
1429          * @private
1430          * @static
1431          */
1432         getScroll: function () {
1433             var t, l, dde=document.documentElement, db=document.body;
1434             if (dde && (dde.scrollTop || dde.scrollLeft)) {
1435                 t = dde.scrollTop;
1436                 l = dde.scrollLeft;
1437             } else if (db) {
1438                 t = db.scrollTop;
1439                 l = db.scrollLeft;
1440             } else {
1441                 YAHOO.log("could not get scroll property", "info", "DragDropMgr");
1442             }
1443             return { top: t, left: l };
1444         },
1445
1446         /**
1447          * Returns the specified element style property
1448          * @method getStyle
1449          * @param {HTMLElement} el          the element
1450          * @param {string}      styleProp   the style property
1451          * @return {string} The value of the style property
1452          * @deprecated use YAHOO.util.Dom.getStyle
1453          * @static
1454          */
1455         getStyle: function(el, styleProp) {
1456             return YAHOO.util.Dom.getStyle(el, styleProp);
1457         },
1458
1459         /**
1460          * Gets the scrollTop
1461          * @method getScrollTop
1462          * @return {int} the document's scrollTop
1463          * @static
1464          */
1465         getScrollTop: function () { return this.getScroll().top; },
1466
1467         /**
1468          * Gets the scrollLeft
1469          * @method getScrollLeft
1470          * @return {int} the document's scrollTop
1471          * @static
1472          */
1473         getScrollLeft: function () { return this.getScroll().left; },
1474
1475         /**
1476          * Sets the x/y position of an element to the location of the
1477          * target element.
1478          * @method moveToEl
1479          * @param {HTMLElement} moveEl      The element to move
1480          * @param {HTMLElement} targetEl    The position reference element
1481          * @static
1482          */
1483         moveToEl: function (moveEl, targetEl) {
1484             var aCoord = YAHOO.util.Dom.getXY(targetEl);
1485             YAHOO.log("moveToEl: " + aCoord, "info", "DragDropMgr");
1486             YAHOO.util.Dom.setXY(moveEl, aCoord);
1487         },
1488
1489         /**
1490          * Gets the client height
1491          * @method getClientHeight
1492          * @return {int} client height in px
1493          * @deprecated use YAHOO.util.Dom.getViewportHeight instead
1494          * @static
1495          */
1496         getClientHeight: function() {
1497             return YAHOO.util.Dom.getViewportHeight();
1498         },
1499
1500         /**
1501          * Gets the client width
1502          * @method getClientWidth
1503          * @return {int} client width in px
1504          * @deprecated use YAHOO.util.Dom.getViewportWidth instead
1505          * @static
1506          */
1507         getClientWidth: function() {
1508             return YAHOO.util.Dom.getViewportWidth();
1509         },
1510
1511         /**
1512          * Numeric array sort function
1513          * @method numericSort
1514          * @static
1515          */
1516         numericSort: function(a, b) { return (a - b); },
1517
1518         /**
1519          * Internal counter
1520          * @property _timeoutCount
1521          * @private
1522          * @static
1523          */
1524         _timeoutCount: 0,
1525
1526         /**
1527          * Trying to make the load order less important.  Without this we get
1528          * an error if this file is loaded before the Event Utility.
1529          * @method _addListeners
1530          * @private
1531          * @static
1532          */
1533         _addListeners: function() {
1534             var DDM = YAHOO.util.DDM;
1535             if ( YAHOO.util.Event && document ) {
1536                 DDM._onLoad();
1537             } else {
1538                 if (DDM._timeoutCount > 2000) {
1539                     YAHOO.log("DragDrop requires the Event Utility", "error", "DragDropMgr");
1540                 } else {
1541                     setTimeout(DDM._addListeners, 10);
1542                     if (document && document.body) {
1543                         DDM._timeoutCount += 1;
1544                     }
1545                 }
1546             }
1547         },
1548
1549         /**
1550          * Recursively searches the immediate parent and all child nodes for 
1551          * the handle element in order to determine wheter or not it was 
1552          * clicked.
1553          * @method handleWasClicked
1554          * @param node the html element to inspect
1555          * @static
1556          */
1557         handleWasClicked: function(node, id) {
1558             if (this.isHandle(id, node.id)) {
1559                 YAHOO.log("clicked node is a handle", "info", "DragDropMgr");
1560                 return true;
1561             } else {
1562                 // check to see if this is a text node child of the one we want
1563                 var p = node.parentNode;
1564                 // YAHOO.log("p: " + p);
1565
1566                 while (p) {
1567                     if (this.isHandle(id, p.id)) {
1568                         return true;
1569                     } else {
1570                         YAHOO.log(p.id + " is not a handle", "info", "DragDropMgr");
1571                         p = p.parentNode;
1572                     }
1573                 }
1574             }
1575
1576             return false;
1577         }
1578
1579     };
1580
1581 }();
1582
1583 // shorter alias, save a few bytes
1584 YAHOO.util.DDM = YAHOO.util.DragDropMgr;
1585 YAHOO.util.DDM._addListeners();
1586
1587 }
1588
1589 (function() {
1590
1591 var Event=YAHOO.util.Event; 
1592 var Dom=YAHOO.util.Dom;
1593
1594 /**
1595  * Defines the interface and base operation of items that that can be 
1596  * dragged or can be drop targets.  It was designed to be extended, overriding
1597  * the event handlers for startDrag, onDrag, onDragOver, onDragOut.
1598  * Up to three html elements can be associated with a DragDrop instance:
1599  * <ul>
1600  * <li>linked element: the element that is passed into the constructor.
1601  * This is the element which defines the boundaries for interaction with 
1602  * other DragDrop objects.</li>
1603  * <li>handle element(s): The drag operation only occurs if the element that 
1604  * was clicked matches a handle element.  By default this is the linked 
1605  * element, but there are times that you will want only a portion of the 
1606  * linked element to initiate the drag operation, and the setHandleElId() 
1607  * method provides a way to define this.</li>
1608  * <li>drag element: this represents an the element that would be moved along
1609  * with the cursor during a drag operation.  By default, this is the linked
1610  * element itself as in {@link YAHOO.util.DD}.  setDragElId() lets you define
1611  * a separate element that would be moved, as in {@link YAHOO.util.DDProxy}
1612  * </li>
1613  * </ul>
1614  * This class should not be instantiated until the onload event to ensure that
1615  * the associated elements are available.
1616  * The following would define a DragDrop obj that would interact with any 
1617  * other DragDrop obj in the "group1" group:
1618  * <pre>
1619  *  dd = new YAHOO.util.DragDrop("div1", "group1");
1620  * </pre>
1621  * Since none of the event handlers have been implemented, nothing would 
1622  * actually happen if you were to run the code above.  Normally you would 
1623  * override this class or one of the default implementations, but you can 
1624  * also override the methods you want on an instance of the class...
1625  * <pre>
1626  *  dd.onDragDrop = function(e, id) {
1627  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
1628  *  }
1629  * </pre>
1630  * @namespace YAHOO.util
1631  * @class DragDrop
1632  * @constructor
1633  * @param {String} id of the element that is linked to this instance
1634  * @param {String} sGroup the group of related DragDrop objects
1635  * @param {object} config an object containing configurable attributes
1636  *                Valid properties for DragDrop: 
1637  *                    padding, isTarget, maintainOffset, primaryButtonOnly,
1638  */
1639 YAHOO.util.DragDrop = function(id, sGroup, config) {
1640     if (id) {
1641         this.init(id, sGroup, config); 
1642     }
1643 };
1644
1645 YAHOO.util.DragDrop.prototype = {
1646     /**
1647      * An Object Literal containing the events that we will be using: mouseDown, b4MouseDown, mouseUp, b4StartDrag, startDrag, b4EndDrag, endDrag, mouseUp, drag, b4Drag, invalidDrop, b4DragOut, dragOut, dragEnter, b4DragOver, dragOver, b4DragDrop, dragDrop
1648      * By setting any of these to false, then event will not be fired.
1649      * @property events
1650      * @type object
1651      */
1652     events: null,
1653     /**
1654     * @method on
1655     * @description Shortcut for EventProvider.subscribe, see <a href="YAHOO.util.EventProvider.html#subscribe">YAHOO.util.EventProvider.subscribe</a>
1656     */
1657     on: function() {
1658         this.subscribe.apply(this, arguments);
1659     },
1660     /**
1661      * The id of the element associated with this object.  This is what we 
1662      * refer to as the "linked element" because the size and position of 
1663      * this element is used to determine when the drag and drop objects have 
1664      * interacted.
1665      * @property id
1666      * @type String
1667      */
1668     id: null,
1669
1670     /**
1671      * Configuration attributes passed into the constructor
1672      * @property config
1673      * @type object
1674      */
1675     config: null,
1676
1677     /**
1678      * The id of the element that will be dragged.  By default this is same 
1679      * as the linked element , but could be changed to another element. Ex: 
1680      * YAHOO.util.DDProxy
1681      * @property dragElId
1682      * @type String
1683      * @private
1684      */
1685     dragElId: null, 
1686
1687     /**
1688      * the id of the element that initiates the drag operation.  By default 
1689      * this is the linked element, but could be changed to be a child of this
1690      * element.  This lets us do things like only starting the drag when the 
1691      * header element within the linked html element is clicked.
1692      * @property handleElId
1693      * @type String
1694      * @private
1695      */
1696     handleElId: null, 
1697
1698     /**
1699      * An associative array of HTML tags that will be ignored if clicked.
1700      * @property invalidHandleTypes
1701      * @type {string: string}
1702      */
1703     invalidHandleTypes: null, 
1704
1705     /**
1706      * An associative array of ids for elements that will be ignored if clicked
1707      * @property invalidHandleIds
1708      * @type {string: string}
1709      */
1710     invalidHandleIds: null, 
1711
1712     /**
1713      * An indexted array of css class names for elements that will be ignored
1714      * if clicked.
1715      * @property invalidHandleClasses
1716      * @type string[]
1717      */
1718     invalidHandleClasses: null, 
1719
1720     /**
1721      * The linked element's absolute X position at the time the drag was 
1722      * started
1723      * @property startPageX
1724      * @type int
1725      * @private
1726      */
1727     startPageX: 0,
1728
1729     /**
1730      * The linked element's absolute X position at the time the drag was 
1731      * started
1732      * @property startPageY
1733      * @type int
1734      * @private
1735      */
1736     startPageY: 0,
1737
1738     /**
1739      * The group defines a logical collection of DragDrop objects that are 
1740      * related.  Instances only get events when interacting with other 
1741      * DragDrop object in the same group.  This lets us define multiple 
1742      * groups using a single DragDrop subclass if we want.
1743      * @property groups
1744      * @type {string: string}
1745      */
1746     groups: null,
1747
1748     /**
1749      * Individual drag/drop instances can be locked.  This will prevent 
1750      * onmousedown start drag.
1751      * @property locked
1752      * @type boolean
1753      * @private
1754      */
1755     locked: false,
1756
1757     /**
1758      * Lock this instance
1759      * @method lock
1760      */
1761     lock: function() { this.locked = true; },
1762
1763     /**
1764      * Unlock this instace
1765      * @method unlock
1766      */
1767     unlock: function() { this.locked = false; },
1768
1769     /**
1770      * By default, all instances can be a drop target.  This can be disabled by
1771      * setting isTarget to false.
1772      * @property isTarget
1773      * @type boolean
1774      */
1775     isTarget: true,
1776
1777     /**
1778      * The padding configured for this drag and drop object for calculating
1779      * the drop zone intersection with this object.
1780      * @property padding
1781      * @type int[]
1782      */
1783     padding: null,
1784     /**
1785      * If this flag is true, do not fire drop events. The element is a drag only element (for movement not dropping)
1786      * @property dragOnly
1787      * @type Boolean
1788      */
1789     dragOnly: false,
1790
1791     /**
1792      * If this flag is true, a shim will be placed over the screen/viewable area to track mouse events. Should help with dragging elements over iframes and other controls.
1793      * @property useShim
1794      * @type Boolean
1795      */
1796     useShim: false,
1797
1798     /**
1799      * Cached reference to the linked element
1800      * @property _domRef
1801      * @private
1802      */
1803     _domRef: null,
1804
1805     /**
1806      * Internal typeof flag
1807      * @property __ygDragDrop
1808      * @private
1809      */
1810     __ygDragDrop: true,
1811
1812     /**
1813      * Set to true when horizontal contraints are applied
1814      * @property constrainX
1815      * @type boolean
1816      * @private
1817      */
1818     constrainX: false,
1819
1820     /**
1821      * Set to true when vertical contraints are applied
1822      * @property constrainY
1823      * @type boolean
1824      * @private
1825      */
1826     constrainY: false,
1827
1828     /**
1829      * The left constraint
1830      * @property minX
1831      * @type int
1832      * @private
1833      */
1834     minX: 0,
1835
1836     /**
1837      * The right constraint
1838      * @property maxX
1839      * @type int
1840      * @private
1841      */
1842     maxX: 0,
1843
1844     /**
1845      * The up constraint 
1846      * @property minY
1847      * @type int
1848      * @type int
1849      * @private
1850      */
1851     minY: 0,
1852
1853     /**
1854      * The down constraint 
1855      * @property maxY
1856      * @type int
1857      * @private
1858      */
1859     maxY: 0,
1860
1861     /**
1862      * The difference between the click position and the source element's location
1863      * @property deltaX
1864      * @type int
1865      * @private
1866      */
1867     deltaX: 0,
1868
1869     /**
1870      * The difference between the click position and the source element's location
1871      * @property deltaY
1872      * @type int
1873      * @private
1874      */
1875     deltaY: 0,
1876
1877     /**
1878      * Maintain offsets when we resetconstraints.  Set to true when you want
1879      * the position of the element relative to its parent to stay the same
1880      * when the page changes
1881      *
1882      * @property maintainOffset
1883      * @type boolean
1884      */
1885     maintainOffset: false,
1886
1887     /**
1888      * Array of pixel locations the element will snap to if we specified a 
1889      * horizontal graduation/interval.  This array is generated automatically
1890      * when you define a tick interval.
1891      * @property xTicks
1892      * @type int[]
1893      */
1894     xTicks: null,
1895
1896     /**
1897      * Array of pixel locations the element will snap to if we specified a 
1898      * vertical graduation/interval.  This array is generated automatically 
1899      * when you define a tick interval.
1900      * @property yTicks
1901      * @type int[]
1902      */
1903     yTicks: null,
1904
1905     /**
1906      * By default the drag and drop instance will only respond to the primary
1907      * button click (left button for a right-handed mouse).  Set to true to
1908      * allow drag and drop to start with any mouse click that is propogated
1909      * by the browser
1910      * @property primaryButtonOnly
1911      * @type boolean
1912      */
1913     primaryButtonOnly: true,
1914
1915     /**
1916      * The availabe property is false until the linked dom element is accessible.
1917      * @property available
1918      * @type boolean
1919      */
1920     available: false,
1921
1922     /**
1923      * By default, drags can only be initiated if the mousedown occurs in the
1924      * region the linked element is.  This is done in part to work around a
1925      * bug in some browsers that mis-report the mousedown if the previous
1926      * mouseup happened outside of the window.  This property is set to true
1927      * if outer handles are defined.
1928      *
1929      * @property hasOuterHandles
1930      * @type boolean
1931      * @default false
1932      */
1933     hasOuterHandles: false,
1934
1935     /**
1936      * Property that is assigned to a drag and drop object when testing to
1937      * see if it is being targeted by another dd object.  This property
1938      * can be used in intersect mode to help determine the focus of
1939      * the mouse interaction.  DDM.getBestMatch uses this property first to
1940      * determine the closest match in INTERSECT mode when multiple targets
1941      * are part of the same interaction.
1942      * @property cursorIsOver
1943      * @type boolean
1944      */
1945     cursorIsOver: false,
1946
1947     /**
1948      * Property that is assigned to a drag and drop object when testing to
1949      * see if it is being targeted by another dd object.  This is a region
1950      * that represents the area the draggable element overlaps this target.
1951      * DDM.getBestMatch uses this property to compare the size of the overlap
1952      * to that of other targets in order to determine the closest match in
1953      * INTERSECT mode when multiple targets are part of the same interaction.
1954      * @property overlap 
1955      * @type YAHOO.util.Region
1956      */
1957     overlap: null,
1958
1959     /**
1960      * Code that executes immediately before the startDrag event
1961      * @method b4StartDrag
1962      * @private
1963      */
1964     b4StartDrag: function(x, y) { },
1965
1966     /**
1967      * Abstract method called after a drag/drop object is clicked
1968      * and the drag or mousedown time thresholds have beeen met.
1969      * @method startDrag
1970      * @param {int} X click location
1971      * @param {int} Y click location
1972      */
1973     startDrag: function(x, y) { /* override this */ },
1974
1975     /**
1976      * Code that executes immediately before the onDrag event
1977      * @method b4Drag
1978      * @private
1979      */
1980     b4Drag: function(e) { },
1981
1982     /**
1983      * Abstract method called during the onMouseMove event while dragging an 
1984      * object.
1985      * @method onDrag
1986      * @param {Event} e the mousemove event
1987      */
1988     onDrag: function(e) { /* override this */ },
1989
1990     /**
1991      * Abstract method called when this element fist begins hovering over 
1992      * another DragDrop obj
1993      * @method onDragEnter
1994      * @param {Event} e the mousemove event
1995      * @param {String|DragDrop[]} id In POINT mode, the element
1996      * id this is hovering over.  In INTERSECT mode, an array of one or more 
1997      * dragdrop items being hovered over.
1998      */
1999     onDragEnter: function(e, id) { /* override this */ },
2000
2001     /**
2002      * Code that executes immediately before the onDragOver event
2003      * @method b4DragOver
2004      * @private
2005      */
2006     b4DragOver: function(e) { },
2007
2008     /**
2009      * Abstract method called when this element is hovering over another 
2010      * DragDrop obj
2011      * @method onDragOver
2012      * @param {Event} e the mousemove event
2013      * @param {String|DragDrop[]} id In POINT mode, the element
2014      * id this is hovering over.  In INTERSECT mode, an array of dd items 
2015      * being hovered over.
2016      */
2017     onDragOver: function(e, id) { /* override this */ },
2018
2019     /**
2020      * Code that executes immediately before the onDragOut event
2021      * @method b4DragOut
2022      * @private
2023      */
2024     b4DragOut: function(e) { },
2025
2026     /**
2027      * Abstract method called when we are no longer hovering over an element
2028      * @method onDragOut
2029      * @param {Event} e the mousemove event
2030      * @param {String|DragDrop[]} id In POINT mode, the element
2031      * id this was hovering over.  In INTERSECT mode, an array of dd items 
2032      * that the mouse is no longer over.
2033      */
2034     onDragOut: function(e, id) { /* override this */ },
2035
2036     /**
2037      * Code that executes immediately before the onDragDrop event
2038      * @method b4DragDrop
2039      * @private
2040      */
2041     b4DragDrop: function(e) { },
2042
2043     /**
2044      * Abstract method called when this item is dropped on another DragDrop 
2045      * obj
2046      * @method onDragDrop
2047      * @param {Event} e the mouseup event
2048      * @param {String|DragDrop[]} id In POINT mode, the element
2049      * id this was dropped on.  In INTERSECT mode, an array of dd items this 
2050      * was dropped on.
2051      */
2052     onDragDrop: function(e, id) { /* override this */ },
2053
2054     /**
2055      * Abstract method called when this item is dropped on an area with no
2056      * drop target
2057      * @method onInvalidDrop
2058      * @param {Event} e the mouseup event
2059      */
2060     onInvalidDrop: function(e) { /* override this */ },
2061
2062     /**
2063      * Code that executes immediately before the endDrag event
2064      * @method b4EndDrag
2065      * @private
2066      */
2067     b4EndDrag: function(e) { },
2068
2069     /**
2070      * Fired when we are done dragging the object
2071      * @method endDrag
2072      * @param {Event} e the mouseup event
2073      */
2074     endDrag: function(e) { /* override this */ },
2075
2076     /**
2077      * Code executed immediately before the onMouseDown event
2078      * @method b4MouseDown
2079      * @param {Event} e the mousedown event
2080      * @private
2081      */
2082     b4MouseDown: function(e) {  },
2083
2084     /**
2085      * Event handler that fires when a drag/drop obj gets a mousedown
2086      * @method onMouseDown
2087      * @param {Event} e the mousedown event
2088      */
2089     onMouseDown: function(e) { /* override this */ },
2090
2091     /**
2092      * Event handler that fires when a drag/drop obj gets a mouseup
2093      * @method onMouseUp
2094      * @param {Event} e the mouseup event
2095      */
2096     onMouseUp: function(e) { /* override this */ },
2097    
2098     /**
2099      * Override the onAvailable method to do what is needed after the initial
2100      * position was determined.
2101      * @method onAvailable
2102      */
2103     onAvailable: function () { 
2104         //this.logger.log("onAvailable (base)"); 
2105     },
2106
2107     /**
2108      * Returns a reference to the linked element
2109      * @method getEl
2110      * @return {HTMLElement} the html element 
2111      */
2112     getEl: function() { 
2113         if (!this._domRef) {
2114             this._domRef = Dom.get(this.id); 
2115         }
2116
2117         return this._domRef;
2118     },
2119
2120     /**
2121      * Returns a reference to the actual element to drag.  By default this is
2122      * the same as the html element, but it can be assigned to another 
2123      * element. An example of this can be found in YAHOO.util.DDProxy
2124      * @method getDragEl
2125      * @return {HTMLElement} the html element 
2126      */
2127     getDragEl: function() {
2128         return Dom.get(this.dragElId);
2129     },
2130
2131     /**
2132      * Sets up the DragDrop object.  Must be called in the constructor of any
2133      * YAHOO.util.DragDrop subclass
2134      * @method init
2135      * @param id the id of the linked element
2136      * @param {String} sGroup the group of related items
2137      * @param {object} config configuration attributes
2138      */
2139     init: function(id, sGroup, config) {
2140         this.initTarget(id, sGroup, config);
2141         Event.on(this._domRef || this.id, "mousedown", 
2142                         this.handleMouseDown, this, true);
2143
2144         // Event.on(this.id, "selectstart", Event.preventDefault);
2145         for (var i in this.events) {
2146             this.createEvent(i + 'Event');
2147         }
2148         
2149     },
2150
2151     /**
2152      * Initializes Targeting functionality only... the object does not
2153      * get a mousedown handler.
2154      * @method initTarget
2155      * @param id the id of the linked element
2156      * @param {String} sGroup the group of related items
2157      * @param {object} config configuration attributes
2158      */
2159     initTarget: function(id, sGroup, config) {
2160
2161         // configuration attributes 
2162         this.config = config || {};
2163
2164         this.events = {};
2165
2166         // create a local reference to the drag and drop manager
2167         this.DDM = YAHOO.util.DDM;
2168
2169         // initialize the groups object
2170         this.groups = {};
2171
2172         // assume that we have an element reference instead of an id if the
2173         // parameter is not a string
2174         if (typeof id !== "string") {
2175             YAHOO.log("id is not a string, assuming it is an HTMLElement");
2176             this._domRef = id;
2177             id = Dom.generateId(id);
2178         }
2179
2180         // set the id
2181         this.id = id;
2182
2183         // add to an interaction group
2184         this.addToGroup((sGroup) ? sGroup : "default");
2185
2186         // We don't want to register this as the handle with the manager
2187         // so we just set the id rather than calling the setter.
2188         this.handleElId = id;
2189
2190         Event.onAvailable(id, this.handleOnAvailable, this, true);
2191
2192         // create a logger instance
2193         this.logger = (YAHOO.widget.LogWriter) ? 
2194                 new YAHOO.widget.LogWriter(this.toString()) : YAHOO;
2195
2196         // the linked element is the element that gets dragged by default
2197         this.setDragElId(id); 
2198
2199         // by default, clicked anchors will not start drag operations. 
2200         // @TODO what else should be here?  Probably form fields.
2201         this.invalidHandleTypes = { A: "A" };
2202         this.invalidHandleIds = {};
2203         this.invalidHandleClasses = [];
2204
2205         this.applyConfig();
2206     },
2207
2208     /**
2209      * Applies the configuration parameters that were passed into the constructor.
2210      * This is supposed to happen at each level through the inheritance chain.  So
2211      * a DDProxy implentation will execute apply config on DDProxy, DD, and 
2212      * DragDrop in order to get all of the parameters that are available in
2213      * each object.
2214      * @method applyConfig
2215      */
2216     applyConfig: function() {
2217         this.events = {
2218             mouseDown: true,
2219             b4MouseDown: true,
2220             mouseUp: true,
2221             b4StartDrag: true,
2222             startDrag: true,
2223             b4EndDrag: true,
2224             endDrag: true,
2225             drag: true,
2226             b4Drag: true,
2227             invalidDrop: true,
2228             b4DragOut: true,
2229             dragOut: true,
2230             dragEnter: true,
2231             b4DragOver: true,
2232             dragOver: true,
2233             b4DragDrop: true,
2234             dragDrop: true
2235         };
2236         
2237         if (this.config.events) {
2238             for (var i in this.config.events) {
2239                 if (this.config.events[i] === false) {
2240                     this.events[i] = false;
2241                 }
2242             }
2243         }
2244
2245
2246         // configurable properties: 
2247         //    padding, isTarget, maintainOffset, primaryButtonOnly
2248         this.padding           = this.config.padding || [0, 0, 0, 0];
2249         this.isTarget          = (this.config.isTarget !== false);
2250         this.maintainOffset    = (this.config.maintainOffset);
2251         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
2252         this.dragOnly = ((this.config.dragOnly === true) ? true : false);
2253         this.useShim = ((this.config.useShim === true) ? true : false);
2254     },
2255
2256     /**
2257      * Executed when the linked element is available
2258      * @method handleOnAvailable
2259      * @private
2260      */
2261     handleOnAvailable: function() {
2262         //this.logger.log("handleOnAvailable");
2263         this.available = true;
2264         this.resetConstraints();
2265         this.onAvailable();
2266     },
2267
2268      /**
2269      * Configures the padding for the target zone in px.  Effectively expands
2270      * (or reduces) the virtual object size for targeting calculations.  
2271      * Supports css-style shorthand; if only one parameter is passed, all sides
2272      * will have that padding, and if only two are passed, the top and bottom
2273      * will have the first param, the left and right the second.
2274      * @method setPadding
2275      * @param {int} iTop    Top pad
2276      * @param {int} iRight  Right pad
2277      * @param {int} iBot    Bot pad
2278      * @param {int} iLeft   Left pad
2279      */
2280     setPadding: function(iTop, iRight, iBot, iLeft) {
2281         // this.padding = [iLeft, iRight, iTop, iBot];
2282         if (!iRight && 0 !== iRight) {
2283             this.padding = [iTop, iTop, iTop, iTop];
2284         } else if (!iBot && 0 !== iBot) {
2285             this.padding = [iTop, iRight, iTop, iRight];
2286         } else {
2287             this.padding = [iTop, iRight, iBot, iLeft];
2288         }
2289     },
2290
2291     /**
2292      * Stores the initial placement of the linked element.
2293      * @method setInitialPosition
2294      * @param {int} diffX   the X offset, default 0
2295      * @param {int} diffY   the Y offset, default 0
2296      * @private
2297      */
2298     setInitPosition: function(diffX, diffY) {
2299         var el = this.getEl();
2300
2301         if (!this.DDM.verifyEl(el)) {
2302             if (el && el.style && (el.style.display == 'none')) {
2303                 this.logger.log(this.id + " can not get initial position, element style is display: none");
2304             } else {
2305                 this.logger.log(this.id + " element is broken");
2306             }
2307             return;
2308         }
2309
2310         var dx = diffX || 0;
2311         var dy = diffY || 0;
2312
2313         var p = Dom.getXY( el );
2314
2315         this.initPageX = p[0] - dx;
2316         this.initPageY = p[1] - dy;
2317
2318         this.lastPageX = p[0];
2319         this.lastPageY = p[1];
2320
2321         this.logger.log(this.id + " initial position: " + this.initPageX + 
2322                 ", " + this.initPageY);
2323
2324
2325         this.setStartPosition(p);
2326     },
2327
2328     /**
2329      * Sets the start position of the element.  This is set when the obj
2330      * is initialized, the reset when a drag is started.
2331      * @method setStartPosition
2332      * @param pos current position (from previous lookup)
2333      * @private
2334      */
2335     setStartPosition: function(pos) {
2336         var p = pos || Dom.getXY(this.getEl());
2337
2338         this.deltaSetXY = null;
2339
2340         this.startPageX = p[0];
2341         this.startPageY = p[1];
2342     },
2343
2344     /**
2345      * Add this instance to a group of related drag/drop objects.  All 
2346      * instances belong to at least one group, and can belong to as many 
2347      * groups as needed.
2348      * @method addToGroup
2349      * @param sGroup {string} the name of the group
2350      */
2351     addToGroup: function(sGroup) {
2352         this.groups[sGroup] = true;
2353         this.DDM.regDragDrop(this, sGroup);
2354     },
2355
2356     /**
2357      * Remove's this instance from the supplied interaction group
2358      * @method removeFromGroup
2359      * @param {string}  sGroup  The group to drop
2360      */
2361     removeFromGroup: function(sGroup) {
2362         this.logger.log("Removing from group: " + sGroup);
2363         if (this.groups[sGroup]) {
2364             delete this.groups[sGroup];
2365         }
2366
2367         this.DDM.removeDDFromGroup(this, sGroup);
2368     },
2369
2370     /**
2371      * Allows you to specify that an element other than the linked element 
2372      * will be moved with the cursor during a drag
2373      * @method setDragElId
2374      * @param id {string} the id of the element that will be used to initiate the drag
2375      */
2376     setDragElId: function(id) {
2377         this.dragElId = id;
2378     },
2379
2380     /**
2381      * Allows you to specify a child of the linked element that should be 
2382      * used to initiate the drag operation.  An example of this would be if 
2383      * you have a content div with text and links.  Clicking anywhere in the 
2384      * content area would normally start the drag operation.  Use this method
2385      * to specify that an element inside of the content div is the element 
2386      * that starts the drag operation.
2387      * @method setHandleElId
2388      * @param id {string} the id of the element that will be used to 
2389      * initiate the drag.
2390      */
2391     setHandleElId: function(id) {
2392         if (typeof id !== "string") {
2393             YAHOO.log("id is not a string, assuming it is an HTMLElement");
2394             id = Dom.generateId(id);
2395         }
2396         this.handleElId = id;
2397         this.DDM.regHandle(this.id, id);
2398     },
2399
2400     /**
2401      * Allows you to set an element outside of the linked element as a drag 
2402      * handle
2403      * @method setOuterHandleElId
2404      * @param id the id of the element that will be used to initiate the drag
2405      */
2406     setOuterHandleElId: function(id) {
2407         if (typeof id !== "string") {
2408             YAHOO.log("id is not a string, assuming it is an HTMLElement");
2409             id = Dom.generateId(id);
2410         }
2411         this.logger.log("Adding outer handle event: " + id);
2412         Event.on(id, "mousedown", 
2413                 this.handleMouseDown, this, true);
2414         this.setHandleElId(id);
2415
2416         this.hasOuterHandles = true;
2417     },
2418
2419     /**
2420      * Remove all drag and drop hooks for this element
2421      * @method unreg
2422      */
2423     unreg: function() {
2424         this.logger.log("DragDrop obj cleanup " + this.id);
2425         Event.removeListener(this.id, "mousedown", 
2426                 this.handleMouseDown);
2427         this._domRef = null;
2428         this.DDM._remove(this);
2429     },
2430
2431     /**
2432      * Returns true if this instance is locked, or the drag drop mgr is locked
2433      * (meaning that all drag/drop is disabled on the page.)
2434      * @method isLocked
2435      * @return {boolean} true if this obj or all drag/drop is locked, else 
2436      * false
2437      */
2438     isLocked: function() {
2439         return (this.DDM.isLocked() || this.locked);
2440     },
2441
2442     /**
2443      * Fired when this object is clicked
2444      * @method handleMouseDown
2445      * @param {Event} e 
2446      * @param {YAHOO.util.DragDrop} oDD the clicked dd object (this dd obj)
2447      * @private
2448      */
2449     handleMouseDown: function(e, oDD) {
2450
2451         var button = e.which || e.button;
2452         this.logger.log("button: " + button);
2453
2454         if (this.primaryButtonOnly && button > 1) {
2455             this.logger.log("Mousedown was not produced by the primary button");
2456             return;
2457         }
2458
2459         if (this.isLocked()) {
2460             this.logger.log("Drag and drop is disabled, aborting");
2461             return;
2462         }
2463
2464         this.logger.log("mousedown " + this.id);
2465
2466         this.logger.log("firing onMouseDown events");
2467
2468         // firing the mousedown events prior to calculating positions
2469         var b4Return = this.b4MouseDown(e),
2470         b4Return2 = true;
2471
2472         if (this.events.b4MouseDown) {
2473             b4Return2 = this.fireEvent('b4MouseDownEvent', e);
2474         }
2475         var mDownReturn = this.onMouseDown(e),
2476             mDownReturn2 = true;
2477         if (this.events.mouseDown) {
2478             mDownReturn2 = this.fireEvent('mouseDownEvent', e);
2479         }
2480
2481         if ((b4Return === false) || (mDownReturn === false) || (b4Return2 === false) || (mDownReturn2 === false)) {
2482             this.logger.log('b4MouseDown or onMouseDown returned false, exiting drag');
2483             return;
2484         }
2485
2486         this.DDM.refreshCache(this.groups);
2487         // var self = this;
2488         // setTimeout( function() { self.DDM.refreshCache(self.groups); }, 0);
2489
2490         // Only process the event if we really clicked within the linked 
2491         // element.  The reason we make this check is that in the case that 
2492         // another element was moved between the clicked element and the 
2493         // cursor in the time between the mousedown and mouseup events. When 
2494         // this happens, the element gets the next mousedown event 
2495         // regardless of where on the screen it happened.  
2496         var pt = new YAHOO.util.Point(Event.getPageX(e), Event.getPageY(e));
2497         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
2498                 this.logger.log("Click was not over the element: " + this.id);
2499         } else {
2500             if (this.clickValidator(e)) {
2501
2502                 this.logger.log("click was a valid handle");
2503
2504                 // set the initial element position
2505                 this.setStartPosition();
2506
2507                 // start tracking mousemove distance and mousedown time to
2508                 // determine when to start the actual drag
2509                 this.DDM.handleMouseDown(e, this);
2510
2511                 // this mousedown is mine
2512                 this.DDM.stopEvent(e);
2513             } else {
2514
2515 this.logger.log("clickValidator returned false, drag not initiated");
2516
2517             }
2518         }
2519     },
2520
2521     /**
2522      * @method clickValidator
2523      * @description Method validates that the clicked element
2524      * was indeed the handle or a valid child of the handle
2525      * @param {Event} e 
2526      */
2527     clickValidator: function(e) {
2528         var target = YAHOO.util.Event.getTarget(e);
2529         return ( this.isValidHandleChild(target) &&
2530                     (this.id == this.handleElId || 
2531                         this.DDM.handleWasClicked(target, this.id)) );
2532     },
2533
2534     /**
2535      * Finds the location the element should be placed if we want to move
2536      * it to where the mouse location less the click offset would place us.
2537      * @method getTargetCoord
2538      * @param {int} iPageX the X coordinate of the click
2539      * @param {int} iPageY the Y coordinate of the click
2540      * @return an object that contains the coordinates (Object.x and Object.y)
2541      * @private
2542      */
2543     getTargetCoord: function(iPageX, iPageY) {
2544
2545         // this.logger.log("getTargetCoord: " + iPageX + ", " + iPageY);
2546
2547         var x = iPageX - this.deltaX;
2548         var y = iPageY - this.deltaY;
2549
2550         if (this.constrainX) {
2551             if (x < this.minX) { x = this.minX; }
2552             if (x > this.maxX) { x = this.maxX; }
2553         }
2554
2555         if (this.constrainY) {
2556             if (y < this.minY) { y = this.minY; }
2557             if (y > this.maxY) { y = this.maxY; }
2558         }
2559
2560         x = this.getTick(x, this.xTicks);
2561         y = this.getTick(y, this.yTicks);
2562
2563         // this.logger.log("getTargetCoord " + 
2564                 // " iPageX: " + iPageX +
2565                 // " iPageY: " + iPageY +
2566                 // " x: " + x + ", y: " + y);
2567
2568         return {x:x, y:y};
2569     },
2570
2571     /**
2572      * Allows you to specify a tag name that should not start a drag operation
2573      * when clicked.  This is designed to facilitate embedding links within a
2574      * drag handle that do something other than start the drag.
2575      * @method addInvalidHandleType
2576      * @param {string} tagName the type of element to exclude
2577      */
2578     addInvalidHandleType: function(tagName) {
2579         var type = tagName.toUpperCase();
2580         this.invalidHandleTypes[type] = type;
2581     },
2582
2583     /**
2584      * Lets you to specify an element id for a child of a drag handle
2585      * that should not initiate a drag
2586      * @method addInvalidHandleId
2587      * @param {string} id the element id of the element you wish to ignore
2588      */
2589     addInvalidHandleId: function(id) {
2590         if (typeof id !== "string") {
2591             YAHOO.log("id is not a string, assuming it is an HTMLElement");
2592             id = Dom.generateId(id);
2593         }
2594         this.invalidHandleIds[id] = id;
2595     },
2596
2597
2598     /**
2599      * Lets you specify a css class of elements that will not initiate a drag
2600      * @method addInvalidHandleClass
2601      * @param {string} cssClass the class of the elements you wish to ignore
2602      */
2603     addInvalidHandleClass: function(cssClass) {
2604         this.invalidHandleClasses.push(cssClass);
2605     },
2606
2607     /**
2608      * Unsets an excluded tag name set by addInvalidHandleType
2609      * @method removeInvalidHandleType
2610      * @param {string} tagName the type of element to unexclude
2611      */
2612     removeInvalidHandleType: function(tagName) {
2613         var type = tagName.toUpperCase();
2614         // this.invalidHandleTypes[type] = null;
2615         delete this.invalidHandleTypes[type];
2616     },
2617     
2618     /**
2619      * Unsets an invalid handle id
2620      * @method removeInvalidHandleId
2621      * @param {string} id the id of the element to re-enable
2622      */
2623     removeInvalidHandleId: function(id) {
2624         if (typeof id !== "string") {
2625             YAHOO.log("id is not a string, assuming it is an HTMLElement");
2626             id = Dom.generateId(id);
2627         }
2628         delete this.invalidHandleIds[id];
2629     },
2630
2631     /**
2632      * Unsets an invalid css class
2633      * @method removeInvalidHandleClass
2634      * @param {string} cssClass the class of the element(s) you wish to 
2635      * re-enable
2636      */
2637     removeInvalidHandleClass: function(cssClass) {
2638         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
2639             if (this.invalidHandleClasses[i] == cssClass) {
2640                 delete this.invalidHandleClasses[i];
2641             }
2642         }
2643     },
2644
2645     /**
2646      * Checks the tag exclusion list to see if this click should be ignored
2647      * @method isValidHandleChild
2648      * @param {HTMLElement} node the HTMLElement to evaluate
2649      * @return {boolean} true if this is a valid tag type, false if not
2650      */
2651     isValidHandleChild: function(node) {
2652
2653         var valid = true;
2654         // var n = (node.nodeName == "#text") ? node.parentNode : node;
2655         var nodeName;
2656         try {
2657             nodeName = node.nodeName.toUpperCase();
2658         } catch(e) {
2659             nodeName = node.nodeName;
2660         }
2661         valid = valid && !this.invalidHandleTypes[nodeName];
2662         valid = valid && !this.invalidHandleIds[node.id];
2663
2664         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
2665             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
2666         }
2667
2668         this.logger.log("Valid handle? ... " + valid);
2669
2670         return valid;
2671
2672     },
2673
2674     /**
2675      * Create the array of horizontal tick marks if an interval was specified
2676      * in setXConstraint().
2677      * @method setXTicks
2678      * @private
2679      */
2680     setXTicks: function(iStartX, iTickSize) {
2681         this.xTicks = [];
2682         this.xTickSize = iTickSize;
2683         
2684         var tickMap = {};
2685
2686         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
2687             if (!tickMap[i]) {
2688                 this.xTicks[this.xTicks.length] = i;
2689                 tickMap[i] = true;
2690             }
2691         }
2692
2693         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
2694             if (!tickMap[i]) {
2695                 this.xTicks[this.xTicks.length] = i;
2696                 tickMap[i] = true;
2697             }
2698         }
2699
2700         this.xTicks.sort(this.DDM.numericSort) ;
2701         this.logger.log("xTicks: " + this.xTicks.join());
2702     },
2703
2704     /**
2705      * Create the array of vertical tick marks if an interval was specified in 
2706      * setYConstraint().
2707      * @method setYTicks
2708      * @private
2709      */
2710     setYTicks: function(iStartY, iTickSize) {
2711         // this.logger.log("setYTicks: " + iStartY + ", " + iTickSize
2712                // + ", " + this.initPageY + ", " + this.minY + ", " + this.maxY );
2713         this.yTicks = [];
2714         this.yTickSize = iTickSize;
2715
2716         var tickMap = {};
2717
2718         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
2719             if (!tickMap[i]) {
2720                 this.yTicks[this.yTicks.length] = i;
2721                 tickMap[i] = true;
2722             }
2723         }
2724
2725         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
2726             if (!tickMap[i]) {
2727                 this.yTicks[this.yTicks.length] = i;
2728                 tickMap[i] = true;
2729             }
2730         }
2731
2732         this.yTicks.sort(this.DDM.numericSort) ;
2733         this.logger.log("yTicks: " + this.yTicks.join());
2734     },
2735
2736     /**
2737      * By default, the element can be dragged any place on the screen.  Use 
2738      * this method to limit the horizontal travel of the element.  Pass in 
2739      * 0,0 for the parameters if you want to lock the drag to the y axis.
2740      * @method setXConstraint
2741      * @param {int} iLeft the number of pixels the element can move to the left
2742      * @param {int} iRight the number of pixels the element can move to the 
2743      * right
2744      * @param {int} iTickSize optional parameter for specifying that the 
2745      * element
2746      * should move iTickSize pixels at a time.
2747      */
2748     setXConstraint: function(iLeft, iRight, iTickSize) {
2749         this.leftConstraint = parseInt(iLeft, 10);
2750         this.rightConstraint = parseInt(iRight, 10);
2751
2752         this.minX = this.initPageX - this.leftConstraint;
2753         this.maxX = this.initPageX + this.rightConstraint;
2754         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
2755
2756         this.constrainX = true;
2757         this.logger.log("initPageX:" + this.initPageX + " minX:" + this.minX + 
2758                 " maxX:" + this.maxX);
2759     },
2760
2761     /**
2762      * Clears any constraints applied to this instance.  Also clears ticks
2763      * since they can't exist independent of a constraint at this time.
2764      * @method clearConstraints
2765      */
2766     clearConstraints: function() {
2767         this.logger.log("Clearing constraints");
2768         this.constrainX = false;
2769         this.constrainY = false;
2770         this.clearTicks();
2771     },
2772
2773     /**
2774      * Clears any tick interval defined for this instance
2775      * @method clearTicks
2776      */
2777     clearTicks: function() {
2778         this.logger.log("Clearing ticks");
2779         this.xTicks = null;
2780         this.yTicks = null;
2781         this.xTickSize = 0;
2782         this.yTickSize = 0;
2783     },
2784
2785     /**
2786      * By default, the element can be dragged any place on the screen.  Set 
2787      * this to limit the vertical travel of the element.  Pass in 0,0 for the
2788      * parameters if you want to lock the drag to the x axis.
2789      * @method setYConstraint
2790      * @param {int} iUp the number of pixels the element can move up
2791      * @param {int} iDown the number of pixels the element can move down
2792      * @param {int} iTickSize optional parameter for specifying that the 
2793      * element should move iTickSize pixels at a time.
2794      */
2795     setYConstraint: function(iUp, iDown, iTickSize) {
2796         this.logger.log("setYConstraint: " + iUp + "," + iDown + "," + iTickSize);
2797         this.topConstraint = parseInt(iUp, 10);
2798         this.bottomConstraint = parseInt(iDown, 10);
2799
2800         this.minY = this.initPageY - this.topConstraint;
2801         this.maxY = this.initPageY + this.bottomConstraint;
2802         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
2803
2804         this.constrainY = true;
2805         
2806         this.logger.log("initPageY:" + this.initPageY + " minY:" + this.minY + 
2807                 " maxY:" + this.maxY);
2808     },
2809
2810     /**
2811      * resetConstraints must be called if you manually reposition a dd element.
2812      * @method resetConstraints
2813      */
2814     resetConstraints: function() {
2815
2816         //this.logger.log("resetConstraints");
2817
2818         // Maintain offsets if necessary
2819         if (this.initPageX || this.initPageX === 0) {
2820             //this.logger.log("init pagexy: " + this.initPageX + ", " + 
2821                                //this.initPageY);
2822             //this.logger.log("last pagexy: " + this.lastPageX + ", " + 
2823                                //this.lastPageY);
2824             // figure out how much this thing has moved
2825             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
2826             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
2827
2828             this.setInitPosition(dx, dy);
2829
2830         // This is the first time we have detected the element's position
2831         } else {
2832             this.setInitPosition();
2833         }
2834
2835         if (this.constrainX) {
2836             this.setXConstraint( this.leftConstraint, 
2837                                  this.rightConstraint, 
2838                                  this.xTickSize        );
2839         }
2840
2841         if (this.constrainY) {
2842             this.setYConstraint( this.topConstraint, 
2843                                  this.bottomConstraint, 
2844                                  this.yTickSize         );
2845         }
2846     },
2847
2848     /**
2849      * Normally the drag element is moved pixel by pixel, but we can specify 
2850      * that it move a number of pixels at a time.  This method resolves the 
2851      * location when we have it set up like this.
2852      * @method getTick
2853      * @param {int} val where we want to place the object
2854      * @param {int[]} tickArray sorted array of valid points
2855      * @return {int} the closest tick
2856      * @private
2857      */
2858     getTick: function(val, tickArray) {
2859
2860         if (!tickArray) {
2861             // If tick interval is not defined, it is effectively 1 pixel, 
2862             // so we return the value passed to us.
2863             return val; 
2864         } else if (tickArray[0] >= val) {
2865             // The value is lower than the first tick, so we return the first
2866             // tick.
2867             return tickArray[0];
2868         } else {
2869             for (var i=0, len=tickArray.length; i<len; ++i) {
2870                 var next = i + 1;
2871                 if (tickArray[next] && tickArray[next] >= val) {
2872                     var diff1 = val - tickArray[i];
2873                     var diff2 = tickArray[next] - val;
2874                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
2875                 }
2876             }
2877
2878             // The value is larger than the last tick, so we return the last
2879             // tick.
2880             return tickArray[tickArray.length - 1];
2881         }
2882     },
2883
2884     /**
2885      * toString method
2886      * @method toString
2887      * @return {string} string representation of the dd obj
2888      */
2889     toString: function() {
2890         return ("DragDrop " + this.id);
2891     }
2892
2893 };
2894 YAHOO.augment(YAHOO.util.DragDrop, YAHOO.util.EventProvider);
2895
2896 /**
2897 * @event mouseDownEvent
2898 * @description Provides access to the mousedown event. The mousedown does not always result in a drag operation.
2899 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2900 */
2901
2902 /**
2903 * @event b4MouseDownEvent
2904 * @description Provides access to the mousedown event, before the mouseDownEvent gets fired. Returning false will cancel the drag.
2905 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2906 */
2907
2908 /**
2909 * @event mouseUpEvent
2910 * @description Fired from inside DragDropMgr when the drag operation is finished.
2911 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2912 */
2913
2914 /**
2915 * @event b4StartDragEvent
2916 * @description Fires before the startDragEvent, returning false will cancel the startDrag Event.
2917 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2918 */
2919
2920 /**
2921 * @event startDragEvent
2922 * @description Occurs after a mouse down and the drag threshold has been met. The drag threshold default is either 3 pixels of mouse movement or 1 full second of holding the mousedown. 
2923 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2924 */
2925
2926 /**
2927 * @event b4EndDragEvent
2928 * @description Fires before the endDragEvent. Returning false will cancel.
2929 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2930 */
2931
2932 /**
2933 * @event endDragEvent
2934 * @description Fires on the mouseup event after a drag has been initiated (startDrag fired).
2935 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2936 */
2937
2938 /**
2939 * @event dragEvent
2940 * @description Occurs every mousemove event while dragging.
2941 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2942 */
2943 /**
2944 * @event b4DragEvent
2945 * @description Fires before the dragEvent.
2946 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2947 */
2948 /**
2949 * @event invalidDropEvent
2950 * @description Fires when the dragged objects is dropped in a location that contains no drop targets.
2951 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2952 */
2953 /**
2954 * @event b4DragOutEvent
2955 * @description Fires before the dragOutEvent
2956 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2957 */
2958 /**
2959 * @event dragOutEvent
2960 * @description Fires when a dragged object is no longer over an object that had the onDragEnter fire. 
2961 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2962 */
2963 /**
2964 * @event dragEnterEvent
2965 * @description Occurs when the dragged object first interacts with another targettable drag and drop object.
2966 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2967 */
2968 /**
2969 * @event b4DragOverEvent
2970 * @description Fires before the dragOverEvent.
2971 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2972 */
2973 /**
2974 * @event dragOverEvent
2975 * @description Fires every mousemove event while over a drag and drop object.
2976 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2977 */
2978 /**
2979 * @event b4DragDropEvent 
2980 * @description Fires before the dragDropEvent
2981 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2982 */
2983 /**
2984 * @event dragDropEvent
2985 * @description Fires when the dragged objects is dropped on another.
2986 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2987 */
2988 })();
2989 /**
2990  * A DragDrop implementation where the linked element follows the 
2991  * mouse cursor during a drag.
2992  * @class DD
2993  * @extends YAHOO.util.DragDrop
2994  * @constructor
2995  * @param {String} id the id of the linked element 
2996  * @param {String} sGroup the group of related DragDrop items
2997  * @param {object} config an object containing configurable attributes
2998  *                Valid properties for DD: 
2999  *                    scroll
3000  */
3001 YAHOO.util.DD = function(id, sGroup, config) {
3002     if (id) {
3003         this.init(id, sGroup, config);
3004     }
3005 };
3006
3007 YAHOO.extend(YAHOO.util.DD, YAHOO.util.DragDrop, {
3008
3009     /**
3010      * When set to true, the utility automatically tries to scroll the browser
3011      * window when a drag and drop element is dragged near the viewport boundary.
3012      * Defaults to true.
3013      * @property scroll
3014      * @type boolean
3015      */
3016     scroll: true, 
3017
3018     /**
3019      * Sets the pointer offset to the distance between the linked element's top 
3020      * left corner and the location the element was clicked
3021      * @method autoOffset
3022      * @param {int} iPageX the X coordinate of the click
3023      * @param {int} iPageY the Y coordinate of the click
3024      */
3025     autoOffset: function(iPageX, iPageY) {
3026         var x = iPageX - this.startPageX;
3027         var y = iPageY - this.startPageY;
3028         this.setDelta(x, y);
3029         // this.logger.log("autoOffset el pos: " + aCoord + ", delta: " + x + "," + y);
3030     },
3031
3032     /** 
3033      * Sets the pointer offset.  You can call this directly to force the 
3034      * offset to be in a particular location (e.g., pass in 0,0 to set it 
3035      * to the center of the object, as done in YAHOO.widget.Slider)
3036      * @method setDelta
3037      * @param {int} iDeltaX the distance from the left
3038      * @param {int} iDeltaY the distance from the top
3039      */
3040     setDelta: function(iDeltaX, iDeltaY) {
3041         this.deltaX = iDeltaX;
3042         this.deltaY = iDeltaY;
3043         this.logger.log("deltaX:" + this.deltaX + ", deltaY:" + this.deltaY);
3044     },
3045
3046     /**
3047      * Sets the drag element to the location of the mousedown or click event, 
3048      * maintaining the cursor location relative to the location on the element 
3049      * that was clicked.  Override this if you want to place the element in a 
3050      * location other than where the cursor is.
3051      * @method setDragElPos
3052      * @param {int} iPageX the X coordinate of the mousedown or drag event
3053      * @param {int} iPageY the Y coordinate of the mousedown or drag event
3054      */
3055     setDragElPos: function(iPageX, iPageY) {
3056         // the first time we do this, we are going to check to make sure
3057         // the element has css positioning
3058
3059         var el = this.getDragEl();
3060         this.alignElWithMouse(el, iPageX, iPageY);
3061     },
3062
3063     /**
3064      * Sets the element to the location of the mousedown or click event, 
3065      * maintaining the cursor location relative to the location on the element 
3066      * that was clicked.  Override this if you want to place the element in a 
3067      * location other than where the cursor is.
3068      * @method alignElWithMouse
3069      * @param {HTMLElement} el the element to move
3070      * @param {int} iPageX the X coordinate of the mousedown or drag event
3071      * @param {int} iPageY the Y coordinate of the mousedown or drag event
3072      */
3073     alignElWithMouse: function(el, iPageX, iPageY) {
3074         var oCoord = this.getTargetCoord(iPageX, iPageY);
3075         // this.logger.log("****alignElWithMouse : " + el.id + ", " + aCoord + ", " + el.style.display);
3076
3077         if (!this.deltaSetXY) {
3078             var aCoord = [oCoord.x, oCoord.y];
3079             YAHOO.util.Dom.setXY(el, aCoord);
3080
3081             var newLeft = parseInt( YAHOO.util.Dom.getStyle(el, "left"), 10 );
3082             var newTop  = parseInt( YAHOO.util.Dom.getStyle(el, "top" ), 10 );
3083
3084             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
3085         } else {
3086             YAHOO.util.Dom.setStyle(el, "left", (oCoord.x + this.deltaSetXY[0]) + "px");
3087             YAHOO.util.Dom.setStyle(el, "top",  (oCoord.y + this.deltaSetXY[1]) + "px");
3088         }
3089         
3090         this.cachePosition(oCoord.x, oCoord.y);
3091         var self = this;
3092         setTimeout(function() {
3093             self.autoScroll.call(self, oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
3094         }, 0);
3095     },
3096
3097     /**
3098      * Saves the most recent position so that we can reset the constraints and
3099      * tick marks on-demand.  We need to know this so that we can calculate the
3100      * number of pixels the element is offset from its original position.
3101      * @method cachePosition
3102      * @param iPageX the current x position (optional, this just makes it so we
3103      * don't have to look it up again)
3104      * @param iPageY the current y position (optional, this just makes it so we
3105      * don't have to look it up again)
3106      */
3107     cachePosition: function(iPageX, iPageY) {
3108         if (iPageX) {
3109             this.lastPageX = iPageX;
3110             this.lastPageY = iPageY;
3111         } else {
3112             var aCoord = YAHOO.util.Dom.getXY(this.getEl());
3113             this.lastPageX = aCoord[0];
3114             this.lastPageY = aCoord[1];
3115         }
3116     },
3117
3118     /**
3119      * Auto-scroll the window if the dragged object has been moved beyond the 
3120      * visible window boundary.
3121      * @method autoScroll
3122      * @param {int} x the drag element's x position
3123      * @param {int} y the drag element's y position
3124      * @param {int} h the height of the drag element
3125      * @param {int} w the width of the drag element
3126      * @private
3127      */
3128     autoScroll: function(x, y, h, w) {
3129
3130         if (this.scroll) {
3131             // The client height
3132             var clientH = this.DDM.getClientHeight();
3133
3134             // The client width
3135             var clientW = this.DDM.getClientWidth();
3136
3137             // The amt scrolled down
3138             var st = this.DDM.getScrollTop();
3139
3140             // The amt scrolled right
3141             var sl = this.DDM.getScrollLeft();
3142
3143             // Location of the bottom of the element
3144             var bot = h + y;
3145
3146             // Location of the right of the element
3147             var right = w + x;
3148
3149             // The distance from the cursor to the bottom of the visible area, 
3150             // adjusted so that we don't scroll if the cursor is beyond the
3151             // element drag constraints
3152             var toBot = (clientH + st - y - this.deltaY);
3153
3154             // The distance from the cursor to the right of the visible area
3155             var toRight = (clientW + sl - x - this.deltaX);
3156
3157             // this.logger.log( " x: " + x + " y: " + y + " h: " + h + 
3158             // " clientH: " + clientH + " clientW: " + clientW + 
3159             // " st: " + st + " sl: " + sl + " bot: " + bot + 
3160             // " right: " + right + " toBot: " + toBot + " toRight: " + toRight);
3161
3162             // How close to the edge the cursor must be before we scroll
3163             // var thresh = (document.all) ? 100 : 40;
3164             var thresh = 40;
3165
3166             // How many pixels to scroll per autoscroll op.  This helps to reduce 
3167             // clunky scrolling. IE is more sensitive about this ... it needs this 
3168             // value to be higher.
3169             var scrAmt = (document.all) ? 80 : 30;
3170
3171             // Scroll down if we are near the bottom of the visible page and the 
3172             // obj extends below the crease
3173             if ( bot > clientH && toBot < thresh ) { 
3174                 window.scrollTo(sl, st + scrAmt); 
3175             }
3176
3177             // Scroll up if the window is scrolled down and the top of the object
3178             // goes above the top border
3179             if ( y < st && st > 0 && y - st < thresh ) { 
3180                 window.scrollTo(sl, st - scrAmt); 
3181             }
3182
3183             // Scroll right if the obj is beyond the right border and the cursor is
3184             // near the border.
3185             if ( right > clientW && toRight < thresh ) { 
3186                 window.scrollTo(sl + scrAmt, st); 
3187             }
3188
3189             // Scroll left if the window has been scrolled to the right and the obj
3190             // extends past the left border
3191             if ( x < sl && sl > 0 && x - sl < thresh ) { 
3192                 window.scrollTo(sl - scrAmt, st);
3193             }
3194         }
3195     },
3196
3197     /*
3198      * Sets up config options specific to this class. Overrides
3199      * YAHOO.util.DragDrop, but all versions of this method through the 
3200      * inheritance chain are called
3201      */
3202     applyConfig: function() {
3203         YAHOO.util.DD.superclass.applyConfig.call(this);
3204         this.scroll = (this.config.scroll !== false);
3205     },
3206
3207     /*
3208      * Event that fires prior to the onMouseDown event.  Overrides 
3209      * YAHOO.util.DragDrop.
3210      */
3211     b4MouseDown: function(e) {
3212         this.setStartPosition();
3213         // this.resetConstraints();
3214         this.autoOffset(YAHOO.util.Event.getPageX(e), 
3215                             YAHOO.util.Event.getPageY(e));
3216     },
3217
3218     /*
3219      * Event that fires prior to the onDrag event.  Overrides 
3220      * YAHOO.util.DragDrop.
3221      */
3222     b4Drag: function(e) {
3223         this.setDragElPos(YAHOO.util.Event.getPageX(e), 
3224                             YAHOO.util.Event.getPageY(e));
3225     },
3226
3227     toString: function() {
3228         return ("DD " + this.id);
3229     }
3230
3231     //////////////////////////////////////////////////////////////////////////
3232     // Debugging ygDragDrop events that can be overridden
3233     //////////////////////////////////////////////////////////////////////////
3234     /*
3235     startDrag: function(x, y) {
3236         this.logger.log(this.id.toString()  + " startDrag");
3237     },
3238
3239     onDrag: function(e) {
3240         this.logger.log(this.id.toString() + " onDrag");
3241     },
3242
3243     onDragEnter: function(e, id) {
3244         this.logger.log(this.id.toString() + " onDragEnter: " + id);
3245     },
3246
3247     onDragOver: function(e, id) {
3248         this.logger.log(this.id.toString() + " onDragOver: " + id);
3249     },
3250
3251     onDragOut: function(e, id) {
3252         this.logger.log(this.id.toString() + " onDragOut: " + id);
3253     },
3254
3255     onDragDrop: function(e, id) {
3256         this.logger.log(this.id.toString() + " onDragDrop: " + id);
3257     },
3258
3259     endDrag: function(e) {
3260         this.logger.log(this.id.toString() + " endDrag");
3261     }
3262
3263     */
3264
3265 /**
3266 * @event mouseDownEvent
3267 * @description Provides access to the mousedown event. The mousedown does not always result in a drag operation.
3268 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3269 */
3270
3271 /**
3272 * @event b4MouseDownEvent
3273 * @description Provides access to the mousedown event, before the mouseDownEvent gets fired. Returning false will cancel the drag.
3274 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3275 */
3276
3277 /**
3278 * @event mouseUpEvent
3279 * @description Fired from inside DragDropMgr when the drag operation is finished.
3280 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3281 */
3282
3283 /**
3284 * @event b4StartDragEvent
3285 * @description Fires before the startDragEvent, returning false will cancel the startDrag Event.
3286 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3287 */
3288
3289 /**
3290 * @event startDragEvent
3291 * @description Occurs after a mouse down and the drag threshold has been met. The drag threshold default is either 3 pixels of mouse movement or 1 full second of holding the mousedown. 
3292 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3293 */
3294
3295 /**
3296 * @event b4EndDragEvent
3297 * @description Fires before the endDragEvent. Returning false will cancel.
3298 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3299 */
3300
3301 /**
3302 * @event endDragEvent
3303 * @description Fires on the mouseup event after a drag has been initiated (startDrag fired).
3304 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3305 */
3306
3307 /**
3308 * @event dragEvent
3309 * @description Occurs every mousemove event while dragging.
3310 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3311 */
3312 /**
3313 * @event b4DragEvent
3314 * @description Fires before the dragEvent.
3315 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3316 */
3317 /**
3318 * @event invalidDropEvent
3319 * @description Fires when the dragged objects is dropped in a location that contains no drop targets.
3320 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3321 */
3322 /**
3323 * @event b4DragOutEvent
3324 * @description Fires before the dragOutEvent
3325 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3326 */
3327 /**
3328 * @event dragOutEvent
3329 * @description Fires when a dragged object is no longer over an object that had the onDragEnter fire. 
3330 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3331 */
3332 /**
3333 * @event dragEnterEvent
3334 * @description Occurs when the dragged object first interacts with another targettable drag and drop object.
3335 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3336 */
3337 /**
3338 * @event b4DragOverEvent
3339 * @description Fires before the dragOverEvent.
3340 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3341 */
3342 /**
3343 * @event dragOverEvent
3344 * @description Fires every mousemove event while over a drag and drop object.
3345 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3346 */
3347 /**
3348 * @event b4DragDropEvent 
3349 * @description Fires before the dragDropEvent
3350 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3351 */
3352 /**
3353 * @event dragDropEvent
3354 * @description Fires when the dragged objects is dropped on another.
3355 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3356 */
3357 });
3358 /**
3359  * A DragDrop implementation that inserts an empty, bordered div into
3360  * the document that follows the cursor during drag operations.  At the time of
3361  * the click, the frame div is resized to the dimensions of the linked html
3362  * element, and moved to the exact location of the linked element.
3363  *
3364  * References to the "frame" element refer to the single proxy element that
3365  * was created to be dragged in place of all DDProxy elements on the
3366  * page.
3367  *
3368  * @class DDProxy
3369  * @extends YAHOO.util.DD
3370  * @constructor
3371  * @param {String} id the id of the linked html element
3372  * @param {String} sGroup the group of related DragDrop objects
3373  * @param {object} config an object containing configurable attributes
3374  *                Valid properties for DDProxy in addition to those in DragDrop: 
3375  *                   resizeFrame, centerFrame, dragElId
3376  */
3377 YAHOO.util.DDProxy = function(id, sGroup, config) {
3378     if (id) {
3379         this.init(id, sGroup, config);
3380         this.initFrame(); 
3381     }
3382 };
3383
3384 /**
3385  * The default drag frame div id
3386  * @property YAHOO.util.DDProxy.dragElId
3387  * @type String
3388  * @static
3389  */
3390 YAHOO.util.DDProxy.dragElId = "ygddfdiv";
3391
3392 YAHOO.extend(YAHOO.util.DDProxy, YAHOO.util.DD, {
3393
3394     /**
3395      * By default we resize the drag frame to be the same size as the element
3396      * we want to drag (this is to get the frame effect).  We can turn it off
3397      * if we want a different behavior.
3398      * @property resizeFrame
3399      * @type boolean
3400      */
3401     resizeFrame: true,
3402
3403     /**
3404      * By default the frame is positioned exactly where the drag element is, so
3405      * we use the cursor offset provided by YAHOO.util.DD.  Another option that works only if
3406      * you do not have constraints on the obj is to have the drag frame centered
3407      * around the cursor.  Set centerFrame to true for this effect.
3408      * @property centerFrame
3409      * @type boolean
3410      */
3411     centerFrame: false,
3412
3413     /**
3414      * Creates the proxy element if it does not yet exist
3415      * @method createFrame
3416      */
3417     createFrame: function() {
3418         var self=this, body=document.body;
3419
3420         if (!body || !body.firstChild) {
3421             setTimeout( function() { self.createFrame(); }, 50 );
3422             return;
3423         }
3424
3425         var div=this.getDragEl(), Dom=YAHOO.util.Dom;
3426
3427         if (!div) {
3428             div    = document.createElement("div");
3429             div.id = this.dragElId;
3430             var s  = div.style;
3431
3432             s.position   = "absolute";
3433             s.visibility = "hidden";
3434             s.cursor     = "move";
3435             s.border     = "2px solid #aaa";
3436             s.zIndex     = 999;
3437             s.height     = "25px";
3438             s.width      = "25px";
3439
3440             var _data = document.createElement('div');
3441             Dom.setStyle(_data, 'height', '100%');
3442             Dom.setStyle(_data, 'width', '100%');
3443             /**
3444             * If the proxy element has no background-color, then it is considered to the "transparent" by Internet Explorer.
3445             * Since it is "transparent" then the events pass through it to the iframe below.
3446             * So creating a "fake" div inside the proxy element and giving it a background-color, then setting it to an
3447             * opacity of 0, it appears to not be there, however IE still thinks that it is so the events never pass through.
3448             */
3449             Dom.setStyle(_data, 'background-color', '#ccc');
3450             Dom.setStyle(_data, 'opacity', '0');
3451             div.appendChild(_data);
3452
3453             // appendChild can blow up IE if invoked prior to the window load event
3454             // while rendering a table.  It is possible there are other scenarios 
3455             // that would cause this to happen as well.
3456             body.insertBefore(div, body.firstChild);
3457         }
3458     },
3459
3460     /**
3461      * Initialization for the drag frame element.  Must be called in the
3462      * constructor of all subclasses
3463      * @method initFrame
3464      */
3465     initFrame: function() {
3466         this.createFrame();
3467     },
3468
3469     applyConfig: function() {
3470         //this.logger.log("DDProxy applyConfig");
3471         YAHOO.util.DDProxy.superclass.applyConfig.call(this);
3472
3473         this.resizeFrame = (this.config.resizeFrame !== false);
3474         this.centerFrame = (this.config.centerFrame);
3475         this.setDragElId(this.config.dragElId || YAHOO.util.DDProxy.dragElId);
3476     },
3477
3478     /**
3479      * Resizes the drag frame to the dimensions of the clicked object, positions 
3480      * it over the object, and finally displays it
3481      * @method showFrame
3482      * @param {int} iPageX X click position
3483      * @param {int} iPageY Y click position
3484      * @private
3485      */
3486     showFrame: function(iPageX, iPageY) {
3487         var el = this.getEl();
3488         var dragEl = this.getDragEl();
3489         var s = dragEl.style;
3490
3491         this._resizeProxy();
3492
3493         if (this.centerFrame) {
3494             this.setDelta( Math.round(parseInt(s.width,  10)/2), 
3495                            Math.round(parseInt(s.height, 10)/2) );
3496         }
3497
3498         this.setDragElPos(iPageX, iPageY);
3499
3500         YAHOO.util.Dom.setStyle(dragEl, "visibility", "visible"); 
3501     },
3502
3503     /**
3504      * The proxy is automatically resized to the dimensions of the linked
3505      * element when a drag is initiated, unless resizeFrame is set to false
3506      * @method _resizeProxy
3507      * @private
3508      */
3509     _resizeProxy: function() {
3510         if (this.resizeFrame) {
3511             var DOM    = YAHOO.util.Dom;
3512             var el     = this.getEl();
3513             var dragEl = this.getDragEl();
3514
3515             var bt = parseInt( DOM.getStyle(dragEl, "borderTopWidth"    ), 10);
3516             var br = parseInt( DOM.getStyle(dragEl, "borderRightWidth"  ), 10);
3517             var bb = parseInt( DOM.getStyle(dragEl, "borderBottomWidth" ), 10);
3518             var bl = parseInt( DOM.getStyle(dragEl, "borderLeftWidth"   ), 10);
3519
3520             if (isNaN(bt)) { bt = 0; }
3521             if (isNaN(br)) { br = 0; }
3522             if (isNaN(bb)) { bb = 0; }
3523             if (isNaN(bl)) { bl = 0; }
3524
3525             this.logger.log("proxy size: " + bt + "  " + br + " " + bb + " " + bl);
3526
3527             var newWidth  = Math.max(0, el.offsetWidth  - br - bl);                                                                                           
3528             var newHeight = Math.max(0, el.offsetHeight - bt - bb);
3529
3530             this.logger.log("Resizing proxy element");
3531
3532             DOM.setStyle( dragEl, "width",  newWidth  + "px" );
3533             DOM.setStyle( dragEl, "height", newHeight + "px" );
3534         }
3535     },
3536
3537     // overrides YAHOO.util.DragDrop
3538     b4MouseDown: function(e) {
3539         this.setStartPosition();
3540         var x = YAHOO.util.Event.getPageX(e);
3541         var y = YAHOO.util.Event.getPageY(e);
3542         this.autoOffset(x, y);
3543
3544         // This causes the autoscroll code to kick off, which means autoscroll can
3545         // happen prior to the check for a valid drag handle.
3546         // this.setDragElPos(x, y);
3547     },
3548
3549     // overrides YAHOO.util.DragDrop
3550     b4StartDrag: function(x, y) {
3551         // show the drag frame
3552         this.logger.log("start drag show frame, x: " + x + ", y: " + y);
3553         this.showFrame(x, y);
3554     },
3555
3556     // overrides YAHOO.util.DragDrop
3557     b4EndDrag: function(e) {
3558         this.logger.log(this.id + " b4EndDrag");
3559         YAHOO.util.Dom.setStyle(this.getDragEl(), "visibility", "hidden"); 
3560     },
3561
3562     // overrides YAHOO.util.DragDrop
3563     // By default we try to move the element to the last location of the frame.  
3564     // This is so that the default behavior mirrors that of YAHOO.util.DD.  
3565     endDrag: function(e) {
3566         var DOM = YAHOO.util.Dom;
3567         this.logger.log(this.id + " endDrag");
3568         var lel = this.getEl();
3569         var del = this.getDragEl();
3570
3571         // Show the drag frame briefly so we can get its position
3572         // del.style.visibility = "";
3573         DOM.setStyle(del, "visibility", ""); 
3574
3575         // Hide the linked element before the move to get around a Safari 
3576         // rendering bug.
3577         //lel.style.visibility = "hidden";
3578         DOM.setStyle(lel, "visibility", "hidden"); 
3579         YAHOO.util.DDM.moveToEl(lel, del);
3580         //del.style.visibility = "hidden";
3581         DOM.setStyle(del, "visibility", "hidden"); 
3582         //lel.style.visibility = "";
3583         DOM.setStyle(lel, "visibility", ""); 
3584     },
3585
3586     toString: function() {
3587         return ("DDProxy " + this.id);
3588     }
3589 /**
3590 * @event mouseDownEvent
3591 * @description Provides access to the mousedown event. The mousedown does not always result in a drag operation.
3592 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3593 */
3594
3595 /**
3596 * @event b4MouseDownEvent
3597 * @description Provides access to the mousedown event, before the mouseDownEvent gets fired. Returning false will cancel the drag.
3598 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3599 */
3600
3601 /**
3602 * @event mouseUpEvent
3603 * @description Fired from inside DragDropMgr when the drag operation is finished.
3604 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3605 */
3606
3607 /**
3608 * @event b4StartDragEvent
3609 * @description Fires before the startDragEvent, returning false will cancel the startDrag Event.
3610 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3611 */
3612
3613 /**
3614 * @event startDragEvent
3615 * @description Occurs after a mouse down and the drag threshold has been met. The drag threshold default is either 3 pixels of mouse movement or 1 full second of holding the mousedown. 
3616 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3617 */
3618
3619 /**
3620 * @event b4EndDragEvent
3621 * @description Fires before the endDragEvent. Returning false will cancel.
3622 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3623 */
3624
3625 /**
3626 * @event endDragEvent
3627 * @description Fires on the mouseup event after a drag has been initiated (startDrag fired).
3628 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3629 */
3630
3631 /**
3632 * @event dragEvent
3633 * @description Occurs every mousemove event while dragging.
3634 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3635 */
3636 /**
3637 * @event b4DragEvent
3638 * @description Fires before the dragEvent.
3639 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3640 */
3641 /**
3642 * @event invalidDropEvent
3643 * @description Fires when the dragged objects is dropped in a location that contains no drop targets.
3644 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3645 */
3646 /**
3647 * @event b4DragOutEvent
3648 * @description Fires before the dragOutEvent
3649 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3650 */
3651 /**
3652 * @event dragOutEvent
3653 * @description Fires when a dragged object is no longer over an object that had the onDragEnter fire. 
3654 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3655 */
3656 /**
3657 * @event dragEnterEvent
3658 * @description Occurs when the dragged object first interacts with another targettable drag and drop object.
3659 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3660 */
3661 /**
3662 * @event b4DragOverEvent
3663 * @description Fires before the dragOverEvent.
3664 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3665 */
3666 /**
3667 * @event dragOverEvent
3668 * @description Fires every mousemove event while over a drag and drop object.
3669 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3670 */
3671 /**
3672 * @event b4DragDropEvent 
3673 * @description Fires before the dragDropEvent
3674 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3675 */
3676 /**
3677 * @event dragDropEvent
3678 * @description Fires when the dragged objects is dropped on another.
3679 * @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3680 */
3681
3682 });
3683 /**
3684  * A DragDrop implementation that does not move, but can be a drop 
3685  * target.  You would get the same result by simply omitting implementation 
3686  * for the event callbacks, but this way we reduce the processing cost of the 
3687  * event listener and the callbacks.
3688  * @class DDTarget
3689  * @extends YAHOO.util.DragDrop 
3690  * @constructor
3691  * @param {String} id the id of the element that is a drop target
3692  * @param {String} sGroup the group of related DragDrop objects
3693  * @param {object} config an object containing configurable attributes
3694  *                 Valid properties for DDTarget in addition to those in 
3695  *                 DragDrop: 
3696  *                    none
3697  */
3698 YAHOO.util.DDTarget = function(id, sGroup, config) {
3699     if (id) {
3700         this.initTarget(id, sGroup, config);
3701     }
3702 };
3703
3704 // YAHOO.util.DDTarget.prototype = new YAHOO.util.DragDrop();
3705 YAHOO.extend(YAHOO.util.DDTarget, YAHOO.util.DragDrop, {
3706     toString: function() {
3707         return ("DDTarget " + this.id);
3708     }
3709 });
3710 YAHOO.register("dragdrop", YAHOO.util.DragDropMgr, {version: "2.8.0r4", build: "2449"});