MDL-11517 reserved word MOD used in table alias in questions backup code
[moodle-pu.git] / lib / yui / dragdrop / dragdrop-debug.js
bloba969e143ba6c6413e98901344554c756f847dd08
1 /*
2 Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 2.3.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  */
19 // Only load the library once.  Rewriting the manager class would orphan 
20 // existing drag and drop instances.
21 if (!YAHOO.util.DragDropMgr) {
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() {
33     var Event = YAHOO.util.Event;
35     return {
37         /**
38          * Two dimensional Array of registered DragDrop objects.  The first 
39          * dimension is the DragDrop item group, the second the DragDrop 
40          * object.
41          * @property ids
42          * @type {string: string}
43          * @private
44          * @static
45          */
46         ids: {},
48         /**
49          * Array of element ids defined as drag handles.  Used to determine 
50          * if the element that generated the mousedown event is actually the 
51          * handle and not the html element itself.
52          * @property handleIds
53          * @type {string: string}
54          * @private
55          * @static
56          */
57         handleIds: {},
59         /**
60          * the DragDrop object that is currently being dragged
61          * @property dragCurrent
62          * @type DragDrop
63          * @private
64          * @static
65          **/
66         dragCurrent: null,
68         /**
69          * the DragDrop object(s) that are being hovered over
70          * @property dragOvers
71          * @type Array
72          * @private
73          * @static
74          */
75         dragOvers: {},
77         /**
78          * the X distance between the cursor and the object being dragged
79          * @property deltaX
80          * @type int
81          * @private
82          * @static
83          */
84         deltaX: 0,
86         /**
87          * the Y distance between the cursor and the object being dragged
88          * @property deltaY
89          * @type int
90          * @private
91          * @static
92          */
93         deltaY: 0,
95         /**
96          * Flag to determine if we should prevent the default behavior of the
97          * events we define. By default this is true, but this can be set to 
98          * false if you need the default behavior (not recommended)
99          * @property preventDefault
100          * @type boolean
101          * @static
102          */
103         preventDefault: true,
105         /**
106          * Flag to determine if we should stop the propagation of the events 
107          * we generate. This is true by default but you may want to set it to
108          * false if the html element contains other features that require the
109          * mouse click.
110          * @property stopPropagation
111          * @type boolean
112          * @static
113          */
114         stopPropagation: true,
116         /**
117          * Internal flag that is set to true when drag and drop has been
118          * initialized
119          * @property initialized
120          * @private
121          * @static
122          */
123         initialized: false,
125         /**
126          * All drag and drop can be disabled.
127          * @property locked
128          * @private
129          * @static
130          */
131         locked: false,
133         /**
134          * Provides additional information about the the current set of
135          * interactions.  Can be accessed from the event handlers. It
136          * contains the following properties:
137          *
138          *       out:       onDragOut interactions
139          *       enter:     onDragEnter interactions
140          *       over:      onDragOver interactions
141          *       drop:      onDragDrop interactions
142          *       point:     The location of the cursor
143          *       draggedRegion: The location of dragged element at the time
144          *                      of the interaction
145          *       sourceRegion: The location of the source elemtn at the time
146          *                     of the interaction
147          *       validDrop: boolean
148          * @property interactionInfo
149          * @type object
150          * @static
151          */
152         interactionInfo: null,
154         /**
155          * Called the first time an element is registered.
156          * @method init
157          * @private
158          * @static
159          */
160         init: function() {
161             this.initialized = true;
162         },
164         /**
165          * In point mode, drag and drop interaction is defined by the 
166          * location of the cursor during the drag/drop
167          * @property POINT
168          * @type int
169          * @static
170          * @final
171          */
172         POINT: 0,
174         /**
175          * In intersect mode, drag and drop interaction is defined by the 
176          * cursor position or the amount of overlap of two or more drag and 
177          * drop objects.
178          * @property INTERSECT
179          * @type int
180          * @static
181          * @final
182          */
183         INTERSECT: 1,
185         /**
186          * In intersect mode, drag and drop interaction is defined only by the 
187          * overlap of two or more drag and drop objects.
188          * @property STRICT_INTERSECT
189          * @type int
190          * @static
191          * @final
192          */
193         STRICT_INTERSECT: 2,
195         /**
196          * The current drag and drop mode.  Default: POINT
197          * @property mode
198          * @type int
199          * @static
200          */
201         mode: 0,
203         /**
204          * Runs method on all drag and drop objects
205          * @method _execOnAll
206          * @private
207          * @static
208          */
209         _execOnAll: function(sMethod, args) {
210             for (var i in this.ids) {
211                 for (var j in this.ids[i]) {
212                     var oDD = this.ids[i][j];
213                     if (! this.isTypeOfDD(oDD)) {
214                         continue;
215                     }
216                     oDD[sMethod].apply(oDD, args);
217                 }
218             }
219         },
221         /**
222          * Drag and drop initialization.  Sets up the global event handlers
223          * @method _onLoad
224          * @private
225          * @static
226          */
227         _onLoad: function() {
229             this.init();
231             YAHOO.log("DragDropMgr onload", "info", "DragDropMgr");
233             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
234             Event.on(document, "mousemove", this.handleMouseMove, this, true);
235             Event.on(window,   "unload",    this._onUnload, this, true);
236             Event.on(window,   "resize",    this._onResize, this, true);
237             // Event.on(window,   "mouseout",    this._test);
239         },
241         /**
242          * Reset constraints on all drag and drop objs
243          * @method _onResize
244          * @private
245          * @static
246          */
247         _onResize: function(e) {
248             YAHOO.log("window resize", "info", "DragDropMgr");
249             this._execOnAll("resetConstraints", []);
250         },
252         /**
253          * Lock all drag and drop functionality
254          * @method lock
255          * @static
256          */
257         lock: function() { this.locked = true; },
259         /**
260          * Unlock all drag and drop functionality
261          * @method unlock
262          * @static
263          */
264         unlock: function() { this.locked = false; },
266         /**
267          * Is drag and drop locked?
268          * @method isLocked
269          * @return {boolean} True if drag and drop is locked, false otherwise.
270          * @static
271          */
272         isLocked: function() { return this.locked; },
274         /**
275          * Location cache that is set for all drag drop objects when a drag is
276          * initiated, cleared when the drag is finished.
277          * @property locationCache
278          * @private
279          * @static
280          */
281         locationCache: {},
283         /**
284          * Set useCache to false if you want to force object the lookup of each
285          * drag and drop linked element constantly during a drag.
286          * @property useCache
287          * @type boolean
288          * @static
289          */
290         useCache: true,
292         /**
293          * The number of pixels that the mouse needs to move after the 
294          * mousedown before the drag is initiated.  Default=3;
295          * @property clickPixelThresh
296          * @type int
297          * @static
298          */
299         clickPixelThresh: 3,
301         /**
302          * The number of milliseconds after the mousedown event to initiate the
303          * drag if we don't get a mouseup event. Default=1000
304          * @property clickTimeThresh
305          * @type int
306          * @static
307          */
308         clickTimeThresh: 1000,
310         /**
311          * Flag that indicates that either the drag pixel threshold or the 
312          * mousdown time threshold has been met
313          * @property dragThreshMet
314          * @type boolean
315          * @private
316          * @static
317          */
318         dragThreshMet: false,
320         /**
321          * Timeout used for the click time threshold
322          * @property clickTimeout
323          * @type Object
324          * @private
325          * @static
326          */
327         clickTimeout: null,
329         /**
330          * The X position of the mousedown event stored for later use when a 
331          * drag threshold is met.
332          * @property startX
333          * @type int
334          * @private
335          * @static
336          */
337         startX: 0,
339         /**
340          * The Y position of the mousedown event stored for later use when a 
341          * drag threshold is met.
342          * @property startY
343          * @type int
344          * @private
345          * @static
346          */
347         startY: 0,
349         /**
350          * Each DragDrop instance must be registered with the DragDropMgr.  
351          * This is executed in DragDrop.init()
352          * @method regDragDrop
353          * @param {DragDrop} oDD the DragDrop object to register
354          * @param {String} sGroup the name of the group this element belongs to
355          * @static
356          */
357         regDragDrop: function(oDD, sGroup) {
358             if (!this.initialized) { this.init(); }
359             
360             if (!this.ids[sGroup]) {
361                 this.ids[sGroup] = {};
362             }
363             this.ids[sGroup][oDD.id] = oDD;
364         },
366         /**
367          * Removes the supplied dd instance from the supplied group. Executed
368          * by DragDrop.removeFromGroup, so don't call this function directly.
369          * @method removeDDFromGroup
370          * @private
371          * @static
372          */
373         removeDDFromGroup: function(oDD, sGroup) {
374             if (!this.ids[sGroup]) {
375                 this.ids[sGroup] = {};
376             }
378             var obj = this.ids[sGroup];
379             if (obj && obj[oDD.id]) {
380                 delete obj[oDD.id];
381             }
382         },
384         /**
385          * Unregisters a drag and drop item.  This is executed in 
386          * DragDrop.unreg, use that method instead of calling this directly.
387          * @method _remove
388          * @private
389          * @static
390          */
391         _remove: function(oDD) {
392             for (var g in oDD.groups) {
393                 if (g && this.ids[g][oDD.id]) {
394                     delete this.ids[g][oDD.id];
395                     //YAHOO.log("NEW LEN " + this.ids.length, "warn");
396                 }
397             }
398             delete this.handleIds[oDD.id];
399         },
401         /**
402          * Each DragDrop handle element must be registered.  This is done
403          * automatically when executing DragDrop.setHandleElId()
404          * @method regHandle
405          * @param {String} sDDId the DragDrop id this element is a handle for
406          * @param {String} sHandleId the id of the element that is the drag 
407          * handle
408          * @static
409          */
410         regHandle: function(sDDId, sHandleId) {
411             if (!this.handleIds[sDDId]) {
412                 this.handleIds[sDDId] = {};
413             }
414             this.handleIds[sDDId][sHandleId] = sHandleId;
415         },
417         /**
418          * Utility function to determine if a given element has been 
419          * registered as a drag drop item.
420          * @method isDragDrop
421          * @param {String} id the element id to check
422          * @return {boolean} true if this element is a DragDrop item, 
423          * false otherwise
424          * @static
425          */
426         isDragDrop: function(id) {
427             return ( this.getDDById(id) ) ? true : false;
428         },
430         /**
431          * Returns the drag and drop instances that are in all groups the
432          * passed in instance belongs to.
433          * @method getRelated
434          * @param {DragDrop} p_oDD the obj to get related data for
435          * @param {boolean} bTargetsOnly if true, only return targetable objs
436          * @return {DragDrop[]} the related instances
437          * @static
438          */
439         getRelated: function(p_oDD, bTargetsOnly) {
440             var oDDs = [];
441             for (var i in p_oDD.groups) {
442                 for (j in this.ids[i]) {
443                     var dd = this.ids[i][j];
444                     if (! this.isTypeOfDD(dd)) {
445                         continue;
446                     }
447                     if (!bTargetsOnly || dd.isTarget) {
448                         oDDs[oDDs.length] = dd;
449                     }
450                 }
451             }
453             return oDDs;
454         },
456         /**
457          * Returns true if the specified dd target is a legal target for 
458          * the specifice drag obj
459          * @method isLegalTarget
460          * @param {DragDrop} the drag obj
461          * @param {DragDrop} the target
462          * @return {boolean} true if the target is a legal target for the 
463          * dd obj
464          * @static
465          */
466         isLegalTarget: function (oDD, oTargetDD) {
467             var targets = this.getRelated(oDD, true);
468             for (var i=0, len=targets.length;i<len;++i) {
469                 if (targets[i].id == oTargetDD.id) {
470                     return true;
471                 }
472             }
474             return false;
475         },
477         /**
478          * My goal is to be able to transparently determine if an object is
479          * typeof DragDrop, and the exact subclass of DragDrop.  typeof 
480          * returns "object", oDD.constructor.toString() always returns
481          * "DragDrop" and not the name of the subclass.  So for now it just
482          * evaluates a well-known variable in DragDrop.
483          * @method isTypeOfDD
484          * @param {Object} the object to evaluate
485          * @return {boolean} true if typeof oDD = DragDrop
486          * @static
487          */
488         isTypeOfDD: function (oDD) {
489             return (oDD && oDD.__ygDragDrop);
490         },
492         /**
493          * Utility function to determine if a given element has been 
494          * registered as a drag drop handle for the given Drag Drop object.
495          * @method isHandle
496          * @param {String} id the element id to check
497          * @return {boolean} true if this element is a DragDrop handle, false 
498          * otherwise
499          * @static
500          */
501         isHandle: function(sDDId, sHandleId) {
502             return ( this.handleIds[sDDId] && 
503                             this.handleIds[sDDId][sHandleId] );
504         },
506         /**
507          * Returns the DragDrop instance for a given id
508          * @method getDDById
509          * @param {String} id the id of the DragDrop object
510          * @return {DragDrop} the drag drop object, null if it is not found
511          * @static
512          */
513         getDDById: function(id) {
514             for (var i in this.ids) {
515                 if (this.ids[i][id]) {
516                     return this.ids[i][id];
517                 }
518             }
519             return null;
520         },
522         /**
523          * Fired after a registered DragDrop object gets the mousedown event.
524          * Sets up the events required to track the object being dragged
525          * @method handleMouseDown
526          * @param {Event} e the event
527          * @param oDD the DragDrop object being dragged
528          * @private
529          * @static
530          */
531         handleMouseDown: function(e, oDD) {
533             this.currentTarget = YAHOO.util.Event.getTarget(e);
535             this.dragCurrent = oDD;
537             var el = oDD.getEl();
539             // track start position
540             this.startX = YAHOO.util.Event.getPageX(e);
541             this.startY = YAHOO.util.Event.getPageY(e);
543             this.deltaX = this.startX - el.offsetLeft;
544             this.deltaY = this.startY - el.offsetTop;
546             this.dragThreshMet = false;
548             this.clickTimeout = setTimeout( 
549                     function() { 
550                         var DDM = YAHOO.util.DDM;
551                         DDM.startDrag(DDM.startX, DDM.startY); 
552                     }, 
553                     this.clickTimeThresh );
554         },
556         /**
557          * Fired when either the drag pixel threshol or the mousedown hold 
558          * time threshold has been met.
559          * @method startDrag
560          * @param x {int} the X position of the original mousedown
561          * @param y {int} the Y position of the original mousedown
562          * @static
563          */
564         startDrag: function(x, y) {
565             YAHOO.log("firing drag start events", "info", "DragDropMgr");
566             clearTimeout(this.clickTimeout);
567             var dc = this.dragCurrent;
568             if (dc) {
569                 dc.b4StartDrag(x, y);
570             }
571             if (dc) {
572                 dc.startDrag(x, y);
573             }
574             this.dragThreshMet = true;
575         },
577         /**
578          * Internal function to handle the mouseup event.  Will be invoked 
579          * from the context of the document.
580          * @method handleMouseUp
581          * @param {Event} e the event
582          * @private
583          * @static
584          */
585         handleMouseUp: function(e) {
586             if (this.dragCurrent) {
587                 clearTimeout(this.clickTimeout);
589                 if (this.dragThreshMet) {
590                     YAHOO.log("mouseup detected - completing drag", "info", "DragDropMgr");
591                     this.fireEvents(e, true);
592                 } else {
593                     YAHOO.log("drag threshold not met", "info", "DragDropMgr");
594                 }
596                 this.stopDrag(e);
598                 this.stopEvent(e);
599             }
600         },
602         /**
603          * Utility to stop event propagation and event default, if these 
604          * features are turned on.
605          * @method stopEvent
606          * @param {Event} e the event as returned by this.getEvent()
607          * @static
608          */
609         stopEvent: function(e) {
610             if (this.stopPropagation) {
611                 YAHOO.util.Event.stopPropagation(e);
612             }
614             if (this.preventDefault) {
615                 YAHOO.util.Event.preventDefault(e);
616             }
617         },
619         /** 
620          * Ends the current drag, cleans up the state, and fires the endDrag
621          * and mouseUp events.  Called internally when a mouseup is detected
622          * during the drag.  Can be fired manually during the drag by passing
623          * either another event (such as the mousemove event received in onDrag)
624          * or a fake event with pageX and pageY defined (so that endDrag and
625          * onMouseUp have usable position data.).  Alternatively, pass true
626          * for the silent parameter so that the endDrag and onMouseUp events
627          * are skipped (so no event data is needed.)
628          *
629          * @method stopDrag
630          * @param {Event} e the mouseup event, another event (or a fake event) 
631          *                  with pageX and pageY defined, or nothing if the 
632          *                  silent parameter is true
633          * @param {boolean} silent skips the enddrag and mouseup events if true
634          * @static
635          */
636         stopDrag: function(e, silent) {
637             // YAHOO.log("mouseup - removing event handlers");
639             // Fire the drag end event for the item that was dragged
640             if (this.dragCurrent && !silent) {
641                 if (this.dragThreshMet) {
642                     YAHOO.log("firing endDrag events", "info", "DragDropMgr");
643                     this.dragCurrent.b4EndDrag(e);
644                     this.dragCurrent.endDrag(e);
645                 }
647                 YAHOO.log("firing dragdrop onMouseUp event", "info", "DragDropMgr");
648                 this.dragCurrent.onMouseUp(e);
649             }
651             this.dragCurrent = null;
652             this.dragOvers = {};
653         },
655         /** 
656          * Internal function to handle the mousemove event.  Will be invoked 
657          * from the context of the html element.
658          *
659          * @TODO figure out what we can do about mouse events lost when the 
660          * user drags objects beyond the window boundary.  Currently we can 
661          * detect this in internet explorer by verifying that the mouse is 
662          * down during the mousemove event.  Firefox doesn't give us the 
663          * button state on the mousemove event.
664          * @method handleMouseMove
665          * @param {Event} e the event
666          * @private
667          * @static
668          */
669         handleMouseMove: function(e) {
670             //YAHOO.log("handlemousemove");
671             
672             var dc = this.dragCurrent;
673             if (dc) {
674                 // YAHOO.log("no current drag obj");
676                 // var button = e.which || e.button;
677                 // YAHOO.log("which: " + e.which + ", button: "+ e.button);
679                 // check for IE mouseup outside of page boundary
680                 if (YAHOO.util.Event.isIE && !e.button) {
681                     YAHOO.log("button failure", "info", "DragDropMgr");
682                     this.stopEvent(e);
683                     return this.handleMouseUp(e);
684                 }
686                 if (!this.dragThreshMet) {
687                     var diffX = Math.abs(this.startX - YAHOO.util.Event.getPageX(e));
688                     var diffY = Math.abs(this.startY - YAHOO.util.Event.getPageY(e));
689                     // YAHOO.log("diffX: " + diffX + "diffY: " + diffY);
690                     if (diffX > this.clickPixelThresh || 
691                                 diffY > this.clickPixelThresh) {
692                         YAHOO.log("pixel threshold met", "info", "DragDropMgr");
693                         this.startDrag(this.startX, this.startY);
694                     }
695                 }
697                 if (this.dragThreshMet) {
698                     dc.b4Drag(e);
699                     if (dc) {
700                         dc.onDrag(e);
701                     }
702                     if (dc) {
703                         this.fireEvents(e, false);
704                     }
705                 }
707                 this.stopEvent(e);
708             }
709         },
711         /**
712          * Iterates over all of the DragDrop elements to find ones we are 
713          * hovering over or dropping on
714          * @method fireEvents
715          * @param {Event} e the event
716          * @param {boolean} isDrop is this a drop op or a mouseover op?
717          * @private
718          * @static
719          */
720         fireEvents: function(e, isDrop) {
721             var dc = this.dragCurrent;
723             // If the user did the mouse up outside of the window, we could 
724             // get here even though we have ended the drag.
725             if (!dc || dc.isLocked()) {
726                 return;
727             }
729             var x = YAHOO.util.Event.getPageX(e);
730             var y = YAHOO.util.Event.getPageY(e);
731             var pt = new YAHOO.util.Point(x,y);
732             var pos = dc.getTargetCoord(pt.x, pt.y);
733             var el = dc.getDragEl();
734             curRegion = new YAHOO.util.Region( pos.y, 
735                                                pos.x + el.offsetWidth,
736                                                pos.y + el.offsetHeight, 
737                                                pos.x );
738             // cache the previous dragOver array
739             var oldOvers = [];
741             var outEvts   = [];
742             var overEvts  = [];
743             var dropEvts  = [];
744             var enterEvts = [];
747             // Check to see if the object(s) we were hovering over is no longer 
748             // being hovered over so we can fire the onDragOut event
749             for (var i in this.dragOvers) {
751                 var ddo = this.dragOvers[i];
753                 if (! this.isTypeOfDD(ddo)) {
754                     continue;
755                 }
757                 if (! this.isOverTarget(pt, ddo, this.mode, curRegion)) {
758                     outEvts.push( ddo );
759                 }
761                 oldOvers[i] = true;
762                 delete this.dragOvers[i];
763             }
765             for (var sGroup in dc.groups) {
766                 // YAHOO.log("Processing group " + sGroup);
767                 
768                 if ("string" != typeof sGroup) {
769                     continue;
770                 }
772                 for (i in this.ids[sGroup]) {
773                     var oDD = this.ids[sGroup][i];
774                     if (! this.isTypeOfDD(oDD)) {
775                         continue;
776                     }
778                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
779                         if (this.isOverTarget(pt, oDD, this.mode, curRegion)) {
780                             // look for drop interactions
781                             if (isDrop) {
782                                 dropEvts.push( oDD );
783                             // look for drag enter and drag over interactions
784                             } else {
786                                 // initial drag over: dragEnter fires
787                                 if (!oldOvers[oDD.id]) {
788                                     enterEvts.push( oDD );
789                                 // subsequent drag overs: dragOver fires
790                                 } else {
791                                     overEvts.push( oDD );
792                                 }
794                                 this.dragOvers[oDD.id] = oDD;
795                             }
796                         }
797                     }
798                 }
799             }
801             this.interactionInfo = {
802                 out:       outEvts,
803                 enter:     enterEvts,
804                 over:      overEvts,
805                 drop:      dropEvts,
806                 point:     pt,
807                 draggedRegion:    curRegion,
808                 sourceRegion: this.locationCache[dc.id],
809                 validDrop: isDrop
810             };
812             // notify about a drop that did not find a target
813             if (isDrop && !dropEvts.length) {
814                 YAHOO.log(dc.id + " dropped, but not on a target", "info", "DragDropMgr");
815                 this.interactionInfo.validDrop = false;
816                 dc.onInvalidDrop(e);
817             }
820             if (this.mode) {
821                 if (outEvts.length) {
822                     YAHOO.log(dc.id+" onDragOut: " + outEvts, "info", "DragDropMgr");
823                     dc.b4DragOut(e, outEvts);
824                     if (dc) {
825                         dc.onDragOut(e, outEvts);
826                     }
827                 }
829                 if (enterEvts.length) {
830                     YAHOO.log(dc.id+" onDragEnter: " + enterEvts, "info", "DragDropMgr");
831                     if (dc) {
832                         dc.onDragEnter(e, enterEvts);
833                     }
834                 }
836                 if (overEvts.length) {
837                     YAHOO.log(dc.id+" onDragOver: " + overEvts, "info", "DragDropMgr");
838                     if (dc) {
839                         dc.b4DragOver(e, overEvts);
840                     }
842                     if (dc) {
843                         dc.onDragOver(e, overEvts);
844                     }
845                 }
847                 if (dropEvts.length) {
848                     YAHOO.log(dc.id+" onDragDrop: " + dropEvts, "info", "DragDropMgr");
849                     if (dc) {
850                         dc.b4DragDrop(e, dropEvts);
851                     }
852                     if (dc) {
853                         dc.onDragDrop(e, dropEvts);
854                     }
855                 }
857             } else {
858                 // fire dragout events
859                 var len = 0;
860                 for (i=0, len=outEvts.length; i<len; ++i) {
861                     YAHOO.log(dc.id+" onDragOut: " + outEvts[i].id, "info", "DragDropMgr");
862                     if (dc) {
863                         dc.b4DragOut(e, outEvts[i].id);
864                     }
865                     if (dc) {
866                         dc.onDragOut(e, outEvts[i].id);
867                     }
868                 }
869                  
870                 // fire enter events
871                 for (i=0,len=enterEvts.length; i<len; ++i) {
872                     YAHOO.log(dc.id + " onDragEnter " + enterEvts[i].id, "info", "DragDropMgr");
873                     // dc.b4DragEnter(e, oDD.id);
875                     if (dc) {
876                         dc.onDragEnter(e, enterEvts[i].id);
877                     }
878                 }
879          
880                 // fire over events
881                 for (i=0,len=overEvts.length; i<len; ++i) {
882                     YAHOO.log(dc.id + " onDragOver " + overEvts[i].id, "info", "DragDropMgr");
883                     if (dc) {
884                         dc.b4DragOver(e, overEvts[i].id);
885                     }
886                     if (dc) {
887                         dc.onDragOver(e, overEvts[i].id);
888                     }
889                 }
891                 // fire drop events
892                 for (i=0, len=dropEvts.length; i<len; ++i) {
893                     YAHOO.log(dc.id + " dropped on " + dropEvts[i].id, "info", "DragDropMgr");
894                     if (dc) {
895                         dc.b4DragDrop(e, dropEvts[i].id);
896                     }
897                     if (dc) {
898                         dc.onDragDrop(e, dropEvts[i].id);
899                     }
900                 }
902             }
903         },
905         /**
906          * Helper function for getting the best match from the list of drag 
907          * and drop objects returned by the drag and drop events when we are 
908          * in INTERSECT mode.  It returns either the first object that the 
909          * cursor is over, or the object that has the greatest overlap with 
910          * the dragged element.
911          * @method getBestMatch
912          * @param  {DragDrop[]} dds The array of drag and drop objects 
913          * targeted
914          * @return {DragDrop}       The best single match
915          * @static
916          */
917         getBestMatch: function(dds) {
918             var winner = null;
920             var len = dds.length;
922             if (len == 1) {
923                 winner = dds[0];
924             } else {
925                 // Loop through the targeted items
926                 for (var i=0; i<len; ++i) {
927                     var dd = dds[i];
928                     // If the cursor is over the object, it wins.  If the 
929                     // cursor is over multiple matches, the first one we come
930                     // to wins.
931                     if (this.mode == this.INTERSECT && dd.cursorIsOver) {
932                         winner = dd;
933                         break;
934                     // Otherwise the object with the most overlap wins
935                     } else {
936                         if (!winner || !winner.overlap || (dd.overlap &&
937                             winner.overlap.getArea() < dd.overlap.getArea())) {
938                             winner = dd;
939                         }
940                     }
941                 }
942             }
944             return winner;
945         },
947         /**
948          * Refreshes the cache of the top-left and bottom-right points of the 
949          * drag and drop objects in the specified group(s).  This is in the
950          * format that is stored in the drag and drop instance, so typical 
951          * usage is:
952          * <code>
953          * YAHOO.util.DragDropMgr.refreshCache(ddinstance.groups);
954          * </code>
955          * Alternatively:
956          * <code>
957          * YAHOO.util.DragDropMgr.refreshCache({group1:true, group2:true});
958          * </code>
959          * @TODO this really should be an indexed array.  Alternatively this
960          * method could accept both.
961          * @method refreshCache
962          * @param {Object} groups an associative array of groups to refresh
963          * @static
964          */
965         refreshCache: function(groups) {
966             YAHOO.log("refreshing element location cache", "info", "DragDropMgr");
968             // refresh everything if group array is not provided
969             var g = groups || this.ids;
971             for (var sGroup in g) {
972                 if ("string" != typeof sGroup) {
973                     continue;
974                 }
975                 for (var i in this.ids[sGroup]) {
976                     var oDD = this.ids[sGroup][i];
978                     if (this.isTypeOfDD(oDD)) {
979                         var loc = this.getLocation(oDD);
980                         if (loc) {
981                             this.locationCache[oDD.id] = loc;
982                         } else {
983                             delete this.locationCache[oDD.id];
984 YAHOO.log("Could not get the loc for " + oDD.id, "warn", "DragDropMgr");
985                         }
986                     }
987                 }
988             }
989         },
991         /**
992          * This checks to make sure an element exists and is in the DOM.  The
993          * main purpose is to handle cases where innerHTML is used to remove
994          * drag and drop objects from the DOM.  IE provides an 'unspecified
995          * error' when trying to access the offsetParent of such an element
996          * @method verifyEl
997          * @param {HTMLElement} el the element to check
998          * @return {boolean} true if the element looks usable
999          * @static
1000          */
1001         verifyEl: function(el) {
1002             try {
1003                 if (el) {
1004                     var parent = el.offsetParent;
1005                     if (parent) {
1006                         return true;
1007                     }
1008                 }
1009             } catch(e) {
1010                 YAHOO.log("detected problem with an element", "info", "DragDropMgr");
1011             }
1013             return false;
1014         },
1015         
1016         /**
1017          * Returns a Region object containing the drag and drop element's position
1018          * and size, including the padding configured for it
1019          * @method getLocation
1020          * @param {DragDrop} oDD the drag and drop object to get the 
1021          *                       location for
1022          * @return {YAHOO.util.Region} a Region object representing the total area
1023          *                             the element occupies, including any padding
1024          *                             the instance is configured for.
1025          * @static
1026          */
1027         getLocation: function(oDD) {
1028             if (! this.isTypeOfDD(oDD)) {
1029                 YAHOO.log(oDD + " is not a DD obj", "info", "DragDropMgr");
1030                 return null;
1031             }
1033             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
1035             try {
1036                 pos= YAHOO.util.Dom.getXY(el);
1037             } catch (e) { }
1039             if (!pos) {
1040                 YAHOO.log("getXY failed", "info", "DragDropMgr");
1041                 return null;
1042             }
1044             x1 = pos[0];
1045             x2 = x1 + el.offsetWidth;
1046             y1 = pos[1];
1047             y2 = y1 + el.offsetHeight;
1049             t = y1 - oDD.padding[0];
1050             r = x2 + oDD.padding[1];
1051             b = y2 + oDD.padding[2];
1052             l = x1 - oDD.padding[3];
1054             return new YAHOO.util.Region( t, r, b, l );
1055         },
1057         /**
1058          * Checks the cursor location to see if it over the target
1059          * @method isOverTarget
1060          * @param {YAHOO.util.Point} pt The point to evaluate
1061          * @param {DragDrop} oTarget the DragDrop object we are inspecting
1062          * @param {boolean} intersect true if we are in intersect mode
1063          * @param {YAHOO.util.Region} pre-cached location of the dragged element
1064          * @return {boolean} true if the mouse is over the target
1065          * @private
1066          * @static
1067          */
1068         isOverTarget: function(pt, oTarget, intersect, curRegion) {
1069             // use cache if available
1070             var loc = this.locationCache[oTarget.id];
1071             if (!loc || !this.useCache) {
1072                 YAHOO.log("cache not populated", "info", "DragDropMgr");
1073                 loc = this.getLocation(oTarget);
1074                 this.locationCache[oTarget.id] = loc;
1076                 YAHOO.log("cache: " + loc, "info", "DragDropMgr");
1077             }
1079             if (!loc) {
1080                 YAHOO.log("could not get the location of the element", "info", "DragDropMgr");
1081                 return false;
1082             }
1084             //YAHOO.log("loc: " + loc + ", pt: " + pt);
1085             oTarget.cursorIsOver = loc.contains( pt );
1087             // DragDrop is using this as a sanity check for the initial mousedown
1088             // in this case we are done.  In POINT mode, if the drag obj has no
1089             // contraints, we are done. Otherwise we need to evaluate the 
1090             // region the target as occupies to determine if the dragged element
1091             // overlaps with it.
1092             
1093             var dc = this.dragCurrent;
1094             if (!dc || (!intersect && !dc.constrainX && !dc.constrainY)) {
1096                 //if (oTarget.cursorIsOver) {
1097                     //YAHOO.log("over " + oTarget + ", " + loc + ", " + pt, "warn");
1098                 //}
1099                 return oTarget.cursorIsOver;
1100             }
1102             oTarget.overlap = null;
1104             // Get the current location of the drag element, this is the
1105             // location of the mouse event less the delta that represents
1106             // where the original mousedown happened on the element.  We
1107             // need to consider constraints and ticks as well.
1109             if (!curRegion) {
1110                 var pos = dc.getTargetCoord(pt.x, pt.y);
1111                 var el = dc.getDragEl();
1112                 curRegion = new YAHOO.util.Region( pos.y, 
1113                                                    pos.x + el.offsetWidth,
1114                                                    pos.y + el.offsetHeight, 
1115                                                    pos.x );
1116             }
1118             var overlap = curRegion.intersect(loc);
1120             if (overlap) {
1121                 oTarget.overlap = overlap;
1122                 return (intersect) ? true : oTarget.cursorIsOver;
1123             } else {
1124                 return false;
1125             }
1126         },
1128         /**
1129          * unload event handler
1130          * @method _onUnload
1131          * @private
1132          * @static
1133          */
1134         _onUnload: function(e, me) {
1135             this.unregAll();
1136         },
1138         /**
1139          * Cleans up the drag and drop events and objects.
1140          * @method unregAll
1141          * @private
1142          * @static
1143          */
1144         unregAll: function() {
1145             YAHOO.log("unregister all", "info", "DragDropMgr");
1147             if (this.dragCurrent) {
1148                 this.stopDrag();
1149                 this.dragCurrent = null;
1150             }
1152             this._execOnAll("unreg", []);
1154             for (i in this.elementCache) {
1155                 delete this.elementCache[i];
1156             }
1158             this.elementCache = {};
1159             this.ids = {};
1160         },
1162         /**
1163          * A cache of DOM elements
1164          * @property elementCache
1165          * @private
1166          * @static
1167          */
1168         elementCache: {},
1169         
1170         /**
1171          * Get the wrapper for the DOM element specified
1172          * @method getElWrapper
1173          * @param {String} id the id of the element to get
1174          * @return {YAHOO.util.DDM.ElementWrapper} the wrapped element
1175          * @private
1176          * @deprecated This wrapper isn't that useful
1177          * @static
1178          */
1179         getElWrapper: function(id) {
1180             var oWrapper = this.elementCache[id];
1181             if (!oWrapper || !oWrapper.el) {
1182                 oWrapper = this.elementCache[id] = 
1183                     new this.ElementWrapper(YAHOO.util.Dom.get(id));
1184             }
1185             return oWrapper;
1186         },
1188         /**
1189          * Returns the actual DOM element
1190          * @method getElement
1191          * @param {String} id the id of the elment to get
1192          * @return {Object} The element
1193          * @deprecated use YAHOO.util.Dom.get instead
1194          * @static
1195          */
1196         getElement: function(id) {
1197             return YAHOO.util.Dom.get(id);
1198         },
1199         
1200         /**
1201          * Returns the style property for the DOM element (i.e., 
1202          * document.getElById(id).style)
1203          * @method getCss
1204          * @param {String} id the id of the elment to get
1205          * @return {Object} The style property of the element
1206          * @deprecated use YAHOO.util.Dom instead
1207          * @static
1208          */
1209         getCss: function(id) {
1210             var el = YAHOO.util.Dom.get(id);
1211             return (el) ? el.style : null;
1212         },
1214         /**
1215          * Inner class for cached elements
1216          * @class DragDropMgr.ElementWrapper
1217          * @for DragDropMgr
1218          * @private
1219          * @deprecated
1220          */
1221         ElementWrapper: function(el) {
1222                 /**
1223                  * The element
1224                  * @property el
1225                  */
1226                 this.el = el || null;
1227                 /**
1228                  * The element id
1229                  * @property id
1230                  */
1231                 this.id = this.el && el.id;
1232                 /**
1233                  * A reference to the style property
1234                  * @property css
1235                  */
1236                 this.css = this.el && el.style;
1237             },
1239         /**
1240          * Returns the X position of an html element
1241          * @method getPosX
1242          * @param el the element for which to get the position
1243          * @return {int} the X coordinate
1244          * @for DragDropMgr
1245          * @deprecated use YAHOO.util.Dom.getX instead
1246          * @static
1247          */
1248         getPosX: function(el) {
1249             return YAHOO.util.Dom.getX(el);
1250         },
1252         /**
1253          * Returns the Y position of an html element
1254          * @method getPosY
1255          * @param el the element for which to get the position
1256          * @return {int} the Y coordinate
1257          * @deprecated use YAHOO.util.Dom.getY instead
1258          * @static
1259          */
1260         getPosY: function(el) {
1261             return YAHOO.util.Dom.getY(el); 
1262         },
1264         /**
1265          * Swap two nodes.  In IE, we use the native method, for others we 
1266          * emulate the IE behavior
1267          * @method swapNode
1268          * @param n1 the first node to swap
1269          * @param n2 the other node to swap
1270          * @static
1271          */
1272         swapNode: function(n1, n2) {
1273             if (n1.swapNode) {
1274                 n1.swapNode(n2);
1275             } else {
1276                 var p = n2.parentNode;
1277                 var s = n2.nextSibling;
1279                 if (s == n1) {
1280                     p.insertBefore(n1, n2);
1281                 } else if (n2 == n1.nextSibling) {
1282                     p.insertBefore(n2, n1);
1283                 } else {
1284                     n1.parentNode.replaceChild(n2, n1);
1285                     p.insertBefore(n1, s);
1286                 }
1287             }
1288         },
1290         /**
1291          * Returns the current scroll position
1292          * @method getScroll
1293          * @private
1294          * @static
1295          */
1296         getScroll: function () {
1297             var t, l, dde=document.documentElement, db=document.body;
1298             if (dde && (dde.scrollTop || dde.scrollLeft)) {
1299                 t = dde.scrollTop;
1300                 l = dde.scrollLeft;
1301             } else if (db) {
1302                 t = db.scrollTop;
1303                 l = db.scrollLeft;
1304             } else {
1305                 YAHOO.log("could not get scroll property", "info", "DragDropMgr");
1306             }
1307             return { top: t, left: l };
1308         },
1310         /**
1311          * Returns the specified element style property
1312          * @method getStyle
1313          * @param {HTMLElement} el          the element
1314          * @param {string}      styleProp   the style property
1315          * @return {string} The value of the style property
1316          * @deprecated use YAHOO.util.Dom.getStyle
1317          * @static
1318          */
1319         getStyle: function(el, styleProp) {
1320             return YAHOO.util.Dom.getStyle(el, styleProp);
1321         },
1323         /**
1324          * Gets the scrollTop
1325          * @method getScrollTop
1326          * @return {int} the document's scrollTop
1327          * @static
1328          */
1329         getScrollTop: function () { return this.getScroll().top; },
1331         /**
1332          * Gets the scrollLeft
1333          * @method getScrollLeft
1334          * @return {int} the document's scrollTop
1335          * @static
1336          */
1337         getScrollLeft: function () { return this.getScroll().left; },
1339         /**
1340          * Sets the x/y position of an element to the location of the
1341          * target element.
1342          * @method moveToEl
1343          * @param {HTMLElement} moveEl      The element to move
1344          * @param {HTMLElement} targetEl    The position reference element
1345          * @static
1346          */
1347         moveToEl: function (moveEl, targetEl) {
1348             var aCoord = YAHOO.util.Dom.getXY(targetEl);
1349             YAHOO.log("moveToEl: " + aCoord, "info", "DragDropMgr");
1350             YAHOO.util.Dom.setXY(moveEl, aCoord);
1351         },
1353         /**
1354          * Gets the client height
1355          * @method getClientHeight
1356          * @return {int} client height in px
1357          * @deprecated use YAHOO.util.Dom.getViewportHeight instead
1358          * @static
1359          */
1360         getClientHeight: function() {
1361             return YAHOO.util.Dom.getViewportHeight();
1362         },
1364         /**
1365          * Gets the client width
1366          * @method getClientWidth
1367          * @return {int} client width in px
1368          * @deprecated use YAHOO.util.Dom.getViewportWidth instead
1369          * @static
1370          */
1371         getClientWidth: function() {
1372             return YAHOO.util.Dom.getViewportWidth();
1373         },
1375         /**
1376          * Numeric array sort function
1377          * @method numericSort
1378          * @static
1379          */
1380         numericSort: function(a, b) { return (a - b); },
1382         /**
1383          * Internal counter
1384          * @property _timeoutCount
1385          * @private
1386          * @static
1387          */
1388         _timeoutCount: 0,
1390         /**
1391          * Trying to make the load order less important.  Without this we get
1392          * an error if this file is loaded before the Event Utility.
1393          * @method _addListeners
1394          * @private
1395          * @static
1396          */
1397         _addListeners: function() {
1398             var DDM = YAHOO.util.DDM;
1399             if ( YAHOO.util.Event && document ) {
1400                 DDM._onLoad();
1401             } else {
1402                 if (DDM._timeoutCount > 2000) {
1403                     YAHOO.log("DragDrop requires the Event Utility", "error", "DragDropMgr");
1404                 } else {
1405                     setTimeout(DDM._addListeners, 10);
1406                     if (document && document.body) {
1407                         DDM._timeoutCount += 1;
1408                     }
1409                 }
1410             }
1411         },
1413         /**
1414          * Recursively searches the immediate parent and all child nodes for 
1415          * the handle element in order to determine wheter or not it was 
1416          * clicked.
1417          * @method handleWasClicked
1418          * @param node the html element to inspect
1419          * @static
1420          */
1421         handleWasClicked: function(node, id) {
1422             if (this.isHandle(id, node.id)) {
1423                 YAHOO.log("clicked node is a handle", "info", "DragDropMgr");
1424                 return true;
1425             } else {
1426                 // check to see if this is a text node child of the one we want
1427                 var p = node.parentNode;
1428                 // YAHOO.log("p: " + p);
1430                 while (p) {
1431                     if (this.isHandle(id, p.id)) {
1432                         return true;
1433                     } else {
1434                         YAHOO.log(p.id + " is not a handle", "info", "DragDropMgr");
1435                         p = p.parentNode;
1436                     }
1437                 }
1438             }
1440             return false;
1441         }
1443     };
1445 }();
1447 // shorter alias, save a few bytes
1448 YAHOO.util.DDM = YAHOO.util.DragDropMgr;
1449 YAHOO.util.DDM._addListeners();
1453 (function() {
1455 var Event=YAHOO.util.Event; 
1456 var Dom=YAHOO.util.Dom;
1459  * Defines the interface and base operation of items that that can be 
1460  * dragged or can be drop targets.  It was designed to be extended, overriding
1461  * the event handlers for startDrag, onDrag, onDragOver, onDragOut.
1462  * Up to three html elements can be associated with a DragDrop instance:
1463  * <ul>
1464  * <li>linked element: the element that is passed into the constructor.
1465  * This is the element which defines the boundaries for interaction with 
1466  * other DragDrop objects.</li>
1467  * <li>handle element(s): The drag operation only occurs if the element that 
1468  * was clicked matches a handle element.  By default this is the linked 
1469  * element, but there are times that you will want only a portion of the 
1470  * linked element to initiate the drag operation, and the setHandleElId() 
1471  * method provides a way to define this.</li>
1472  * <li>drag element: this represents an the element that would be moved along
1473  * with the cursor during a drag operation.  By default, this is the linked
1474  * element itself as in {@link YAHOO.util.DD}.  setDragElId() lets you define
1475  * a separate element that would be moved, as in {@link YAHOO.util.DDProxy}
1476  * </li>
1477  * </ul>
1478  * This class should not be instantiated until the onload event to ensure that
1479  * the associated elements are available.
1480  * The following would define a DragDrop obj that would interact with any 
1481  * other DragDrop obj in the "group1" group:
1482  * <pre>
1483  *  dd = new YAHOO.util.DragDrop("div1", "group1");
1484  * </pre>
1485  * Since none of the event handlers have been implemented, nothing would 
1486  * actually happen if you were to run the code above.  Normally you would 
1487  * override this class or one of the default implementations, but you can 
1488  * also override the methods you want on an instance of the class...
1489  * <pre>
1490  *  dd.onDragDrop = function(e, id) {
1491  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
1492  *  }
1493  * </pre>
1494  * @namespace YAHOO.util
1495  * @class DragDrop
1496  * @constructor
1497  * @param {String} id of the element that is linked to this instance
1498  * @param {String} sGroup the group of related DragDrop objects
1499  * @param {object} config an object containing configurable attributes
1500  *                Valid properties for DragDrop: 
1501  *                    padding, isTarget, maintainOffset, primaryButtonOnly,
1502  */
1503 YAHOO.util.DragDrop = function(id, sGroup, config) {
1504     if (id) {
1505         this.init(id, sGroup, config); 
1506     }
1509 YAHOO.util.DragDrop.prototype = {
1511     /**
1512      * The id of the element associated with this object.  This is what we 
1513      * refer to as the "linked element" because the size and position of 
1514      * this element is used to determine when the drag and drop objects have 
1515      * interacted.
1516      * @property id
1517      * @type String
1518      */
1519     id: null,
1521     /**
1522      * Configuration attributes passed into the constructor
1523      * @property config
1524      * @type object
1525      */
1526     config: null,
1528     /**
1529      * The id of the element that will be dragged.  By default this is same 
1530      * as the linked element , but could be changed to another element. Ex: 
1531      * YAHOO.util.DDProxy
1532      * @property dragElId
1533      * @type String
1534      * @private
1535      */
1536     dragElId: null, 
1538     /**
1539      * the id of the element that initiates the drag operation.  By default 
1540      * this is the linked element, but could be changed to be a child of this
1541      * element.  This lets us do things like only starting the drag when the 
1542      * header element within the linked html element is clicked.
1543      * @property handleElId
1544      * @type String
1545      * @private
1546      */
1547     handleElId: null, 
1549     /**
1550      * An associative array of HTML tags that will be ignored if clicked.
1551      * @property invalidHandleTypes
1552      * @type {string: string}
1553      */
1554     invalidHandleTypes: null, 
1556     /**
1557      * An associative array of ids for elements that will be ignored if clicked
1558      * @property invalidHandleIds
1559      * @type {string: string}
1560      */
1561     invalidHandleIds: null, 
1563     /**
1564      * An indexted array of css class names for elements that will be ignored
1565      * if clicked.
1566      * @property invalidHandleClasses
1567      * @type string[]
1568      */
1569     invalidHandleClasses: null, 
1571     /**
1572      * The linked element's absolute X position at the time the drag was 
1573      * started
1574      * @property startPageX
1575      * @type int
1576      * @private
1577      */
1578     startPageX: 0,
1580     /**
1581      * The linked element's absolute X position at the time the drag was 
1582      * started
1583      * @property startPageY
1584      * @type int
1585      * @private
1586      */
1587     startPageY: 0,
1589     /**
1590      * The group defines a logical collection of DragDrop objects that are 
1591      * related.  Instances only get events when interacting with other 
1592      * DragDrop object in the same group.  This lets us define multiple 
1593      * groups using a single DragDrop subclass if we want.
1594      * @property groups
1595      * @type {string: string}
1596      */
1597     groups: null,
1599     /**
1600      * Individual drag/drop instances can be locked.  This will prevent 
1601      * onmousedown start drag.
1602      * @property locked
1603      * @type boolean
1604      * @private
1605      */
1606     locked: false,
1608     /**
1609      * Lock this instance
1610      * @method lock
1611      */
1612     lock: function() { this.locked = true; },
1614     /**
1615      * Unlock this instace
1616      * @method unlock
1617      */
1618     unlock: function() { this.locked = false; },
1620     /**
1621      * By default, all instances can be a drop target.  This can be disabled by
1622      * setting isTarget to false.
1623      * @method isTarget
1624      * @type boolean
1625      */
1626     isTarget: true,
1628     /**
1629      * The padding configured for this drag and drop object for calculating
1630      * the drop zone intersection with this object.
1631      * @method padding
1632      * @type int[]
1633      */
1634     padding: null,
1636     /**
1637      * Cached reference to the linked element
1638      * @property _domRef
1639      * @private
1640      */
1641     _domRef: null,
1643     /**
1644      * Internal typeof flag
1645      * @property __ygDragDrop
1646      * @private
1647      */
1648     __ygDragDrop: true,
1650     /**
1651      * Set to true when horizontal contraints are applied
1652      * @property constrainX
1653      * @type boolean
1654      * @private
1655      */
1656     constrainX: false,
1658     /**
1659      * Set to true when vertical contraints are applied
1660      * @property constrainY
1661      * @type boolean
1662      * @private
1663      */
1664     constrainY: false,
1666     /**
1667      * The left constraint
1668      * @property minX
1669      * @type int
1670      * @private
1671      */
1672     minX: 0,
1674     /**
1675      * The right constraint
1676      * @property maxX
1677      * @type int
1678      * @private
1679      */
1680     maxX: 0,
1682     /**
1683      * The up constraint 
1684      * @property minY
1685      * @type int
1686      * @type int
1687      * @private
1688      */
1689     minY: 0,
1691     /**
1692      * The down constraint 
1693      * @property maxY
1694      * @type int
1695      * @private
1696      */
1697     maxY: 0,
1699     /**
1700      * The difference between the click position and the source element's location
1701      * @property deltaX
1702      * @type int
1703      * @private
1704      */
1705     deltaX: 0,
1707     /**
1708      * The difference between the click position and the source element's location
1709      * @property deltaY
1710      * @type int
1711      * @private
1712      */
1713     deltaY: 0,
1715     /**
1716      * Maintain offsets when we resetconstraints.  Set to true when you want
1717      * the position of the element relative to its parent to stay the same
1718      * when the page changes
1719      *
1720      * @property maintainOffset
1721      * @type boolean
1722      */
1723     maintainOffset: false,
1725     /**
1726      * Array of pixel locations the element will snap to if we specified a 
1727      * horizontal graduation/interval.  This array is generated automatically
1728      * when you define a tick interval.
1729      * @property xTicks
1730      * @type int[]
1731      */
1732     xTicks: null,
1734     /**
1735      * Array of pixel locations the element will snap to if we specified a 
1736      * vertical graduation/interval.  This array is generated automatically 
1737      * when you define a tick interval.
1738      * @property yTicks
1739      * @type int[]
1740      */
1741     yTicks: null,
1743     /**
1744      * By default the drag and drop instance will only respond to the primary
1745      * button click (left button for a right-handed mouse).  Set to true to
1746      * allow drag and drop to start with any mouse click that is propogated
1747      * by the browser
1748      * @property primaryButtonOnly
1749      * @type boolean
1750      */
1751     primaryButtonOnly: true,
1753     /**
1754      * The availabe property is false until the linked dom element is accessible.
1755      * @property available
1756      * @type boolean
1757      */
1758     available: false,
1760     /**
1761      * By default, drags can only be initiated if the mousedown occurs in the
1762      * region the linked element is.  This is done in part to work around a
1763      * bug in some browsers that mis-report the mousedown if the previous
1764      * mouseup happened outside of the window.  This property is set to true
1765      * if outer handles are defined.
1766      *
1767      * @property hasOuterHandles
1768      * @type boolean
1769      * @default false
1770      */
1771     hasOuterHandles: false,
1773     /**
1774      * Property that is assigned to a drag and drop object when testing to
1775      * see if it is being targeted by another dd object.  This property
1776      * can be used in intersect mode to help determine the focus of
1777      * the mouse interaction.  DDM.getBestMatch uses this property first to
1778      * determine the closest match in INTERSECT mode when multiple targets
1779      * are part of the same interaction.
1780      * @property cursorIsOver
1781      * @type boolean
1782      */
1783     cursorIsOver: false,
1785     /**
1786      * Property that is assigned to a drag and drop object when testing to
1787      * see if it is being targeted by another dd object.  This is a region
1788      * that represents the area the draggable element overlaps this target.
1789      * DDM.getBestMatch uses this property to compare the size of the overlap
1790      * to that of other targets in order to determine the closest match in
1791      * INTERSECT mode when multiple targets are part of the same interaction.
1792      * @property overlap 
1793      * @type YAHOO.util.Region
1794      */
1795     overlap: null,
1797     /**
1798      * Code that executes immediately before the startDrag event
1799      * @method b4StartDrag
1800      * @private
1801      */
1802     b4StartDrag: function(x, y) { },
1804     /**
1805      * Abstract method called after a drag/drop object is clicked
1806      * and the drag or mousedown time thresholds have beeen met.
1807      * @method startDrag
1808      * @param {int} X click location
1809      * @param {int} Y click location
1810      */
1811     startDrag: function(x, y) { /* override this */ },
1813     /**
1814      * Code that executes immediately before the onDrag event
1815      * @method b4Drag
1816      * @private
1817      */
1818     b4Drag: function(e) { },
1820     /**
1821      * Abstract method called during the onMouseMove event while dragging an 
1822      * object.
1823      * @method onDrag
1824      * @param {Event} e the mousemove event
1825      */
1826     onDrag: function(e) { /* override this */ },
1828     /**
1829      * Abstract method called when this element fist begins hovering over 
1830      * another DragDrop obj
1831      * @method onDragEnter
1832      * @param {Event} e the mousemove event
1833      * @param {String|DragDrop[]} id In POINT mode, the element
1834      * id this is hovering over.  In INTERSECT mode, an array of one or more 
1835      * dragdrop items being hovered over.
1836      */
1837     onDragEnter: function(e, id) { /* override this */ },
1839     /**
1840      * Code that executes immediately before the onDragOver event
1841      * @method b4DragOver
1842      * @private
1843      */
1844     b4DragOver: function(e) { },
1846     /**
1847      * Abstract method called when this element is hovering over another 
1848      * DragDrop obj
1849      * @method onDragOver
1850      * @param {Event} e the mousemove event
1851      * @param {String|DragDrop[]} id In POINT mode, the element
1852      * id this is hovering over.  In INTERSECT mode, an array of dd items 
1853      * being hovered over.
1854      */
1855     onDragOver: function(e, id) { /* override this */ },
1857     /**
1858      * Code that executes immediately before the onDragOut event
1859      * @method b4DragOut
1860      * @private
1861      */
1862     b4DragOut: function(e) { },
1864     /**
1865      * Abstract method called when we are no longer hovering over an element
1866      * @method onDragOut
1867      * @param {Event} e the mousemove event
1868      * @param {String|DragDrop[]} id In POINT mode, the element
1869      * id this was hovering over.  In INTERSECT mode, an array of dd items 
1870      * that the mouse is no longer over.
1871      */
1872     onDragOut: function(e, id) { /* override this */ },
1874     /**
1875      * Code that executes immediately before the onDragDrop event
1876      * @method b4DragDrop
1877      * @private
1878      */
1879     b4DragDrop: function(e) { },
1881     /**
1882      * Abstract method called when this item is dropped on another DragDrop 
1883      * obj
1884      * @method onDragDrop
1885      * @param {Event} e the mouseup event
1886      * @param {String|DragDrop[]} id In POINT mode, the element
1887      * id this was dropped on.  In INTERSECT mode, an array of dd items this 
1888      * was dropped on.
1889      */
1890     onDragDrop: function(e, id) { /* override this */ },
1892     /**
1893      * Abstract method called when this item is dropped on an area with no
1894      * drop target
1895      * @method onInvalidDrop
1896      * @param {Event} e the mouseup event
1897      */
1898     onInvalidDrop: function(e) { /* override this */ },
1900     /**
1901      * Code that executes immediately before the endDrag event
1902      * @method b4EndDrag
1903      * @private
1904      */
1905     b4EndDrag: function(e) { },
1907     /**
1908      * Fired when we are done dragging the object
1909      * @method endDrag
1910      * @param {Event} e the mouseup event
1911      */
1912     endDrag: function(e) { /* override this */ },
1914     /**
1915      * Code executed immediately before the onMouseDown event
1916      * @method b4MouseDown
1917      * @param {Event} e the mousedown event
1918      * @private
1919      */
1920     b4MouseDown: function(e) {  },
1922     /**
1923      * Event handler that fires when a drag/drop obj gets a mousedown
1924      * @method onMouseDown
1925      * @param {Event} e the mousedown event
1926      */
1927     onMouseDown: function(e) { /* override this */ },
1929     /**
1930      * Event handler that fires when a drag/drop obj gets a mouseup
1931      * @method onMouseUp
1932      * @param {Event} e the mouseup event
1933      */
1934     onMouseUp: function(e) { /* override this */ },
1935    
1936     /**
1937      * Override the onAvailable method to do what is needed after the initial
1938      * position was determined.
1939      * @method onAvailable
1940      */
1941     onAvailable: function () { 
1942         //this.logger.log("onAvailable (base)"); 
1943     },
1945     /**
1946      * Returns a reference to the linked element
1947      * @method getEl
1948      * @return {HTMLElement} the html element 
1949      */
1950     getEl: function() { 
1951         if (!this._domRef) {
1952             this._domRef = Dom.get(this.id); 
1953         }
1955         return this._domRef;
1956     },
1958     /**
1959      * Returns a reference to the actual element to drag.  By default this is
1960      * the same as the html element, but it can be assigned to another 
1961      * element. An example of this can be found in YAHOO.util.DDProxy
1962      * @method getDragEl
1963      * @return {HTMLElement} the html element 
1964      */
1965     getDragEl: function() {
1966         return Dom.get(this.dragElId);
1967     },
1969     /**
1970      * Sets up the DragDrop object.  Must be called in the constructor of any
1971      * YAHOO.util.DragDrop subclass
1972      * @method init
1973      * @param id the id of the linked element
1974      * @param {String} sGroup the group of related items
1975      * @param {object} config configuration attributes
1976      */
1977     init: function(id, sGroup, config) {
1978         this.initTarget(id, sGroup, config);
1979         Event.on(this._domRef || this.id, "mousedown", 
1980                         this.handleMouseDown, this, true);
1981         // Event.on(this.id, "selectstart", Event.preventDefault);
1982     },
1984     /**
1985      * Initializes Targeting functionality only... the object does not
1986      * get a mousedown handler.
1987      * @method initTarget
1988      * @param id the id of the linked element
1989      * @param {String} sGroup the group of related items
1990      * @param {object} config configuration attributes
1991      */
1992     initTarget: function(id, sGroup, config) {
1994         // configuration attributes 
1995         this.config = config || {};
1997         // create a local reference to the drag and drop manager
1998         this.DDM = YAHOO.util.DDM;
2000         // initialize the groups object
2001         this.groups = {};
2003         // assume that we have an element reference instead of an id if the
2004         // parameter is not a string
2005         if (typeof id !== "string") {
2006             YAHOO.log("id is not a string, assuming it is an HTMLElement");
2007             this._domRef = id;
2008             id = Dom.generateId(id);
2009         }
2011         // set the id
2012         this.id = id;
2014         // add to an interaction group
2015         this.addToGroup((sGroup) ? sGroup : "default");
2017         // We don't want to register this as the handle with the manager
2018         // so we just set the id rather than calling the setter.
2019         this.handleElId = id;
2021         Event.onAvailable(id, this.handleOnAvailable, this, true);
2023         // create a logger instance
2024         this.logger = (YAHOO.widget.LogWriter) ? 
2025                 new YAHOO.widget.LogWriter(this.toString()) : YAHOO;
2027         // the linked element is the element that gets dragged by default
2028         this.setDragElId(id); 
2030         // by default, clicked anchors will not start drag operations. 
2031         // @TODO what else should be here?  Probably form fields.
2032         this.invalidHandleTypes = { A: "A" };
2033         this.invalidHandleIds = {};
2034         this.invalidHandleClasses = [];
2036         this.applyConfig();
2037     },
2039     /**
2040      * Applies the configuration parameters that were passed into the constructor.
2041      * This is supposed to happen at each level through the inheritance chain.  So
2042      * a DDProxy implentation will execute apply config on DDProxy, DD, and 
2043      * DragDrop in order to get all of the parameters that are available in
2044      * each object.
2045      * @method applyConfig
2046      */
2047     applyConfig: function() {
2049         // configurable properties: 
2050         //    padding, isTarget, maintainOffset, primaryButtonOnly
2051         this.padding           = this.config.padding || [0, 0, 0, 0];
2052         this.isTarget          = (this.config.isTarget !== false);
2053         this.maintainOffset    = (this.config.maintainOffset);
2054         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
2056     },
2058     /**
2059      * Executed when the linked element is available
2060      * @method handleOnAvailable
2061      * @private
2062      */
2063     handleOnAvailable: function() {
2064         //this.logger.log("handleOnAvailable");
2065         this.available = true;
2066         this.resetConstraints();
2067         this.onAvailable();
2068     },
2070      /**
2071      * Configures the padding for the target zone in px.  Effectively expands
2072      * (or reduces) the virtual object size for targeting calculations.  
2073      * Supports css-style shorthand; if only one parameter is passed, all sides
2074      * will have that padding, and if only two are passed, the top and bottom
2075      * will have the first param, the left and right the second.
2076      * @method setPadding
2077      * @param {int} iTop    Top pad
2078      * @param {int} iRight  Right pad
2079      * @param {int} iBot    Bot pad
2080      * @param {int} iLeft   Left pad
2081      */
2082     setPadding: function(iTop, iRight, iBot, iLeft) {
2083         // this.padding = [iLeft, iRight, iTop, iBot];
2084         if (!iRight && 0 !== iRight) {
2085             this.padding = [iTop, iTop, iTop, iTop];
2086         } else if (!iBot && 0 !== iBot) {
2087             this.padding = [iTop, iRight, iTop, iRight];
2088         } else {
2089             this.padding = [iTop, iRight, iBot, iLeft];
2090         }
2091     },
2093     /**
2094      * Stores the initial placement of the linked element.
2095      * @method setInitialPosition
2096      * @param {int} diffX   the X offset, default 0
2097      * @param {int} diffY   the Y offset, default 0
2098      * @private
2099      */
2100     setInitPosition: function(diffX, diffY) {
2101         var el = this.getEl();
2103         if (!this.DDM.verifyEl(el)) {
2104             this.logger.log(this.id + " element is broken");
2105             return;
2106         }
2108         var dx = diffX || 0;
2109         var dy = diffY || 0;
2111         var p = Dom.getXY( el );
2113         this.initPageX = p[0] - dx;
2114         this.initPageY = p[1] - dy;
2116         this.lastPageX = p[0];
2117         this.lastPageY = p[1];
2119         this.logger.log(this.id + " initial position: " + this.initPageX + 
2120                 ", " + this.initPageY);
2123         this.setStartPosition(p);
2124     },
2126     /**
2127      * Sets the start position of the element.  This is set when the obj
2128      * is initialized, the reset when a drag is started.
2129      * @method setStartPosition
2130      * @param pos current position (from previous lookup)
2131      * @private
2132      */
2133     setStartPosition: function(pos) {
2134         var p = pos || Dom.getXY(this.getEl());
2136         this.deltaSetXY = null;
2138         this.startPageX = p[0];
2139         this.startPageY = p[1];
2140     },
2142     /**
2143      * Add this instance to a group of related drag/drop objects.  All 
2144      * instances belong to at least one group, and can belong to as many 
2145      * groups as needed.
2146      * @method addToGroup
2147      * @param sGroup {string} the name of the group
2148      */
2149     addToGroup: function(sGroup) {
2150         this.groups[sGroup] = true;
2151         this.DDM.regDragDrop(this, sGroup);
2152     },
2154     /**
2155      * Remove's this instance from the supplied interaction group
2156      * @method removeFromGroup
2157      * @param {string}  sGroup  The group to drop
2158      */
2159     removeFromGroup: function(sGroup) {
2160         this.logger.log("Removing from group: " + sGroup);
2161         if (this.groups[sGroup]) {
2162             delete this.groups[sGroup];
2163         }
2165         this.DDM.removeDDFromGroup(this, sGroup);
2166     },
2168     /**
2169      * Allows you to specify that an element other than the linked element 
2170      * will be moved with the cursor during a drag
2171      * @method setDragElId
2172      * @param id {string} the id of the element that will be used to initiate the drag
2173      */
2174     setDragElId: function(id) {
2175         this.dragElId = id;
2176     },
2178     /**
2179      * Allows you to specify a child of the linked element that should be 
2180      * used to initiate the drag operation.  An example of this would be if 
2181      * you have a content div with text and links.  Clicking anywhere in the 
2182      * content area would normally start the drag operation.  Use this method
2183      * to specify that an element inside of the content div is the element 
2184      * that starts the drag operation.
2185      * @method setHandleElId
2186      * @param id {string} the id of the element that will be used to 
2187      * initiate the drag.
2188      */
2189     setHandleElId: function(id) {
2190         if (typeof id !== "string") {
2191             YAHOO.log("id is not a string, assuming it is an HTMLElement");
2192             id = Dom.generateId(id);
2193         }
2194         this.handleElId = id;
2195         this.DDM.regHandle(this.id, id);
2196     },
2198     /**
2199      * Allows you to set an element outside of the linked element as a drag 
2200      * handle
2201      * @method setOuterHandleElId
2202      * @param id the id of the element that will be used to initiate the drag
2203      */
2204     setOuterHandleElId: function(id) {
2205         if (typeof id !== "string") {
2206             YAHOO.log("id is not a string, assuming it is an HTMLElement");
2207             id = Dom.generateId(id);
2208         }
2209         this.logger.log("Adding outer handle event: " + id);
2210         Event.on(id, "mousedown", 
2211                 this.handleMouseDown, this, true);
2212         this.setHandleElId(id);
2214         this.hasOuterHandles = true;
2215     },
2217     /**
2218      * Remove all drag and drop hooks for this element
2219      * @method unreg
2220      */
2221     unreg: function() {
2222         this.logger.log("DragDrop obj cleanup " + this.id);
2223         Event.removeListener(this.id, "mousedown", 
2224                 this.handleMouseDown);
2225         this._domRef = null;
2226         this.DDM._remove(this);
2227     },
2229     /**
2230      * Returns true if this instance is locked, or the drag drop mgr is locked
2231      * (meaning that all drag/drop is disabled on the page.)
2232      * @method isLocked
2233      * @return {boolean} true if this obj or all drag/drop is locked, else 
2234      * false
2235      */
2236     isLocked: function() {
2237         return (this.DDM.isLocked() || this.locked);
2238     },
2240     /**
2241      * Fired when this object is clicked
2242      * @method handleMouseDown
2243      * @param {Event} e 
2244      * @param {YAHOO.util.DragDrop} oDD the clicked dd object (this dd obj)
2245      * @private
2246      */
2247     handleMouseDown: function(e, oDD) {
2249         var button = e.which || e.button;
2250         this.logger.log("button: " + button);
2252         if (this.primaryButtonOnly && button > 1) {
2253             this.logger.log("Mousedown was not produced by the primary button");
2254             return;
2255         }
2257         if (this.isLocked()) {
2258             this.logger.log("Drag and drop is disabled, aborting");
2259             return;
2260         }
2262         this.logger.log("mousedown " + this.id);
2264         this.logger.log("firing onMouseDown events");
2266         // firing the mousedown events prior to calculating positions
2267         this.b4MouseDown(e);
2268         this.onMouseDown(e);
2270         this.DDM.refreshCache(this.groups);
2271         // var self = this;
2272         // setTimeout( function() { self.DDM.refreshCache(self.groups); }, 0);
2274         // Only process the event if we really clicked within the linked 
2275         // element.  The reason we make this check is that in the case that 
2276         // another element was moved between the clicked element and the 
2277         // cursor in the time between the mousedown and mouseup events. When 
2278         // this happens, the element gets the next mousedown event 
2279         // regardless of where on the screen it happened.  
2280         var pt = new YAHOO.util.Point(Event.getPageX(e), Event.getPageY(e));
2281         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
2282                 this.logger.log("Click was not over the element: " + this.id);
2283         } else {
2284             if (this.clickValidator(e)) {
2286                 this.logger.log("click was a valid handle");
2288                 // set the initial element position
2289                 this.setStartPosition();
2291                 // start tracking mousemove distance and mousedown time to
2292                 // determine when to start the actual drag
2293                 this.DDM.handleMouseDown(e, this);
2295                 // this mousedown is mine
2296                 this.DDM.stopEvent(e);
2297             } else {
2299 this.logger.log("clickValidator returned false, drag not initiated");
2301             }
2302         }
2303     },
2305     clickValidator: function(e) {
2306         var target = Event.getTarget(e);
2307         return ( this.isValidHandleChild(target) &&
2308                     (this.id == this.handleElId || 
2309                         this.DDM.handleWasClicked(target, this.id)) );
2310     },
2312     /**
2313      * Finds the location the element should be placed if we want to move
2314      * it to where the mouse location less the click offset would place us.
2315      * @method getTargetCoord
2316      * @param {int} iPageX the X coordinate of the click
2317      * @param {int} iPageY the Y coordinate of the click
2318      * @return an object that contains the coordinates (Object.x and Object.y)
2319      * @private
2320      */
2321     getTargetCoord: function(iPageX, iPageY) {
2323         // this.logger.log("getTargetCoord: " + iPageX + ", " + iPageY);
2325         var x = iPageX - this.deltaX;
2326         var y = iPageY - this.deltaY;
2328         if (this.constrainX) {
2329             if (x < this.minX) { x = this.minX; }
2330             if (x > this.maxX) { x = this.maxX; }
2331         }
2333         if (this.constrainY) {
2334             if (y < this.minY) { y = this.minY; }
2335             if (y > this.maxY) { y = this.maxY; }
2336         }
2338         x = this.getTick(x, this.xTicks);
2339         y = this.getTick(y, this.yTicks);
2341         // this.logger.log("getTargetCoord " + 
2342                 // " iPageX: " + iPageX +
2343                 // " iPageY: " + iPageY +
2344                 // " x: " + x + ", y: " + y);
2346         return {x:x, y:y};
2347     },
2349     /**
2350      * Allows you to specify a tag name that should not start a drag operation
2351      * when clicked.  This is designed to facilitate embedding links within a
2352      * drag handle that do something other than start the drag.
2353      * @method addInvalidHandleType
2354      * @param {string} tagName the type of element to exclude
2355      */
2356     addInvalidHandleType: function(tagName) {
2357         var type = tagName.toUpperCase();
2358         this.invalidHandleTypes[type] = type;
2359     },
2361     /**
2362      * Lets you to specify an element id for a child of a drag handle
2363      * that should not initiate a drag
2364      * @method addInvalidHandleId
2365      * @param {string} id the element id of the element you wish to ignore
2366      */
2367     addInvalidHandleId: function(id) {
2368         if (typeof id !== "string") {
2369             YAHOO.log("id is not a string, assuming it is an HTMLElement");
2370             id = Dom.generateId(id);
2371         }
2372         this.invalidHandleIds[id] = id;
2373     },
2376     /**
2377      * Lets you specify a css class of elements that will not initiate a drag
2378      * @method addInvalidHandleClass
2379      * @param {string} cssClass the class of the elements you wish to ignore
2380      */
2381     addInvalidHandleClass: function(cssClass) {
2382         this.invalidHandleClasses.push(cssClass);
2383     },
2385     /**
2386      * Unsets an excluded tag name set by addInvalidHandleType
2387      * @method removeInvalidHandleType
2388      * @param {string} tagName the type of element to unexclude
2389      */
2390     removeInvalidHandleType: function(tagName) {
2391         var type = tagName.toUpperCase();
2392         // this.invalidHandleTypes[type] = null;
2393         delete this.invalidHandleTypes[type];
2394     },
2395     
2396     /**
2397      * Unsets an invalid handle id
2398      * @method removeInvalidHandleId
2399      * @param {string} id the id of the element to re-enable
2400      */
2401     removeInvalidHandleId: function(id) {
2402         if (typeof id !== "string") {
2403             YAHOO.log("id is not a string, assuming it is an HTMLElement");
2404             id = Dom.generateId(id);
2405         }
2406         delete this.invalidHandleIds[id];
2407     },
2409     /**
2410      * Unsets an invalid css class
2411      * @method removeInvalidHandleClass
2412      * @param {string} cssClass the class of the element(s) you wish to 
2413      * re-enable
2414      */
2415     removeInvalidHandleClass: function(cssClass) {
2416         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
2417             if (this.invalidHandleClasses[i] == cssClass) {
2418                 delete this.invalidHandleClasses[i];
2419             }
2420         }
2421     },
2423     /**
2424      * Checks the tag exclusion list to see if this click should be ignored
2425      * @method isValidHandleChild
2426      * @param {HTMLElement} node the HTMLElement to evaluate
2427      * @return {boolean} true if this is a valid tag type, false if not
2428      */
2429     isValidHandleChild: function(node) {
2431         var valid = true;
2432         // var n = (node.nodeName == "#text") ? node.parentNode : node;
2433         var nodeName;
2434         try {
2435             nodeName = node.nodeName.toUpperCase();
2436         } catch(e) {
2437             nodeName = node.nodeName;
2438         }
2439         valid = valid && !this.invalidHandleTypes[nodeName];
2440         valid = valid && !this.invalidHandleIds[node.id];
2442         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
2443             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
2444         }
2446         this.logger.log("Valid handle? ... " + valid);
2448         return valid;
2450     },
2452     /**
2453      * Create the array of horizontal tick marks if an interval was specified
2454      * in setXConstraint().
2455      * @method setXTicks
2456      * @private
2457      */
2458     setXTicks: function(iStartX, iTickSize) {
2459         this.xTicks = [];
2460         this.xTickSize = iTickSize;
2461         
2462         var tickMap = {};
2464         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
2465             if (!tickMap[i]) {
2466                 this.xTicks[this.xTicks.length] = i;
2467                 tickMap[i] = true;
2468             }
2469         }
2471         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
2472             if (!tickMap[i]) {
2473                 this.xTicks[this.xTicks.length] = i;
2474                 tickMap[i] = true;
2475             }
2476         }
2478         this.xTicks.sort(this.DDM.numericSort) ;
2479         this.logger.log("xTicks: " + this.xTicks.join());
2480     },
2482     /**
2483      * Create the array of vertical tick marks if an interval was specified in 
2484      * setYConstraint().
2485      * @method setYTicks
2486      * @private
2487      */
2488     setYTicks: function(iStartY, iTickSize) {
2489         // this.logger.log("setYTicks: " + iStartY + ", " + iTickSize
2490                // + ", " + this.initPageY + ", " + this.minY + ", " + this.maxY );
2491         this.yTicks = [];
2492         this.yTickSize = iTickSize;
2494         var tickMap = {};
2496         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
2497             if (!tickMap[i]) {
2498                 this.yTicks[this.yTicks.length] = i;
2499                 tickMap[i] = true;
2500             }
2501         }
2503         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
2504             if (!tickMap[i]) {
2505                 this.yTicks[this.yTicks.length] = i;
2506                 tickMap[i] = true;
2507             }
2508         }
2510         this.yTicks.sort(this.DDM.numericSort) ;
2511         this.logger.log("yTicks: " + this.yTicks.join());
2512     },
2514     /**
2515      * By default, the element can be dragged any place on the screen.  Use 
2516      * this method to limit the horizontal travel of the element.  Pass in 
2517      * 0,0 for the parameters if you want to lock the drag to the y axis.
2518      * @method setXConstraint
2519      * @param {int} iLeft the number of pixels the element can move to the left
2520      * @param {int} iRight the number of pixels the element can move to the 
2521      * right
2522      * @param {int} iTickSize optional parameter for specifying that the 
2523      * element
2524      * should move iTickSize pixels at a time.
2525      */
2526     setXConstraint: function(iLeft, iRight, iTickSize) {
2527         this.leftConstraint = parseInt(iLeft, 10);
2528         this.rightConstraint = parseInt(iRight, 10);
2530         this.minX = this.initPageX - this.leftConstraint;
2531         this.maxX = this.initPageX + this.rightConstraint;
2532         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
2534         this.constrainX = true;
2535         this.logger.log("initPageX:" + this.initPageX + " minX:" + this.minX + 
2536                 " maxX:" + this.maxX);
2537     },
2539     /**
2540      * Clears any constraints applied to this instance.  Also clears ticks
2541      * since they can't exist independent of a constraint at this time.
2542      * @method clearConstraints
2543      */
2544     clearConstraints: function() {
2545         this.logger.log("Clearing constraints");
2546         this.constrainX = false;
2547         this.constrainY = false;
2548         this.clearTicks();
2549     },
2551     /**
2552      * Clears any tick interval defined for this instance
2553      * @method clearTicks
2554      */
2555     clearTicks: function() {
2556         this.logger.log("Clearing ticks");
2557         this.xTicks = null;
2558         this.yTicks = null;
2559         this.xTickSize = 0;
2560         this.yTickSize = 0;
2561     },
2563     /**
2564      * By default, the element can be dragged any place on the screen.  Set 
2565      * this to limit the vertical travel of the element.  Pass in 0,0 for the
2566      * parameters if you want to lock the drag to the x axis.
2567      * @method setYConstraint
2568      * @param {int} iUp the number of pixels the element can move up
2569      * @param {int} iDown the number of pixels the element can move down
2570      * @param {int} iTickSize optional parameter for specifying that the 
2571      * element should move iTickSize pixels at a time.
2572      */
2573     setYConstraint: function(iUp, iDown, iTickSize) {
2574         this.logger.log("setYConstraint: " + iUp + "," + iDown + "," + iTickSize);
2575         this.topConstraint = parseInt(iUp, 10);
2576         this.bottomConstraint = parseInt(iDown, 10);
2578         this.minY = this.initPageY - this.topConstraint;
2579         this.maxY = this.initPageY + this.bottomConstraint;
2580         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
2582         this.constrainY = true;
2583         
2584         this.logger.log("initPageY:" + this.initPageY + " minY:" + this.minY + 
2585                 " maxY:" + this.maxY);
2586     },
2588     /**
2589      * resetConstraints must be called if you manually reposition a dd element.
2590      * @method resetConstraints
2591      */
2592     resetConstraints: function() {
2594         //this.logger.log("resetConstraints");
2596         // Maintain offsets if necessary
2597         if (this.initPageX || this.initPageX === 0) {
2598             //this.logger.log("init pagexy: " + this.initPageX + ", " + 
2599                                //this.initPageY);
2600             //this.logger.log("last pagexy: " + this.lastPageX + ", " + 
2601                                //this.lastPageY);
2602             // figure out how much this thing has moved
2603             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
2604             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
2606             this.setInitPosition(dx, dy);
2608         // This is the first time we have detected the element's position
2609         } else {
2610             this.setInitPosition();
2611         }
2613         if (this.constrainX) {
2614             this.setXConstraint( this.leftConstraint, 
2615                                  this.rightConstraint, 
2616                                  this.xTickSize        );
2617         }
2619         if (this.constrainY) {
2620             this.setYConstraint( this.topConstraint, 
2621                                  this.bottomConstraint, 
2622                                  this.yTickSize         );
2623         }
2624     },
2626     /**
2627      * Normally the drag element is moved pixel by pixel, but we can specify 
2628      * that it move a number of pixels at a time.  This method resolves the 
2629      * location when we have it set up like this.
2630      * @method getTick
2631      * @param {int} val where we want to place the object
2632      * @param {int[]} tickArray sorted array of valid points
2633      * @return {int} the closest tick
2634      * @private
2635      */
2636     getTick: function(val, tickArray) {
2638         if (!tickArray) {
2639             // If tick interval is not defined, it is effectively 1 pixel, 
2640             // so we return the value passed to us.
2641             return val; 
2642         } else if (tickArray[0] >= val) {
2643             // The value is lower than the first tick, so we return the first
2644             // tick.
2645             return tickArray[0];
2646         } else {
2647             for (var i=0, len=tickArray.length; i<len; ++i) {
2648                 var next = i + 1;
2649                 if (tickArray[next] && tickArray[next] >= val) {
2650                     var diff1 = val - tickArray[i];
2651                     var diff2 = tickArray[next] - val;
2652                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
2653                 }
2654             }
2656             // The value is larger than the last tick, so we return the last
2657             // tick.
2658             return tickArray[tickArray.length - 1];
2659         }
2660     },
2662     /**
2663      * toString method
2664      * @method toString
2665      * @return {string} string representation of the dd obj
2666      */
2667     toString: function() {
2668         return ("DragDrop " + this.id);
2669     }
2673 })();
2675  * A DragDrop implementation where the linked element follows the 
2676  * mouse cursor during a drag.
2677  * @class DD
2678  * @extends YAHOO.util.DragDrop
2679  * @constructor
2680  * @param {String} id the id of the linked element 
2681  * @param {String} sGroup the group of related DragDrop items
2682  * @param {object} config an object containing configurable attributes
2683  *                Valid properties for DD: 
2684  *                    scroll
2685  */
2686 YAHOO.util.DD = function(id, sGroup, config) {
2687     if (id) {
2688         this.init(id, sGroup, config);
2689     }
2692 YAHOO.extend(YAHOO.util.DD, YAHOO.util.DragDrop, {
2694     /**
2695      * When set to true, the utility automatically tries to scroll the browser
2696      * window wehn a drag and drop element is dragged near the viewport boundary.
2697      * Defaults to true.
2698      * @property scroll
2699      * @type boolean
2700      */
2701     scroll: true, 
2703     /**
2704      * Sets the pointer offset to the distance between the linked element's top 
2705      * left corner and the location the element was clicked
2706      * @method autoOffset
2707      * @param {int} iPageX the X coordinate of the click
2708      * @param {int} iPageY the Y coordinate of the click
2709      */
2710     autoOffset: function(iPageX, iPageY) {
2711         var x = iPageX - this.startPageX;
2712         var y = iPageY - this.startPageY;
2713         this.setDelta(x, y);
2714         // this.logger.log("autoOffset el pos: " + aCoord + ", delta: " + x + "," + y);
2715     },
2717     /** 
2718      * Sets the pointer offset.  You can call this directly to force the 
2719      * offset to be in a particular location (e.g., pass in 0,0 to set it 
2720      * to the center of the object, as done in YAHOO.widget.Slider)
2721      * @method setDelta
2722      * @param {int} iDeltaX the distance from the left
2723      * @param {int} iDeltaY the distance from the top
2724      */
2725     setDelta: function(iDeltaX, iDeltaY) {
2726         this.deltaX = iDeltaX;
2727         this.deltaY = iDeltaY;
2728         this.logger.log("deltaX:" + this.deltaX + ", deltaY:" + this.deltaY);
2729     },
2731     /**
2732      * Sets the drag element to the location of the mousedown or click event, 
2733      * maintaining the cursor location relative to the location on the element 
2734      * that was clicked.  Override this if you want to place the element in a 
2735      * location other than where the cursor is.
2736      * @method setDragElPos
2737      * @param {int} iPageX the X coordinate of the mousedown or drag event
2738      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2739      */
2740     setDragElPos: function(iPageX, iPageY) {
2741         // the first time we do this, we are going to check to make sure
2742         // the element has css positioning
2744         var el = this.getDragEl();
2745         this.alignElWithMouse(el, iPageX, iPageY);
2746     },
2748     /**
2749      * Sets the element to the location of the mousedown or click event, 
2750      * maintaining the cursor location relative to the location on the element 
2751      * that was clicked.  Override this if you want to place the element in a 
2752      * location other than where the cursor is.
2753      * @method alignElWithMouse
2754      * @param {HTMLElement} el the element to move
2755      * @param {int} iPageX the X coordinate of the mousedown or drag event
2756      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2757      */
2758     alignElWithMouse: function(el, iPageX, iPageY) {
2759         var oCoord = this.getTargetCoord(iPageX, iPageY);
2760         // this.logger.log("****alignElWithMouse : " + el.id + ", " + aCoord + ", " + el.style.display);
2762         if (!this.deltaSetXY) {
2763             var aCoord = [oCoord.x, oCoord.y];
2764             YAHOO.util.Dom.setXY(el, aCoord);
2765             var newLeft = parseInt( YAHOO.util.Dom.getStyle(el, "left"), 10 );
2766             var newTop  = parseInt( YAHOO.util.Dom.getStyle(el, "top" ), 10 );
2768             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2769         } else {
2770             YAHOO.util.Dom.setStyle(el, "left", (oCoord.x + this.deltaSetXY[0]) + "px");
2771             YAHOO.util.Dom.setStyle(el, "top",  (oCoord.y + this.deltaSetXY[1]) + "px");
2772         }
2773         
2774         this.cachePosition(oCoord.x, oCoord.y);
2775         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2776     },
2778     /**
2779      * Saves the most recent position so that we can reset the constraints and
2780      * tick marks on-demand.  We need to know this so that we can calculate the
2781      * number of pixels the element is offset from its original position.
2782      * @method cachePosition
2783      * @param iPageX the current x position (optional, this just makes it so we
2784      * don't have to look it up again)
2785      * @param iPageY the current y position (optional, this just makes it so we
2786      * don't have to look it up again)
2787      */
2788     cachePosition: function(iPageX, iPageY) {
2789         if (iPageX) {
2790             this.lastPageX = iPageX;
2791             this.lastPageY = iPageY;
2792         } else {
2793             var aCoord = YAHOO.util.Dom.getXY(this.getEl());
2794             this.lastPageX = aCoord[0];
2795             this.lastPageY = aCoord[1];
2796         }
2797     },
2799     /**
2800      * Auto-scroll the window if the dragged object has been moved beyond the 
2801      * visible window boundary.
2802      * @method autoScroll
2803      * @param {int} x the drag element's x position
2804      * @param {int} y the drag element's y position
2805      * @param {int} h the height of the drag element
2806      * @param {int} w the width of the drag element
2807      * @private
2808      */
2809     autoScroll: function(x, y, h, w) {
2811         if (this.scroll) {
2812             // The client height
2813             var clientH = this.DDM.getClientHeight();
2815             // The client width
2816             var clientW = this.DDM.getClientWidth();
2818             // The amt scrolled down
2819             var st = this.DDM.getScrollTop();
2821             // The amt scrolled right
2822             var sl = this.DDM.getScrollLeft();
2824             // Location of the bottom of the element
2825             var bot = h + y;
2827             // Location of the right of the element
2828             var right = w + x;
2830             // The distance from the cursor to the bottom of the visible area, 
2831             // adjusted so that we don't scroll if the cursor is beyond the
2832             // element drag constraints
2833             var toBot = (clientH + st - y - this.deltaY);
2835             // The distance from the cursor to the right of the visible area
2836             var toRight = (clientW + sl - x - this.deltaX);
2838             // this.logger.log( " x: " + x + " y: " + y + " h: " + h + 
2839             // " clientH: " + clientH + " clientW: " + clientW + 
2840             // " st: " + st + " sl: " + sl + " bot: " + bot + 
2841             // " right: " + right + " toBot: " + toBot + " toRight: " + toRight);
2843             // How close to the edge the cursor must be before we scroll
2844             // var thresh = (document.all) ? 100 : 40;
2845             var thresh = 40;
2847             // How many pixels to scroll per autoscroll op.  This helps to reduce 
2848             // clunky scrolling. IE is more sensitive about this ... it needs this 
2849             // value to be higher.
2850             var scrAmt = (document.all) ? 80 : 30;
2852             // Scroll down if we are near the bottom of the visible page and the 
2853             // obj extends below the crease
2854             if ( bot > clientH && toBot < thresh ) { 
2855                 window.scrollTo(sl, st + scrAmt); 
2856             }
2858             // Scroll up if the window is scrolled down and the top of the object
2859             // goes above the top border
2860             if ( y < st && st > 0 && y - st < thresh ) { 
2861                 window.scrollTo(sl, st - scrAmt); 
2862             }
2864             // Scroll right if the obj is beyond the right border and the cursor is
2865             // near the border.
2866             if ( right > clientW && toRight < thresh ) { 
2867                 window.scrollTo(sl + scrAmt, st); 
2868             }
2870             // Scroll left if the window has been scrolled to the right and the obj
2871             // extends past the left border
2872             if ( x < sl && sl > 0 && x - sl < thresh ) { 
2873                 window.scrollTo(sl - scrAmt, st);
2874             }
2875         }
2876     },
2878     /*
2879      * Sets up config options specific to this class. Overrides
2880      * YAHOO.util.DragDrop, but all versions of this method through the 
2881      * inheritance chain are called
2882      */
2883     applyConfig: function() {
2884         YAHOO.util.DD.superclass.applyConfig.call(this);
2885         this.scroll = (this.config.scroll !== false);
2886     },
2888     /*
2889      * Event that fires prior to the onMouseDown event.  Overrides 
2890      * YAHOO.util.DragDrop.
2891      */
2892     b4MouseDown: function(e) {
2893         this.setStartPosition();
2894         // this.resetConstraints();
2895         this.autoOffset(YAHOO.util.Event.getPageX(e), 
2896                             YAHOO.util.Event.getPageY(e));
2897     },
2899     /*
2900      * Event that fires prior to the onDrag event.  Overrides 
2901      * YAHOO.util.DragDrop.
2902      */
2903     b4Drag: function(e) {
2904         this.setDragElPos(YAHOO.util.Event.getPageX(e), 
2905                             YAHOO.util.Event.getPageY(e));
2906     },
2908     toString: function() {
2909         return ("DD " + this.id);
2910     }
2912     //////////////////////////////////////////////////////////////////////////
2913     // Debugging ygDragDrop events that can be overridden
2914     //////////////////////////////////////////////////////////////////////////
2915     /*
2916     startDrag: function(x, y) {
2917         this.logger.log(this.id.toString()  + " startDrag");
2918     },
2920     onDrag: function(e) {
2921         this.logger.log(this.id.toString() + " onDrag");
2922     },
2924     onDragEnter: function(e, id) {
2925         this.logger.log(this.id.toString() + " onDragEnter: " + id);
2926     },
2928     onDragOver: function(e, id) {
2929         this.logger.log(this.id.toString() + " onDragOver: " + id);
2930     },
2932     onDragOut: function(e, id) {
2933         this.logger.log(this.id.toString() + " onDragOut: " + id);
2934     },
2936     onDragDrop: function(e, id) {
2937         this.logger.log(this.id.toString() + " onDragDrop: " + id);
2938     },
2940     endDrag: function(e) {
2941         this.logger.log(this.id.toString() + " endDrag");
2942     }
2944     */
2948  * A DragDrop implementation that inserts an empty, bordered div into
2949  * the document that follows the cursor during drag operations.  At the time of
2950  * the click, the frame div is resized to the dimensions of the linked html
2951  * element, and moved to the exact location of the linked element.
2953  * References to the "frame" element refer to the single proxy element that
2954  * was created to be dragged in place of all DDProxy elements on the
2955  * page.
2957  * @class DDProxy
2958  * @extends YAHOO.util.DD
2959  * @constructor
2960  * @param {String} id the id of the linked html element
2961  * @param {String} sGroup the group of related DragDrop objects
2962  * @param {object} config an object containing configurable attributes
2963  *                Valid properties for DDProxy in addition to those in DragDrop: 
2964  *                   resizeFrame, centerFrame, dragElId
2965  */
2966 YAHOO.util.DDProxy = function(id, sGroup, config) {
2967     if (id) {
2968         this.init(id, sGroup, config);
2969         this.initFrame(); 
2970     }
2974  * The default drag frame div id
2975  * @property YAHOO.util.DDProxy.dragElId
2976  * @type String
2977  * @static
2978  */
2979 YAHOO.util.DDProxy.dragElId = "ygddfdiv";
2981 YAHOO.extend(YAHOO.util.DDProxy, YAHOO.util.DD, {
2983     /**
2984      * By default we resize the drag frame to be the same size as the element
2985      * we want to drag (this is to get the frame effect).  We can turn it off
2986      * if we want a different behavior.
2987      * @property resizeFrame
2988      * @type boolean
2989      */
2990     resizeFrame: true,
2992     /**
2993      * By default the frame is positioned exactly where the drag element is, so
2994      * we use the cursor offset provided by YAHOO.util.DD.  Another option that works only if
2995      * you do not have constraints on the obj is to have the drag frame centered
2996      * around the cursor.  Set centerFrame to true for this effect.
2997      * @property centerFrame
2998      * @type boolean
2999      */
3000     centerFrame: false,
3002     /**
3003      * Creates the proxy element if it does not yet exist
3004      * @method createFrame
3005      */
3006     createFrame: function() {
3007         var self=this, body=document.body;
3009         if (!body || !body.firstChild) {
3010             setTimeout( function() { self.createFrame(); }, 50 );
3011             return;
3012         }
3014         var div=this.getDragEl(), Dom=YAHOO.util.Dom;
3016         if (!div) {
3017             div    = document.createElement("div");
3018             div.id = this.dragElId;
3019             var s  = div.style;
3021             s.position   = "absolute";
3022             s.visibility = "hidden";
3023             s.cursor     = "move";
3024             s.border     = "2px solid #aaa";
3025             s.zIndex     = 999;
3026             s.height     = "25px";
3027             s.width      = "25px";
3029             var _data = document.createElement('div');
3030             Dom.setStyle(_data, 'height', '100%');
3031             Dom.setStyle(_data, 'width', '100%');
3032             /**
3033             * If the proxy element has no background-color, then it is considered to the "transparent" by Internet Explorer.
3034             * Since it is "transparent" then the events pass through it to the iframe below.
3035             * So creating a "fake" div inside the proxy element and giving it a background-color, then setting it to an
3036             * opacity of 0, it appears to not be there, however IE still thinks that it is so the events never pass through.
3037             */
3038             Dom.setStyle(_data, 'background-color', '#ccc');
3039             Dom.setStyle(_data, 'opacity', '0');
3040             div.appendChild(_data);
3042             // appendChild can blow up IE if invoked prior to the window load event
3043             // while rendering a table.  It is possible there are other scenarios 
3044             // that would cause this to happen as well.
3045             body.insertBefore(div, body.firstChild);
3046         }
3047     },
3049     /**
3050      * Initialization for the drag frame element.  Must be called in the
3051      * constructor of all subclasses
3052      * @method initFrame
3053      */
3054     initFrame: function() {
3055         this.createFrame();
3056     },
3058     applyConfig: function() {
3059         //this.logger.log("DDProxy applyConfig");
3060         YAHOO.util.DDProxy.superclass.applyConfig.call(this);
3062         this.resizeFrame = (this.config.resizeFrame !== false);
3063         this.centerFrame = (this.config.centerFrame);
3064         this.setDragElId(this.config.dragElId || YAHOO.util.DDProxy.dragElId);
3065     },
3067     /**
3068      * Resizes the drag frame to the dimensions of the clicked object, positions 
3069      * it over the object, and finally displays it
3070      * @method showFrame
3071      * @param {int} iPageX X click position
3072      * @param {int} iPageY Y click position
3073      * @private
3074      */
3075     showFrame: function(iPageX, iPageY) {
3076         var el = this.getEl();
3077         var dragEl = this.getDragEl();
3078         var s = dragEl.style;
3080         this._resizeProxy();
3082         if (this.centerFrame) {
3083             this.setDelta( Math.round(parseInt(s.width,  10)/2), 
3084                            Math.round(parseInt(s.height, 10)/2) );
3085         }
3087         this.setDragElPos(iPageX, iPageY);
3089         YAHOO.util.Dom.setStyle(dragEl, "visibility", "visible"); 
3090     },
3092     /**
3093      * The proxy is automatically resized to the dimensions of the linked
3094      * element when a drag is initiated, unless resizeFrame is set to false
3095      * @method _resizeProxy
3096      * @private
3097      */
3098     _resizeProxy: function() {
3099         if (this.resizeFrame) {
3100             var DOM    = YAHOO.util.Dom;
3101             var el     = this.getEl();
3102             var dragEl = this.getDragEl();
3104             var bt = parseInt( DOM.getStyle(dragEl, "borderTopWidth"    ), 10);
3105             var br = parseInt( DOM.getStyle(dragEl, "borderRightWidth"  ), 10);
3106             var bb = parseInt( DOM.getStyle(dragEl, "borderBottomWidth" ), 10);
3107             var bl = parseInt( DOM.getStyle(dragEl, "borderLeftWidth"   ), 10);
3109             if (isNaN(bt)) { bt = 0; }
3110             if (isNaN(br)) { br = 0; }
3111             if (isNaN(bb)) { bb = 0; }
3112             if (isNaN(bl)) { bl = 0; }
3114             this.logger.log("proxy size: " + bt + "  " + br + " " + bb + " " + bl);
3116             var newWidth  = Math.max(0, el.offsetWidth  - br - bl);                                                                                           
3117             var newHeight = Math.max(0, el.offsetHeight - bt - bb);
3119             this.logger.log("Resizing proxy element");
3121             DOM.setStyle( dragEl, "width",  newWidth  + "px" );
3122             DOM.setStyle( dragEl, "height", newHeight + "px" );
3123         }
3124     },
3126     // overrides YAHOO.util.DragDrop
3127     b4MouseDown: function(e) {
3128         this.setStartPosition();
3129         var x = YAHOO.util.Event.getPageX(e);
3130         var y = YAHOO.util.Event.getPageY(e);
3131         this.autoOffset(x, y);
3133         // This causes the autoscroll code to kick off, which means autoscroll can
3134         // happen prior to the check for a valid drag handle.
3135         // this.setDragElPos(x, y);
3136     },
3138     // overrides YAHOO.util.DragDrop
3139     b4StartDrag: function(x, y) {
3140         // show the drag frame
3141         this.logger.log("start drag show frame, x: " + x + ", y: " + y);
3142         this.showFrame(x, y);
3143     },
3145     // overrides YAHOO.util.DragDrop
3146     b4EndDrag: function(e) {
3147         this.logger.log(this.id + " b4EndDrag");
3148         YAHOO.util.Dom.setStyle(this.getDragEl(), "visibility", "hidden"); 
3149     },
3151     // overrides YAHOO.util.DragDrop
3152     // By default we try to move the element to the last location of the frame.  
3153     // This is so that the default behavior mirrors that of YAHOO.util.DD.  
3154     endDrag: function(e) {
3155         var DOM = YAHOO.util.Dom;
3156         this.logger.log(this.id + " endDrag");
3157         var lel = this.getEl();
3158         var del = this.getDragEl();
3160         // Show the drag frame briefly so we can get its position
3161         // del.style.visibility = "";
3162         DOM.setStyle(del, "visibility", ""); 
3164         // Hide the linked element before the move to get around a Safari 
3165         // rendering bug.
3166         //lel.style.visibility = "hidden";
3167         DOM.setStyle(lel, "visibility", "hidden"); 
3168         YAHOO.util.DDM.moveToEl(lel, del);
3169         //del.style.visibility = "hidden";
3170         DOM.setStyle(del, "visibility", "hidden"); 
3171         //lel.style.visibility = "";
3172         DOM.setStyle(lel, "visibility", ""); 
3173     },
3175     toString: function() {
3176         return ("DDProxy " + this.id);
3177     }
3181  * A DragDrop implementation that does not move, but can be a drop 
3182  * target.  You would get the same result by simply omitting implementation 
3183  * for the event callbacks, but this way we reduce the processing cost of the 
3184  * event listener and the callbacks.
3185  * @class DDTarget
3186  * @extends YAHOO.util.DragDrop 
3187  * @constructor
3188  * @param {String} id the id of the element that is a drop target
3189  * @param {String} sGroup the group of related DragDrop objects
3190  * @param {object} config an object containing configurable attributes
3191  *                 Valid properties for DDTarget in addition to those in 
3192  *                 DragDrop: 
3193  *                    none
3194  */
3195 YAHOO.util.DDTarget = function(id, sGroup, config) {
3196     if (id) {
3197         this.initTarget(id, sGroup, config);
3198     }
3201 // YAHOO.util.DDTarget.prototype = new YAHOO.util.DragDrop();
3202 YAHOO.extend(YAHOO.util.DDTarget, YAHOO.util.DragDrop, {
3203     toString: function() {
3204         return ("DDTarget " + this.id);
3205     }
3207 YAHOO.register("dragdrop", YAHOO.util.DragDropMgr, {version: "2.3.0", build: "442"});