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