Project

General

Profile

1
/*
2
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.net/yui/license.txt
5
version: 2.8.0r4
6
*/
7
/**
8
 * The drag and drop utility provides a framework for building drag and drop
9
 * applications.  In addition to enabling drag and drop for specific elements,
10
 * the drag and drop elements are tracked by the manager class, and the
11
 * interactions between the various elements are tracked during the drag and
12
 * the implementing code is notified about these important moments.
13
 * @module dragdrop
14
 * @title Drag and Drop
15
 * @requires yahoo,dom,event
16
 * @namespace YAHOO.util
17
 */
18

    
19
// Only load the library once.  Rewriting the manager class would orphan 
20
// existing drag and drop instances.
21
if (!YAHOO.util.DragDropMgr) {
22

    
23
/**
24
 * DragDropMgr is a singleton that tracks the element interaction for 
25
 * all DragDrop items in the window.  Generally, you will not call 
26
 * this class directly, but it does have helper methods that could 
27
 * be useful in your DragDrop implementations.
28
 * @class DragDropMgr
29
 * @static
30
 */
31
YAHOO.util.DragDropMgr = function() {
32

    
33
    var Event = YAHOO.util.Event,
34
        Dom = YAHOO.util.Dom;
35

    
36
    return {
37
        /**
38
        * This property is used to turn on global use of the shim element on all DragDrop instances, defaults to false for backcompat. (Use: YAHOO.util.DDM.useShim = true)
39
       * @property useShim
40
       * @type Boolean
41
       * @static
42
        */
43
        useShim: false,
44
        /**
45
        * This property is used to determine if the shim is active over the screen, default false.
46
       * @private
47
       * @property _shimActive
48
       * @type Boolean
49
       * @static
50
        */
51
        _shimActive: false,
52
        /**
53
        * This property is used when useShim is set on a DragDrop object to store the current state of DDM.useShim so it can be reset when a drag operation is done.
54
       * @private
55
       * @property _shimState
56
       * @type Boolean
57
       * @static
58
        */
59
        _shimState: false,
60
        /**
61
        * This property is used when useShim is set to true, it will set the opacity on the shim to .5 for debugging. Use: (YAHOO.util.DDM._debugShim = true;)
62
       * @private
63
       * @property _debugShim
64
       * @type Boolean
65
       * @static
66
        */
67
        _debugShim: false,
68
        /**
69
        * This method will create a shim element (giving it the id of yui-ddm-shim), it also attaches the mousemove and mouseup listeners to it and attaches a scroll listener on the window
70
       * @private
71
       * @method _sizeShim
72
       * @static
73
        */
74
        _createShim: function() {
75
            var s = document.createElement('div');
76
            s.id = 'yui-ddm-shim';
77
            if (document.body.firstChild) {
78
                document.body.insertBefore(s, document.body.firstChild);
79
            } else {
80
                document.body.appendChild(s);
81
            }
82
            s.style.display = 'none';
83
            s.style.backgroundColor = 'red';
84
            s.style.position = 'absolute';
85
            s.style.zIndex = '99999';
86
            Dom.setStyle(s, 'opacity', '0');
87
            this._shim = s;
88
            Event.on(s, "mouseup",   this.handleMouseUp, this, true);
89
            Event.on(s, "mousemove", this.handleMouseMove, this, true);
90
            Event.on(window, 'scroll', this._sizeShim, this, true);
91
        },
92
        /**
93
        * This method will size the shim, called from activate and on window scroll event
94
       * @private
95
       * @method _sizeShim
96
       * @static
97
        */
98
        _sizeShim: function() {
99
            if (this._shimActive) {
100
                var s = this._shim;
101
                s.style.height = Dom.getDocumentHeight() + 'px';
102
                s.style.width = Dom.getDocumentWidth() + 'px';
103
                s.style.top = '0';
104
                s.style.left = '0';
105
            }
106
        },
107
        /**
108
        * This method will create the shim element if needed, then show the shim element, size the element and set the _shimActive property to true
109
       * @private
110
       * @method _activateShim
111
       * @static
112
        */
113
        _activateShim: function() {
114
            if (this.useShim) {
115
                if (!this._shim) {
116
                    this._createShim();
117
                }
118
                this._shimActive = true;
119
                var s = this._shim,
120
                    o = '0';
121
                if (this._debugShim) {
122
                    o = '.5';
123
                }
124
                Dom.setStyle(s, 'opacity', o);
125
                this._sizeShim();
126
                s.style.display = 'block';
127
            }
128
        },
129
        /**
130
        * This method will hide the shim element and set the _shimActive property to false
131
       * @private
132
       * @method _deactivateShim
133
       * @static
134
        */
135
        _deactivateShim: function() {
136
            this._shim.style.display = 'none';
137
            this._shimActive = false;
138
        },
139
        /**
140
        * The HTML element created to use as a shim over the document to track mouse movements
141
       * @private
142
       * @property _shim
143
       * @type HTMLElement
144
       * @static
145
        */
146
        _shim: null,
147
        /**
148
         * Two dimensional Array of registered DragDrop objects.  The first 
149
         * dimension is the DragDrop item group, the second the DragDrop 
150
         * object.
151
        * @property ids
152
        * @type {string: string}
153
        * @private
154
        * @static
155
         */
156
        ids: {},
157

    
158
        /**
159
         * Array of element ids defined as drag handles.  Used to determine 
160
         * if the element that generated the mousedown event is actually the 
161
         * handle and not the html element itself.
162
        * @property handleIds
163
        * @type {string: string}
164
        * @private
165
        * @static
166
         */
167
        handleIds: {},
168

    
169
        /**
170
         * the DragDrop object that is currently being dragged
171
        * @property dragCurrent
172
        * @type DragDrop
173
        * @private
174
        * @static
175
         **/
176
        dragCurrent: null,
177

    
178
        /**
179
         * the DragDrop object(s) that are being hovered over
180
        * @property dragOvers
181
        * @type Array
182
        * @private
183
        * @static
184
         */
185
        dragOvers: {},
186

    
187
        /**
188
         * the X distance between the cursor and the object being dragged
189
        * @property deltaX
190
        * @type int
191
        * @private
192
        * @static
193
         */
194
        deltaX: 0,
195

    
196
        /**
197
         * the Y distance between the cursor and the object being dragged
198
        * @property deltaY
199
        * @type int
200
        * @private
201
        * @static
202
         */
203
        deltaY: 0,
204

    
205
        /**
206
         * Flag to determine if we should prevent the default behavior of the
207
         * events we define. By default this is true, but this can be set to 
208
         * false if you need the default behavior (not recommended)
209
        * @property preventDefault
210
        * @type boolean
211
        * @static
212
         */
213
        preventDefault: true,
214

    
215
        /**
216
         * Flag to determine if we should stop the propagation of the events 
217
         * we generate. This is true by default but you may want to set it to
218
         * false if the html element contains other features that require the
219
         * mouse click.
220
        * @property stopPropagation
221
        * @type boolean
222
        * @static
223
         */
224
        stopPropagation: true,
225

    
226
        /**
227
         * Internal flag that is set to true when drag and drop has been
228
         * initialized
229
        * @property initialized
230
        * @private
231
        * @static
232
         */
233
        initialized: false,
234

    
235
        /**
236
         * All drag and drop can be disabled.
237
        * @property locked
238
        * @private
239
        * @static
240
         */
241
        locked: false,
242

    
243
        /**
244
         * Provides additional information about the the current set of
245
         * interactions.  Can be accessed from the event handlers. It
246
         * contains the following properties:
247
         *
248
         *       out:       onDragOut interactions
249
         *       enter:     onDragEnter interactions
250
         *       over:      onDragOver interactions
251
         *       drop:      onDragDrop interactions
252
         *       point:     The location of the cursor
253
         *       draggedRegion: The location of dragged element at the time
254
         *                      of the interaction
255
         *       sourceRegion: The location of the source elemtn at the time
256
         *                     of the interaction
257
         *       validDrop: boolean
258
        * @property interactionInfo
259
        * @type object
260
        * @static
261
         */
262
        interactionInfo: null,
263

    
264
        /**
265
         * Called the first time an element is registered.
266
        * @method init
267
        * @private
268
        * @static
269
         */
270
        init: function() {
271
            this.initialized = true;
272
        },
273

    
274
        /**
275
         * In point mode, drag and drop interaction is defined by the 
276
         * location of the cursor during the drag/drop
277
        * @property POINT
278
        * @type int
279
        * @static
280
        * @final
281
         */
282
        POINT: 0,
283

    
284
        /**
285
         * In intersect mode, drag and drop interaction is defined by the 
286
         * cursor position or the amount of overlap of two or more drag and 
287
         * drop objects.
288
        * @property INTERSECT
289
        * @type int
290
        * @static
291
        * @final
292
         */
293
        INTERSECT: 1,
294

    
295
        /**
296
         * In intersect mode, drag and drop interaction is defined only by the 
297
         * overlap of two or more drag and drop objects.
298
        * @property STRICT_INTERSECT
299
        * @type int
300
        * @static
301
        * @final
302
         */
303
        STRICT_INTERSECT: 2,
304

    
305
        /**
306
         * The current drag and drop mode.  Default: POINT
307
        * @property mode
308
        * @type int
309
        * @static
310
         */
311
        mode: 0,
312

    
313
        /**
314
         * Runs method on all drag and drop objects
315
        * @method _execOnAll
316
        * @private
317
        * @static
318
         */
319
        _execOnAll: function(sMethod, args) {
320
            for (var i in this.ids) {
321
                for (var j in this.ids[i]) {
322
                    var oDD = this.ids[i][j];
323
                    if (! this.isTypeOfDD(oDD)) {
324
                        continue;
325
                    }
326
                    oDD[sMethod].apply(oDD, args);
327
                }
328
            }
329
        },
330

    
331
        /**
332
         * Drag and drop initialization.  Sets up the global event handlers
333
        * @method _onLoad
334
        * @private
335
        * @static
336
         */
337
        _onLoad: function() {
338

    
339
            this.init();
340

    
341
            Event.on(document, "mouseup",   this.handleMouseUp, this, true);
342
            Event.on(document, "mousemove", this.handleMouseMove, this, true);
343
            Event.on(window,   "unload",    this._onUnload, this, true);
344
            Event.on(window,   "resize",    this._onResize, this, true);
345
            // Event.on(window,   "mouseout",    this._test);
346

    
347
        },
348

    
349
        /**
350
         * Reset constraints on all drag and drop objs
351
        * @method _onResize
352
        * @private
353
        * @static
354
         */
355
        _onResize: function(e) {
356
            this._execOnAll("resetConstraints", []);
357
        },
358

    
359
        /**
360
         * Lock all drag and drop functionality
361
        * @method lock
362
        * @static
363
         */
364
        lock: function() { this.locked = true; },
365

    
366
        /**
367
         * Unlock all drag and drop functionality
368
        * @method unlock
369
        * @static
370
         */
371
        unlock: function() { this.locked = false; },
372

    
373
        /**
374
         * Is drag and drop locked?
375
        * @method isLocked
376
        * @return {boolean} True if drag and drop is locked, false otherwise.
377
        * @static
378
         */
379
        isLocked: function() { return this.locked; },
380

    
381
        /**
382
         * Location cache that is set for all drag drop objects when a drag is
383
         * initiated, cleared when the drag is finished.
384
        * @property locationCache
385
        * @private
386
        * @static
387
         */
388
        locationCache: {},
389

    
390
        /**
391
         * Set useCache to false if you want to force object the lookup of each
392
         * drag and drop linked element constantly during a drag.
393
        * @property useCache
394
        * @type boolean
395
        * @static
396
         */
397
        useCache: true,
398

    
399
        /**
400
         * The number of pixels that the mouse needs to move after the 
401
         * mousedown before the drag is initiated.  Default=3;
402
        * @property clickPixelThresh
403
        * @type int
404
        * @static
405
         */
406
        clickPixelThresh: 3,
407

    
408
        /**
409
         * The number of milliseconds after the mousedown event to initiate the
410
         * drag if we don't get a mouseup event. Default=1000
411
        * @property clickTimeThresh
412
        * @type int
413
        * @static
414
         */
415
        clickTimeThresh: 1000,
416

    
417
        /**
418
         * Flag that indicates that either the drag pixel threshold or the 
419
         * mousdown time threshold has been met
420
        * @property dragThreshMet
421
        * @type boolean
422
        * @private
423
        * @static
424
         */
425
        dragThreshMet: false,
426

    
427
        /**
428
         * Timeout used for the click time threshold
429
        * @property clickTimeout
430
        * @type Object
431
        * @private
432
        * @static
433
         */
434
        clickTimeout: null,
435

    
436
        /**
437
         * The X position of the mousedown event stored for later use when a 
438
         * drag threshold is met.
439
        * @property startX
440
        * @type int
441
        * @private
442
        * @static
443
         */
444
        startX: 0,
445

    
446
        /**
447
         * The Y position of the mousedown event stored for later use when a 
448
         * drag threshold is met.
449
        * @property startY
450
        * @type int
451
        * @private
452
        * @static
453
         */
454
        startY: 0,
455

    
456
        /**
457
         * Flag to determine if the drag event was fired from the click timeout and
458
         * not the mouse move threshold.
459
        * @property fromTimeout
460
        * @type boolean
461
        * @private
462
        * @static
463
         */
464
        fromTimeout: false,
465

    
466
        /**
467
         * Each DragDrop instance must be registered with the DragDropMgr.  
468
         * This is executed in DragDrop.init()
469
        * @method regDragDrop
470
        * @param {DragDrop} oDD the DragDrop object to register
471
        * @param {String} sGroup the name of the group this element belongs to
472
        * @static
473
         */
474
        regDragDrop: function(oDD, sGroup) {
475
            if (!this.initialized) { this.init(); }
476
            
477
            if (!this.ids[sGroup]) {
478
                this.ids[sGroup] = {};
479
            }
480
            this.ids[sGroup][oDD.id] = oDD;
481
        },
482

    
483
        /**
484
         * Removes the supplied dd instance from the supplied group. Executed
485
         * by DragDrop.removeFromGroup, so don't call this function directly.
486
        * @method removeDDFromGroup
487
        * @private
488
        * @static
489
         */
490
        removeDDFromGroup: function(oDD, sGroup) {
491
            if (!this.ids[sGroup]) {
492
                this.ids[sGroup] = {};
493
            }
494

    
495
            var obj = this.ids[sGroup];
496
            if (obj && obj[oDD.id]) {
497
                delete obj[oDD.id];
498
            }
499
        },
500

    
501
        /**
502
         * Unregisters a drag and drop item.  This is executed in 
503
         * DragDrop.unreg, use that method instead of calling this directly.
504
        * @method _remove
505
        * @private
506
        * @static
507
         */
508
        _remove: function(oDD) {
509
            for (var g in oDD.groups) {
510
                if (g) {
511
                    var item = this.ids[g];
512
                    if (item && item[oDD.id]) {
513
                        delete item[oDD.id];
514
                    }
515
                }
516
                
517
            }
518
            delete this.handleIds[oDD.id];
519
        },
520

    
521
        /**
522
         * Each DragDrop handle element must be registered.  This is done
523
         * automatically when executing DragDrop.setHandleElId()
524
        * @method regHandle
525
        * @param {String} sDDId the DragDrop id this element is a handle for
526
        * @param {String} sHandleId the id of the element that is the drag 
527
         * handle
528
        * @static
529
         */
530
        regHandle: function(sDDId, sHandleId) {
531
            if (!this.handleIds[sDDId]) {
532
                this.handleIds[sDDId] = {};
533
            }
534
            this.handleIds[sDDId][sHandleId] = sHandleId;
535
        },
536

    
537
        /**
538
         * Utility function to determine if a given element has been 
539
         * registered as a drag drop item.
540
        * @method isDragDrop
541
        * @param {String} id the element id to check
542
        * @return {boolean} true if this element is a DragDrop item, 
543
         * false otherwise
544
        * @static
545
         */
546
        isDragDrop: function(id) {
547
            return ( this.getDDById(id) ) ? true : false;
548
        },
549

    
550
        /**
551
         * Returns the drag and drop instances that are in all groups the
552
         * passed in instance belongs to.
553
        * @method getRelated
554
        * @param {DragDrop} p_oDD the obj to get related data for
555
        * @param {boolean} bTargetsOnly if true, only return targetable objs
556
        * @return {DragDrop[]} the related instances
557
        * @static
558
         */
559
        getRelated: function(p_oDD, bTargetsOnly) {
560
            var oDDs = [];
561
            for (var i in p_oDD.groups) {
562
                for (var j in this.ids[i]) {
563
                    var dd = this.ids[i][j];
564
                    if (! this.isTypeOfDD(dd)) {
565
                        continue;
566
                    }
567
                    if (!bTargetsOnly || dd.isTarget) {
568
                        oDDs[oDDs.length] = dd;
569
                    }
570
                }
571
            }
572

    
573
            return oDDs;
574
        },
575

    
576
        /**
577
         * Returns true if the specified dd target is a legal target for 
578
         * the specifice drag obj
579
        * @method isLegalTarget
580
        * @param {DragDrop} the drag obj
581
        * @param {DragDrop} the target
582
        * @return {boolean} true if the target is a legal target for the 
583
         * dd obj
584
        * @static
585
         */
586
        isLegalTarget: function (oDD, oTargetDD) {
587
            var targets = this.getRelated(oDD, true);
588
            for (var i=0, len=targets.length;i<len;++i) {
589
                if (targets[i].id == oTargetDD.id) {
590
                    return true;
591
                }
592
            }
593

    
594
            return false;
595
        },
596

    
597
        /**
598
         * My goal is to be able to transparently determine if an object is
599
         * typeof DragDrop, and the exact subclass of DragDrop.  typeof 
600
         * returns "object", oDD.constructor.toString() always returns
601
         * "DragDrop" and not the name of the subclass.  So for now it just
602
         * evaluates a well-known variable in DragDrop.
603
        * @method isTypeOfDD
604
        * @param {Object} the object to evaluate
605
        * @return {boolean} true if typeof oDD = DragDrop
606
        * @static
607
         */
608
        isTypeOfDD: function (oDD) {
609
            return (oDD && oDD.__ygDragDrop);
610
        },
611

    
612
        /**
613
         * Utility function to determine if a given element has been 
614
         * registered as a drag drop handle for the given Drag Drop object.
615
        * @method isHandle
616
        * @param {String} id the element id to check
617
        * @return {boolean} true if this element is a DragDrop handle, false 
618
         * otherwise
619
        * @static
620
         */
621
        isHandle: function(sDDId, sHandleId) {
622
            return ( this.handleIds[sDDId] && 
623
                            this.handleIds[sDDId][sHandleId] );
624
        },
625

    
626
        /**
627
         * Returns the DragDrop instance for a given id
628
        * @method getDDById
629
        * @param {String} id the id of the DragDrop object
630
        * @return {DragDrop} the drag drop object, null if it is not found
631
        * @static
632
         */
633
        getDDById: function(id) {
634
            for (var i in this.ids) {
635
                if (this.ids[i][id]) {
636
                    return this.ids[i][id];
637
                }
638
            }
639
            return null;
640
        },
641

    
642
        /**
643
         * Fired after a registered DragDrop object gets the mousedown event.
644
         * Sets up the events required to track the object being dragged
645
        * @method handleMouseDown
646
        * @param {Event} e the event
647
        * @param oDD the DragDrop object being dragged
648
        * @private
649
        * @static
650
         */
651
        handleMouseDown: function(e, oDD) {
652
            //this._activateShim();
653

    
654
            this.currentTarget = YAHOO.util.Event.getTarget(e);
655

    
656
            this.dragCurrent = oDD;
657

    
658
            var el = oDD.getEl();
659

    
660
            // track start position
661
            this.startX = YAHOO.util.Event.getPageX(e);
662
            this.startY = YAHOO.util.Event.getPageY(e);
663

    
664
            this.deltaX = this.startX - el.offsetLeft;
665
            this.deltaY = this.startY - el.offsetTop;
666

    
667
            this.dragThreshMet = false;
668

    
669
            this.clickTimeout = setTimeout( 
670
                    function() { 
671
                        var DDM = YAHOO.util.DDM;
672
                        DDM.startDrag(DDM.startX, DDM.startY);
673
                        DDM.fromTimeout = true;
674
                    }, 
675
                    this.clickTimeThresh );
676
        },
677

    
678
        /**
679
         * Fired when either the drag pixel threshold or the mousedown hold 
680
         * time threshold has been met.
681
        * @method startDrag
682
        * @param x {int} the X position of the original mousedown
683
        * @param y {int} the Y position of the original mousedown
684
        * @static
685
         */
686
        startDrag: function(x, y) {
687
            if (this.dragCurrent && this.dragCurrent.useShim) {
688
                this._shimState = this.useShim;
689
                this.useShim = true;
690
            }
691
            this._activateShim();
692
            clearTimeout(this.clickTimeout);
693
            var dc = this.dragCurrent;
694
            if (dc && dc.events.b4StartDrag) {
695
                dc.b4StartDrag(x, y);
696
                dc.fireEvent('b4StartDragEvent', { x: x, y: y });
697
            }
698
            if (dc && dc.events.startDrag) {
699
                dc.startDrag(x, y);
700
                dc.fireEvent('startDragEvent', { x: x, y: y });
701
            }
702
            this.dragThreshMet = true;
703
        },
704

    
705
        /**
706
         * Internal function to handle the mouseup event.  Will be invoked 
707
         * from the context of the document.
708
        * @method handleMouseUp
709
        * @param {Event} e the event
710
        * @private
711
        * @static
712
         */
713
        handleMouseUp: function(e) {
714
            if (this.dragCurrent) {
715
                clearTimeout(this.clickTimeout);
716

    
717
                if (this.dragThreshMet) {
718
                    if (this.fromTimeout) {
719
                        this.fromTimeout = false;
720
                        this.handleMouseMove(e);
721
                    }
722
                    this.fromTimeout = false;
723
                    this.fireEvents(e, true);
724
                } else {
725
                }
726

    
727
                this.stopDrag(e);
728

    
729
                this.stopEvent(e);
730
            }
731
        },
732

    
733
        /**
734
         * Utility to stop event propagation and event default, if these 
735
         * features are turned on.
736
        * @method stopEvent
737
        * @param {Event} e the event as returned by this.getEvent()
738
        * @static
739
         */
740
        stopEvent: function(e) {
741
            if (this.stopPropagation) {
742
                YAHOO.util.Event.stopPropagation(e);
743
            }
744

    
745
            if (this.preventDefault) {
746
                YAHOO.util.Event.preventDefault(e);
747
            }
748
        },
749

    
750
        /** 
751
         * Ends the current drag, cleans up the state, and fires the endDrag
752
         * and mouseUp events.  Called internally when a mouseup is detected
753
         * during the drag.  Can be fired manually during the drag by passing
754
         * either another event (such as the mousemove event received in onDrag)
755
         * or a fake event with pageX and pageY defined (so that endDrag and
756
         * onMouseUp have usable position data.).  Alternatively, pass true
757
         * for the silent parameter so that the endDrag and onMouseUp events
758
         * are skipped (so no event data is needed.)
759
         *
760
        * @method stopDrag
761
        * @param {Event} e the mouseup event, another event (or a fake event) 
762
         *                  with pageX and pageY defined, or nothing if the 
763
         *                  silent parameter is true
764
        * @param {boolean} silent skips the enddrag and mouseup events if true
765
        * @static
766
         */
767
        stopDrag: function(e, silent) {
768
            var dc = this.dragCurrent;
769
            // Fire the drag end event for the item that was dragged
770
            if (dc && !silent) {
771
                if (this.dragThreshMet) {
772
                    if (dc.events.b4EndDrag) {
773
                        dc.b4EndDrag(e);
774
                        dc.fireEvent('b4EndDragEvent', { e: e });
775
                    }
776
                    if (dc.events.endDrag) {
777
                        dc.endDrag(e);
778
                        dc.fireEvent('endDragEvent', { e: e });
779
                    }
780
                }
781
                if (dc.events.mouseUp) {
782
                    dc.onMouseUp(e);
783
                    dc.fireEvent('mouseUpEvent', { e: e });
784
                }
785
            }
786

    
787
            if (this._shimActive) {
788
                this._deactivateShim();
789
                if (this.dragCurrent && this.dragCurrent.useShim) {
790
                    this.useShim = this._shimState;
791
                    this._shimState = false;
792
                }
793
            }
794

    
795
            this.dragCurrent = null;
796
            this.dragOvers = {};
797
        },
798

    
799
        /** 
800
         * Internal function to handle the mousemove event.  Will be invoked 
801
         * from the context of the html element.
802
         *
803
        * @TODO figure out what we can do about mouse events lost when the 
804
         * user drags objects beyond the window boundary.  Currently we can 
805
         * detect this in internet explorer by verifying that the mouse is 
806
         * down during the mousemove event.  Firefox doesn't give us the 
807
         * button state on the mousemove event.
808
        * @method handleMouseMove
809
        * @param {Event} e the event
810
        * @private
811
        * @static
812
         */
813
        handleMouseMove: function(e) {
814

    
815
            var dc = this.dragCurrent;
816
            if (dc) {
817

    
818
                // var button = e.which || e.button;
819

    
820
                // check for IE mouseup outside of page boundary
821
                if (YAHOO.util.Event.isIE && !e.button) {
822
                    this.stopEvent(e);
823
                    return this.handleMouseUp(e);
824
                } else {
825
                    if (e.clientX < 0 || e.clientY < 0) {
826
                        //This will stop the element from leaving the viewport in FF, Opera & Safari
827
                        //Not turned on yet
828
                        //this.stopEvent(e);
829
                        //return false;
830
                    }
831
                }
832

    
833
                if (!this.dragThreshMet) {
834
                    var diffX = Math.abs(this.startX - YAHOO.util.Event.getPageX(e));
835
                    var diffY = Math.abs(this.startY - YAHOO.util.Event.getPageY(e));
836
                    if (diffX > this.clickPixelThresh || 
837
                                diffY > this.clickPixelThresh) {
838
                        this.startDrag(this.startX, this.startY);
839
                    }
840
                }
841

    
842
                if (this.dragThreshMet) {
843
                    if (dc && dc.events.b4Drag) {
844
                        dc.b4Drag(e);
845
                        dc.fireEvent('b4DragEvent', { e: e});
846
                    }
847
                    if (dc && dc.events.drag) {
848
                        dc.onDrag(e);
849
                        dc.fireEvent('dragEvent', { e: e});
850
                    }
851
                    if (dc) {
852
                        this.fireEvents(e, false);
853
                    }
854
                }
855

    
856
                this.stopEvent(e);
857
            }
858
        },
859
        
860
        /**
861
         * Iterates over all of the DragDrop elements to find ones we are 
862
         * hovering over or dropping on
863
        * @method fireEvents
864
        * @param {Event} e the event
865
        * @param {boolean} isDrop is this a drop op or a mouseover op?
866
        * @private
867
        * @static
868
         */
869
        fireEvents: function(e, isDrop) {
870
            var dc = this.dragCurrent;
871

    
872
            // If the user did the mouse up outside of the window, we could 
873
            // get here even though we have ended the drag.
874
            // If the config option dragOnly is true, bail out and don't fire the events
875
            if (!dc || dc.isLocked() || dc.dragOnly) {
876
                return;
877
            }
878

    
879
            var x = YAHOO.util.Event.getPageX(e),
880
                y = YAHOO.util.Event.getPageY(e),
881
                pt = new YAHOO.util.Point(x,y),
882
                pos = dc.getTargetCoord(pt.x, pt.y),
883
                el = dc.getDragEl(),
884
                events = ['out', 'over', 'drop', 'enter'],
885
                curRegion = new YAHOO.util.Region( pos.y, 
886
                                               pos.x + el.offsetWidth,
887
                                               pos.y + el.offsetHeight, 
888
                                               pos.x ),
889
            
890
                oldOvers = [], // cache the previous dragOver array
891
                inGroupsObj  = {},
892
                inGroups  = [],
893
                data = {
894
                    outEvts: [],
895
                    overEvts: [],
896
                    dropEvts: [],
897
                    enterEvts: []
898
                };
899

    
900

    
901
            // Check to see if the object(s) we were hovering over is no longer 
902
            // being hovered over so we can fire the onDragOut event
903
            for (var i in this.dragOvers) {
904

    
905
                var ddo = this.dragOvers[i];
906

    
907
                if (! this.isTypeOfDD(ddo)) {
908
                    continue;
909
                }
910
                if (! this.isOverTarget(pt, ddo, this.mode, curRegion)) {
911
                    data.outEvts.push( ddo );
912
                }
913

    
914
                oldOvers[i] = true;
915
                delete this.dragOvers[i];
916
            }
917

    
918
            for (var sGroup in dc.groups) {
919
                
920
                if ("string" != typeof sGroup) {
921
                    continue;
922
                }
923

    
924
                for (i in this.ids[sGroup]) {
925
                    var oDD = this.ids[sGroup][i];
926
                    if (! this.isTypeOfDD(oDD)) {
927
                        continue;
928
                    }
929

    
930
                    if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
931
                        if (this.isOverTarget(pt, oDD, this.mode, curRegion)) {
932
                            inGroupsObj[sGroup] = true;
933
                            // look for drop interactions
934
                            if (isDrop) {
935
                                data.dropEvts.push( oDD );
936
                            // look for drag enter and drag over interactions
937
                            } else {
938

    
939
                                // initial drag over: dragEnter fires
940
                                if (!oldOvers[oDD.id]) {
941
                                    data.enterEvts.push( oDD );
942
                                // subsequent drag overs: dragOver fires
943
                                } else {
944
                                    data.overEvts.push( oDD );
945
                                }
946

    
947
                                this.dragOvers[oDD.id] = oDD;
948
                            }
949
                        }
950
                    }
951
                }
952
            }
953

    
954
            this.interactionInfo = {
955
                out:       data.outEvts,
956
                enter:     data.enterEvts,
957
                over:      data.overEvts,
958
                drop:      data.dropEvts,
959
                point:     pt,
960
                draggedRegion:    curRegion,
961
                sourceRegion: this.locationCache[dc.id],
962
                validDrop: isDrop
963
            };
964

    
965
            
966
            for (var inG in inGroupsObj) {
967
                inGroups.push(inG);
968
            }
969

    
970
            // notify about a drop that did not find a target
971
            if (isDrop && !data.dropEvts.length) {
972
                this.interactionInfo.validDrop = false;
973
                if (dc.events.invalidDrop) {
974
                    dc.onInvalidDrop(e);
975
                    dc.fireEvent('invalidDropEvent', { e: e });
976
                }
977
            }
978
            for (i = 0; i < events.length; i++) {
979
                var tmp = null;
980
                if (data[events[i] + 'Evts']) {
981
                    tmp = data[events[i] + 'Evts'];
982
                }
983
                if (tmp && tmp.length) {
984
                    var type = events[i].charAt(0).toUpperCase() + events[i].substr(1),
985
                        ev = 'onDrag' + type,
986
                        b4 = 'b4Drag' + type,
987
                        cev = 'drag' + type + 'Event',
988
                        check = 'drag' + type;
989
                    if (this.mode) {
990
                        if (dc.events[b4]) {
991
                            dc[b4](e, tmp, inGroups);
992
                            dc.fireEvent(b4 + 'Event', { event: e, info: tmp, group: inGroups });
993
                            
994
                        }
995
                        if (dc.events[check]) {
996
                            dc[ev](e, tmp, inGroups);
997
                            dc.fireEvent(cev, { event: e, info: tmp, group: inGroups });
998
                        }
999
                    } else {
1000
                        for (var b = 0, len = tmp.length; b < len; ++b) {
1001
                            if (dc.events[b4]) {
1002
                                dc[b4](e, tmp[b].id, inGroups[0]);
1003
                                dc.fireEvent(b4 + 'Event', { event: e, info: tmp[b].id, group: inGroups[0] });
1004
                            }
1005
                            if (dc.events[check]) {
1006
                                dc[ev](e, tmp[b].id, inGroups[0]);
1007
                                dc.fireEvent(cev, { event: e, info: tmp[b].id, group: inGroups[0] });
1008
                            }
1009
                        }
1010
                    }
1011
                }
1012
            }
1013
        },
1014

    
1015
        /**
1016
         * Helper function for getting the best match from the list of drag 
1017
         * and drop objects returned by the drag and drop events when we are 
1018
         * in INTERSECT mode.  It returns either the first object that the 
1019
         * cursor is over, or the object that has the greatest overlap with 
1020
         * the dragged element.
1021
        * @method getBestMatch
1022
        * @param  {DragDrop[]} dds The array of drag and drop objects 
1023
         * targeted
1024
        * @return {DragDrop}       The best single match
1025
        * @static
1026
         */
1027
        getBestMatch: function(dds) {
1028
            var winner = null;
1029

    
1030
            var len = dds.length;
1031

    
1032
            if (len == 1) {
1033
                winner = dds[0];
1034
            } else {
1035
                // Loop through the targeted items
1036
                for (var i=0; i<len; ++i) {
1037
                    var dd = dds[i];
1038
                    // If the cursor is over the object, it wins.  If the 
1039
                    // cursor is over multiple matches, the first one we come
1040
                    // to wins.
1041
                    if (this.mode == this.INTERSECT && dd.cursorIsOver) {
1042
                        winner = dd;
1043
                        break;
1044
                    // Otherwise the object with the most overlap wins
1045
                    } else {
1046
                        if (!winner || !winner.overlap || (dd.overlap &&
1047
                            winner.overlap.getArea() < dd.overlap.getArea())) {
1048
                            winner = dd;
1049
                        }
1050
                    }
1051
                }
1052
            }
1053

    
1054
            return winner;
1055
        },
1056

    
1057
        /**
1058
         * Refreshes the cache of the top-left and bottom-right points of the 
1059
         * drag and drop objects in the specified group(s).  This is in the
1060
         * format that is stored in the drag and drop instance, so typical 
1061
         * usage is:
1062
         * <code>
1063
         * YAHOO.util.DragDropMgr.refreshCache(ddinstance.groups);
1064
         * </code>
1065
         * Alternatively:
1066
         * <code>
1067
         * YAHOO.util.DragDropMgr.refreshCache({group1:true, group2:true});
1068
         * </code>
1069
        * @TODO this really should be an indexed array.  Alternatively this
1070
         * method could accept both.
1071
        * @method refreshCache
1072
        * @param {Object} groups an associative array of groups to refresh
1073
        * @static
1074
         */
1075
        refreshCache: function(groups) {
1076

    
1077
            // refresh everything if group array is not provided
1078
            var g = groups || this.ids;
1079

    
1080
            for (var sGroup in g) {
1081
                if ("string" != typeof sGroup) {
1082
                    continue;
1083
                }
1084
                for (var i in this.ids[sGroup]) {
1085
                    var oDD = this.ids[sGroup][i];
1086

    
1087
                    if (this.isTypeOfDD(oDD)) {
1088
                        var loc = this.getLocation(oDD);
1089
                        if (loc) {
1090
                            this.locationCache[oDD.id] = loc;
1091
                        } else {
1092
                            delete this.locationCache[oDD.id];
1093
                        }
1094
                    }
1095
                }
1096
            }
1097
        },
1098

    
1099
        /**
1100
         * This checks to make sure an element exists and is in the DOM.  The
1101
         * main purpose is to handle cases where innerHTML is used to remove
1102
         * drag and drop objects from the DOM.  IE provides an 'unspecified
1103
         * error' when trying to access the offsetParent of such an element
1104
        * @method verifyEl
1105
        * @param {HTMLElement} el the element to check
1106
        * @return {boolean} true if the element looks usable
1107
        * @static
1108
         */
1109
        verifyEl: function(el) {
1110
            try {
1111
                if (el) {
1112
                    var parent = el.offsetParent;
1113
                    if (parent) {
1114
                        return true;
1115
                    }
1116
                }
1117
            } catch(e) {
1118
            }
1119

    
1120
            return false;
1121
        },
1122
        
1123
        /**
1124
         * Returns a Region object containing the drag and drop element's position
1125
         * and size, including the padding configured for it
1126
        * @method getLocation
1127
        * @param {DragDrop} oDD the drag and drop object to get the 
1128
         *                       location for
1129
        * @return {YAHOO.util.Region} a Region object representing the total area
1130
         *                             the element occupies, including any padding
1131
         *                             the instance is configured for.
1132
        * @static
1133
         */
1134
        getLocation: function(oDD) {
1135
            if (! this.isTypeOfDD(oDD)) {
1136
                return null;
1137
            }
1138

    
1139
            var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
1140

    
1141
            try {
1142
                pos= YAHOO.util.Dom.getXY(el);
1143
            } catch (e) { }
1144

    
1145
            if (!pos) {
1146
                return null;
1147
            }
1148

    
1149
            x1 = pos[0];
1150
            x2 = x1 + el.offsetWidth;
1151
            y1 = pos[1];
1152
            y2 = y1 + el.offsetHeight;
1153

    
1154
            t = y1 - oDD.padding[0];
1155
            r = x2 + oDD.padding[1];
1156
            b = y2 + oDD.padding[2];
1157
            l = x1 - oDD.padding[3];
1158

    
1159
            return new YAHOO.util.Region( t, r, b, l );
1160
        },
1161

    
1162
        /**
1163
         * Checks the cursor location to see if it over the target
1164
        * @method isOverTarget
1165
        * @param {YAHOO.util.Point} pt The point to evaluate
1166
        * @param {DragDrop} oTarget the DragDrop object we are inspecting
1167
        * @param {boolean} intersect true if we are in intersect mode
1168
        * @param {YAHOO.util.Region} pre-cached location of the dragged element
1169
        * @return {boolean} true if the mouse is over the target
1170
        * @private
1171
        * @static
1172
         */
1173
        isOverTarget: function(pt, oTarget, intersect, curRegion) {
1174
            // use cache if available
1175
            var loc = this.locationCache[oTarget.id];
1176
            if (!loc || !this.useCache) {
1177
                loc = this.getLocation(oTarget);
1178
                this.locationCache[oTarget.id] = loc;
1179

    
1180
            }
1181

    
1182
            if (!loc) {
1183
                return false;
1184
            }
1185

    
1186
            oTarget.cursorIsOver = loc.contains( pt );
1187

    
1188
            // DragDrop is using this as a sanity check for the initial mousedown
1189
            // in this case we are done.  In POINT mode, if the drag obj has no
1190
            // contraints, we are done. Otherwise we need to evaluate the 
1191
            // region the target as occupies to determine if the dragged element
1192
            // overlaps with it.
1193
            
1194
            var dc = this.dragCurrent;
1195
            if (!dc || (!intersect && !dc.constrainX && !dc.constrainY)) {
1196

    
1197
                //if (oTarget.cursorIsOver) {
1198
                //}
1199
                return oTarget.cursorIsOver;
1200
            }
1201

    
1202
            oTarget.overlap = null;
1203

    
1204

    
1205
            // Get the current location of the drag element, this is the
1206
            // location of the mouse event less the delta that represents
1207
            // where the original mousedown happened on the element.  We
1208
            // need to consider constraints and ticks as well.
1209

    
1210
            if (!curRegion) {
1211
                var pos = dc.getTargetCoord(pt.x, pt.y);
1212
                var el = dc.getDragEl();
1213
                curRegion = new YAHOO.util.Region( pos.y, 
1214
                                                   pos.x + el.offsetWidth,
1215
                                                   pos.y + el.offsetHeight, 
1216
                                                   pos.x );
1217
            }
1218

    
1219
            var overlap = curRegion.intersect(loc);
1220

    
1221
            if (overlap) {
1222
                oTarget.overlap = overlap;
1223
                return (intersect) ? true : oTarget.cursorIsOver;
1224
            } else {
1225
                return false;
1226
            }
1227
        },
1228

    
1229
        /**
1230
         * unload event handler
1231
        * @method _onUnload
1232
        * @private
1233
        * @static
1234
         */
1235
        _onUnload: function(e, me) {
1236
            this.unregAll();
1237
        },
1238

    
1239
        /**
1240
         * Cleans up the drag and drop events and objects.
1241
        * @method unregAll
1242
        * @private
1243
        * @static
1244
         */
1245
        unregAll: function() {
1246

    
1247
            if (this.dragCurrent) {
1248
                this.stopDrag();
1249
                this.dragCurrent = null;
1250
            }
1251

    
1252
            this._execOnAll("unreg", []);
1253

    
1254
            //for (var i in this.elementCache) {
1255
                //delete this.elementCache[i];
1256
            //}
1257
            //this.elementCache = {};
1258

    
1259
            this.ids = {};
1260
        },
1261

    
1262
        /**
1263
         * A cache of DOM elements
1264
        * @property elementCache
1265
        * @private
1266
        * @static
1267
        * @deprecated elements are not cached now
1268
         */
1269
        elementCache: {},
1270
        
1271
        /**
1272
         * Get the wrapper for the DOM element specified
1273
        * @method getElWrapper
1274
        * @param {String} id the id of the element to get
1275
        * @return {YAHOO.util.DDM.ElementWrapper} the wrapped element
1276
        * @private
1277
        * @deprecated This wrapper isn't that useful
1278
        * @static
1279
         */
1280
        getElWrapper: function(id) {
1281
            var oWrapper = this.elementCache[id];
1282
            if (!oWrapper || !oWrapper.el) {
1283
                oWrapper = this.elementCache[id] = 
1284
                    new this.ElementWrapper(YAHOO.util.Dom.get(id));
1285
            }
1286
            return oWrapper;
1287
        },
1288

    
1289
        /**
1290
         * Returns the actual DOM element
1291
        * @method getElement
1292
        * @param {String} id the id of the elment to get
1293
        * @return {Object} The element
1294
        * @deprecated use YAHOO.util.Dom.get instead
1295
        * @static
1296
         */
1297
        getElement: function(id) {
1298
            return YAHOO.util.Dom.get(id);
1299
        },
1300
        
1301
        /**
1302
         * Returns the style property for the DOM element (i.e., 
1303
         * document.getElById(id).style)
1304
        * @method getCss
1305
        * @param {String} id the id of the elment to get
1306
        * @return {Object} The style property of the element
1307
        * @deprecated use YAHOO.util.Dom instead
1308
        * @static
1309
         */
1310
        getCss: function(id) {
1311
            var el = YAHOO.util.Dom.get(id);
1312
            return (el) ? el.style : null;
1313
        },
1314

    
1315
        /**
1316
         * Inner class for cached elements
1317
        * @class DragDropMgr.ElementWrapper
1318
        * @for DragDropMgr
1319
        * @private
1320
        * @deprecated
1321
         */
1322
        ElementWrapper: function(el) {
1323
                /**
1324
                 * The element
1325
                * @property el
1326
                 */
1327
                this.el = el || null;
1328
                /**
1329
                 * The element id
1330
                * @property id
1331
                 */
1332
                this.id = this.el && el.id;
1333
                /**
1334
                 * A reference to the style property
1335
                * @property css
1336
                 */
1337
                this.css = this.el && el.style;
1338
            },
1339

    
1340
        /**
1341
         * Returns the X position of an html element
1342
        * @method getPosX
1343
        * @param el the element for which to get the position
1344
        * @return {int} the X coordinate
1345
        * @for DragDropMgr
1346
        * @deprecated use YAHOO.util.Dom.getX instead
1347
        * @static
1348
         */
1349
        getPosX: function(el) {
1350
            return YAHOO.util.Dom.getX(el);
1351
        },
1352

    
1353
        /**
1354
         * Returns the Y position of an html element
1355
        * @method getPosY
1356
        * @param el the element for which to get the position
1357
        * @return {int} the Y coordinate
1358
        * @deprecated use YAHOO.util.Dom.getY instead
1359
        * @static
1360
         */
1361
        getPosY: function(el) {
1362
            return YAHOO.util.Dom.getY(el); 
1363
        },
1364

    
1365
        /**
1366
         * Swap two nodes.  In IE, we use the native method, for others we 
1367
         * emulate the IE behavior
1368
        * @method swapNode
1369
        * @param n1 the first node to swap
1370
        * @param n2 the other node to swap
1371
        * @static
1372
         */
1373
        swapNode: function(n1, n2) {
1374
            if (n1.swapNode) {
1375
                n1.swapNode(n2);
1376
            } else {
1377
                var p = n2.parentNode;
1378
                var s = n2.nextSibling;
1379

    
1380
                if (s == n1) {
1381
                    p.insertBefore(n1, n2);
1382
                } else if (n2 == n1.nextSibling) {
1383
                    p.insertBefore(n2, n1);
1384
                } else {
1385
                    n1.parentNode.replaceChild(n2, n1);
1386
                    p.insertBefore(n1, s);
1387
                }
1388
            }
1389
        },
1390

    
1391
        /**
1392
         * Returns the current scroll position
1393
        * @method getScroll
1394
        * @private
1395
        * @static
1396
         */
1397
        getScroll: function () {
1398
            var t, l, dde=document.documentElement, db=document.body;
1399
            if (dde && (dde.scrollTop || dde.scrollLeft)) {
1400
                t = dde.scrollTop;
1401
                l = dde.scrollLeft;
1402
            } else if (db) {
1403
                t = db.scrollTop;
1404
                l = db.scrollLeft;
1405
            } else {
1406
            }
1407
            return { top: t, left: l };
1408
        },
1409

    
1410
        /**
1411
         * Returns the specified element style property
1412
        * @method getStyle
1413
        * @param {HTMLElement} el          the element
1414
        * @param {string}      styleProp   the style property
1415
        * @return {string} The value of the style property
1416
        * @deprecated use YAHOO.util.Dom.getStyle
1417
        * @static
1418
         */
1419
        getStyle: function(el, styleProp) {
1420
            return YAHOO.util.Dom.getStyle(el, styleProp);
1421
        },
1422

    
1423
        /**
1424
         * Gets the scrollTop
1425
        * @method getScrollTop
1426
        * @return {int} the document's scrollTop
1427
        * @static
1428
         */
1429
        getScrollTop: function () { return this.getScroll().top; },
1430

    
1431
        /**
1432
         * Gets the scrollLeft
1433
        * @method getScrollLeft
1434
        * @return {int} the document's scrollTop
1435
        * @static
1436
         */
1437
        getScrollLeft: function () { return this.getScroll().left; },
1438

    
1439
        /**
1440
         * Sets the x/y position of an element to the location of the
1441
         * target element.
1442
        * @method moveToEl
1443
        * @param {HTMLElement} moveEl      The element to move
1444
        * @param {HTMLElement} targetEl    The position reference element
1445
        * @static
1446
         */
1447
        moveToEl: function (moveEl, targetEl) {
1448
            var aCoord = YAHOO.util.Dom.getXY(targetEl);
1449
            YAHOO.util.Dom.setXY(moveEl, aCoord);
1450
        },
1451

    
1452
        /**
1453
         * Gets the client height
1454
        * @method getClientHeight
1455
        * @return {int} client height in px
1456
        * @deprecated use YAHOO.util.Dom.getViewportHeight instead
1457
        * @static
1458
         */
1459
        getClientHeight: function() {
1460
            return YAHOO.util.Dom.getViewportHeight();
1461
        },
1462

    
1463
        /**
1464
         * Gets the client width
1465
        * @method getClientWidth
1466
        * @return {int} client width in px
1467
        * @deprecated use YAHOO.util.Dom.getViewportWidth instead
1468
        * @static
1469
         */
1470
        getClientWidth: function() {
1471
            return YAHOO.util.Dom.getViewportWidth();
1472
        },
1473

    
1474
        /**
1475
         * Numeric array sort function
1476
        * @method numericSort
1477
        * @static
1478
         */
1479
        numericSort: function(a, b) { return (a - b); },
1480

    
1481
        /**
1482
         * Internal counter
1483
        * @property _timeoutCount
1484
        * @private
1485
        * @static
1486
         */
1487
        _timeoutCount: 0,
1488

    
1489
        /**
1490
         * Trying to make the load order less important.  Without this we get
1491
         * an error if this file is loaded before the Event Utility.
1492
        * @method _addListeners
1493
        * @private
1494
        * @static
1495
         */
1496
        _addListeners: function() {
1497
            var DDM = YAHOO.util.DDM;
1498
            if ( YAHOO.util.Event && document ) {
1499
                DDM._onLoad();
1500
            } else {
1501
                if (DDM._timeoutCount > 2000) {
1502
                } else {
1503
                    setTimeout(DDM._addListeners, 10);
1504
                    if (document && document.body) {
1505
                        DDM._timeoutCount += 1;
1506
                    }
1507
                }
1508
            }
1509
        },
1510

    
1511
        /**
1512
         * Recursively searches the immediate parent and all child nodes for 
1513
         * the handle element in order to determine wheter or not it was 
1514
         * clicked.
1515
        * @method handleWasClicked
1516
        * @param node the html element to inspect
1517
        * @static
1518
         */
1519
        handleWasClicked: function(node, id) {
1520
            if (this.isHandle(id, node.id)) {
1521
                return true;
1522
            } else {
1523
                // check to see if this is a text node child of the one we want
1524
                var p = node.parentNode;
1525

    
1526
                while (p) {
1527
                    if (this.isHandle(id, p.id)) {
1528
                        return true;
1529
                    } else {
1530
                        p = p.parentNode;
1531
                    }
1532
                }
1533
            }
1534

    
1535
            return false;
1536
        }
1537

    
1538
    };
1539

    
1540
}();
1541

    
1542
// shorter alias, save a few bytes
1543
YAHOO.util.DDM = YAHOO.util.DragDropMgr;
1544
YAHOO.util.DDM._addListeners();
1545

    
1546
}
1547

    
1548
(function() {
1549

    
1550
var Event=YAHOO.util.Event; 
1551
var Dom=YAHOO.util.Dom;
1552

    
1553
/**
1554
 * Defines the interface and base operation of items that that can be 
1555
 * dragged or can be drop targets.  It was designed to be extended, overriding
1556
 * the event handlers for startDrag, onDrag, onDragOver, onDragOut.
1557
 * Up to three html elements can be associated with a DragDrop instance:
1558
 * <ul>
1559
 * <li>linked element: the element that is passed into the constructor.
1560
 * This is the element which defines the boundaries for interaction with 
1561
 * other DragDrop objects.</li>
1562
 * <li>handle element(s): The drag operation only occurs if the element that 
1563
 * was clicked matches a handle element.  By default this is the linked 
1564
 * element, but there are times that you will want only a portion of the 
1565
 * linked element to initiate the drag operation, and the setHandleElId() 
1566
 * method provides a way to define this.</li>
1567
 * <li>drag element: this represents an the element that would be moved along
1568
 * with the cursor during a drag operation.  By default, this is the linked
1569
 * element itself as in {@link YAHOO.util.DD}.  setDragElId() lets you define
1570
 * a separate element that would be moved, as in {@link YAHOO.util.DDProxy}
1571
 * </li>
1572
 * </ul>
1573
 * This class should not be instantiated until the onload event to ensure that
1574
 * the associated elements are available.
1575
 * The following would define a DragDrop obj that would interact with any 
1576
 * other DragDrop obj in the "group1" group:
1577
 * <pre>
1578
 *  dd = new YAHOO.util.DragDrop("div1", "group1");
1579
 * </pre>
1580
 * Since none of the event handlers have been implemented, nothing would 
1581
 * actually happen if you were to run the code above.  Normally you would 
1582
 * override this class or one of the default implementations, but you can 
1583
 * also override the methods you want on an instance of the class...
1584
 * <pre>
1585
 *  dd.onDragDrop = function(e, id) {
1586
 *  &nbsp;&nbsp;alert("dd was dropped on " + id);
1587
 *  }
1588
 * </pre>
1589
 * @namespace YAHOO.util
1590
 * @class DragDrop
1591
 * @constructor
1592
 * @param {String} id of the element that is linked to this instance
1593
 * @param {String} sGroup the group of related DragDrop objects
1594
 * @param {object} config an object containing configurable attributes
1595
 *                Valid properties for DragDrop: 
1596
 *                    padding, isTarget, maintainOffset, primaryButtonOnly,
1597
 */
1598
YAHOO.util.DragDrop = function(id, sGroup, config) {
1599
    if (id) {
1600
        this.init(id, sGroup, config); 
1601
    }
1602
};
1603

    
1604
YAHOO.util.DragDrop.prototype = {
1605
    /**
1606
     * An Object Literal containing the events that we will be using: mouseDown, b4MouseDown, mouseUp, b4StartDrag, startDrag, b4EndDrag, endDrag, mouseUp, drag, b4Drag, invalidDrop, b4DragOut, dragOut, dragEnter, b4DragOver, dragOver, b4DragDrop, dragDrop
1607
     * By setting any of these to false, then event will not be fired.
1608
    * @property events
1609
    * @type object
1610
     */
1611
    events: null,
1612
    /**
1613
   * @method on
1614
   * @description Shortcut for EventProvider.subscribe, see <a href="YAHOO.util.EventProvider.html#subscribe">YAHOO.util.EventProvider.subscribe</a>
1615
    */
1616
    on: function() {
1617
        this.subscribe.apply(this, arguments);
1618
    },
1619
    /**
1620
     * The id of the element associated with this object.  This is what we 
1621
     * refer to as the "linked element" because the size and position of 
1622
     * this element is used to determine when the drag and drop objects have 
1623
     * interacted.
1624
    * @property id
1625
    * @type String
1626
     */
1627
    id: null,
1628

    
1629
    /**
1630
     * Configuration attributes passed into the constructor
1631
    * @property config
1632
    * @type object
1633
     */
1634
    config: null,
1635

    
1636
    /**
1637
     * The id of the element that will be dragged.  By default this is same 
1638
     * as the linked element , but could be changed to another element. Ex: 
1639
     * YAHOO.util.DDProxy
1640
    * @property dragElId
1641
    * @type String
1642
    * @private
1643
     */
1644
    dragElId: null, 
1645

    
1646
    /**
1647
     * the id of the element that initiates the drag operation.  By default 
1648
     * this is the linked element, but could be changed to be a child of this
1649
     * element.  This lets us do things like only starting the drag when the 
1650
     * header element within the linked html element is clicked.
1651
    * @property handleElId
1652
    * @type String
1653
    * @private
1654
     */
1655
    handleElId: null, 
1656

    
1657
    /**
1658
     * An associative array of HTML tags that will be ignored if clicked.
1659
    * @property invalidHandleTypes
1660
    * @type {string: string}
1661
     */
1662
    invalidHandleTypes: null, 
1663

    
1664
    /**
1665
     * An associative array of ids for elements that will be ignored if clicked
1666
    * @property invalidHandleIds
1667
    * @type {string: string}
1668
     */
1669
    invalidHandleIds: null, 
1670

    
1671
    /**
1672
     * An indexted array of css class names for elements that will be ignored
1673
     * if clicked.
1674
    * @property invalidHandleClasses
1675
    * @type string[]
1676
     */
1677
    invalidHandleClasses: null, 
1678

    
1679
    /**
1680
     * The linked element's absolute X position at the time the drag was 
1681
     * started
1682
    * @property startPageX
1683
    * @type int
1684
    * @private
1685
     */
1686
    startPageX: 0,
1687

    
1688
    /**
1689
     * The linked element's absolute X position at the time the drag was 
1690
     * started
1691
    * @property startPageY
1692
    * @type int
1693
    * @private
1694
     */
1695
    startPageY: 0,
1696

    
1697
    /**
1698
     * The group defines a logical collection of DragDrop objects that are 
1699
     * related.  Instances only get events when interacting with other 
1700
     * DragDrop object in the same group.  This lets us define multiple 
1701
     * groups using a single DragDrop subclass if we want.
1702
    * @property groups
1703
    * @type {string: string}
1704
     */
1705
    groups: null,
1706

    
1707
    /**
1708
     * Individual drag/drop instances can be locked.  This will prevent 
1709
     * onmousedown start drag.
1710
    * @property locked
1711
    * @type boolean
1712
    * @private
1713
     */
1714
    locked: false,
1715

    
1716
    /**
1717
     * Lock this instance
1718
    * @method lock
1719
     */
1720
    lock: function() { this.locked = true; },
1721

    
1722
    /**
1723
     * Unlock this instace
1724
    * @method unlock
1725
     */
1726
    unlock: function() { this.locked = false; },
1727

    
1728
    /**
1729
     * By default, all instances can be a drop target.  This can be disabled by
1730
     * setting isTarget to false.
1731
    * @property isTarget
1732
    * @type boolean
1733
     */
1734
    isTarget: true,
1735

    
1736
    /**
1737
     * The padding configured for this drag and drop object for calculating
1738
     * the drop zone intersection with this object.
1739
    * @property padding
1740
    * @type int[]
1741
     */
1742
    padding: null,
1743
    /**
1744
     * If this flag is true, do not fire drop events. The element is a drag only element (for movement not dropping)
1745
    * @property dragOnly
1746
    * @type Boolean
1747
     */
1748
    dragOnly: false,
1749

    
1750
    /**
1751
     * If this flag is true, a shim will be placed over the screen/viewable area to track mouse events. Should help with dragging elements over iframes and other controls.
1752
    * @property useShim
1753
    * @type Boolean
1754
     */
1755
    useShim: false,
1756

    
1757
    /**
1758
     * Cached reference to the linked element
1759
    * @property _domRef
1760
    * @private
1761
     */
1762
    _domRef: null,
1763

    
1764
    /**
1765
     * Internal typeof flag
1766
    * @property __ygDragDrop
1767
    * @private
1768
     */
1769
    __ygDragDrop: true,
1770

    
1771
    /**
1772
     * Set to true when horizontal contraints are applied
1773
    * @property constrainX
1774
    * @type boolean
1775
    * @private
1776
     */
1777
    constrainX: false,
1778

    
1779
    /**
1780
     * Set to true when vertical contraints are applied
1781
    * @property constrainY
1782
    * @type boolean
1783
    * @private
1784
     */
1785
    constrainY: false,
1786

    
1787
    /**
1788
     * The left constraint
1789
    * @property minX
1790
    * @type int
1791
    * @private
1792
     */
1793
    minX: 0,
1794

    
1795
    /**
1796
     * The right constraint
1797
    * @property maxX
1798
    * @type int
1799
    * @private
1800
     */
1801
    maxX: 0,
1802

    
1803
    /**
1804
     * The up constraint 
1805
    * @property minY
1806
    * @type int
1807
    * @type int
1808
    * @private
1809
     */
1810
    minY: 0,
1811

    
1812
    /**
1813
     * The down constraint 
1814
    * @property maxY
1815
    * @type int
1816
    * @private
1817
     */
1818
    maxY: 0,
1819

    
1820
    /**
1821
     * The difference between the click position and the source element's location
1822
    * @property deltaX
1823
    * @type int
1824
    * @private
1825
     */
1826
    deltaX: 0,
1827

    
1828
    /**
1829
     * The difference between the click position and the source element's location
1830
    * @property deltaY
1831
    * @type int
1832
    * @private
1833
     */
1834
    deltaY: 0,
1835

    
1836
    /**
1837
     * Maintain offsets when we resetconstraints.  Set to true when you want
1838
     * the position of the element relative to its parent to stay the same
1839
     * when the page changes
1840
     *
1841
    * @property maintainOffset
1842
    * @type boolean
1843
     */
1844
    maintainOffset: false,
1845

    
1846
    /**
1847
     * Array of pixel locations the element will snap to if we specified a 
1848
     * horizontal graduation/interval.  This array is generated automatically
1849
     * when you define a tick interval.
1850
    * @property xTicks
1851
    * @type int[]
1852
     */
1853
    xTicks: null,
1854

    
1855
    /**
1856
     * Array of pixel locations the element will snap to if we specified a 
1857
     * vertical graduation/interval.  This array is generated automatically 
1858
     * when you define a tick interval.
1859
    * @property yTicks
1860
    * @type int[]
1861
     */
1862
    yTicks: null,
1863

    
1864
    /**
1865
     * By default the drag and drop instance will only respond to the primary
1866
     * button click (left button for a right-handed mouse).  Set to true to
1867
     * allow drag and drop to start with any mouse click that is propogated
1868
     * by the browser
1869
    * @property primaryButtonOnly
1870
    * @type boolean
1871
     */
1872
    primaryButtonOnly: true,
1873

    
1874
    /**
1875
     * The availabe property is false until the linked dom element is accessible.
1876
    * @property available
1877
    * @type boolean
1878
     */
1879
    available: false,
1880

    
1881
    /**
1882
     * By default, drags can only be initiated if the mousedown occurs in the
1883
     * region the linked element is.  This is done in part to work around a
1884
     * bug in some browsers that mis-report the mousedown if the previous
1885
     * mouseup happened outside of the window.  This property is set to true
1886
     * if outer handles are defined.
1887
     *
1888
    * @property hasOuterHandles
1889
    * @type boolean
1890
    * @default false
1891
     */
1892
    hasOuterHandles: false,
1893

    
1894
    /**
1895
     * Property that is assigned to a drag and drop object when testing to
1896
     * see if it is being targeted by another dd object.  This property
1897
     * can be used in intersect mode to help determine the focus of
1898
     * the mouse interaction.  DDM.getBestMatch uses this property first to
1899
     * determine the closest match in INTERSECT mode when multiple targets
1900
     * are part of the same interaction.
1901
    * @property cursorIsOver
1902
    * @type boolean
1903
     */
1904
    cursorIsOver: false,
1905

    
1906
    /**
1907
     * Property that is assigned to a drag and drop object when testing to
1908
     * see if it is being targeted by another dd object.  This is a region
1909
     * that represents the area the draggable element overlaps this target.
1910
     * DDM.getBestMatch uses this property to compare the size of the overlap
1911
     * to that of other targets in order to determine the closest match in
1912
     * INTERSECT mode when multiple targets are part of the same interaction.
1913
    * @property overlap 
1914
    * @type YAHOO.util.Region
1915
     */
1916
    overlap: null,
1917

    
1918
    /**
1919
     * Code that executes immediately before the startDrag event
1920
    * @method b4StartDrag
1921
    * @private
1922
     */
1923
    b4StartDrag: function(x, y) { },
1924

    
1925
    /**
1926
     * Abstract method called after a drag/drop object is clicked
1927
     * and the drag or mousedown time thresholds have beeen met.
1928
    * @method startDrag
1929
    * @param {int} X click location
1930
    * @param {int} Y click location
1931
     */
1932
    startDrag: function(x, y) { /* override this */ },
1933

    
1934
    /**
1935
     * Code that executes immediately before the onDrag event
1936
    * @method b4Drag
1937
    * @private
1938
     */
1939
    b4Drag: function(e) { },
1940

    
1941
    /**
1942
     * Abstract method called during the onMouseMove event while dragging an 
1943
     * object.
1944
    * @method onDrag
1945
    * @param {Event} e the mousemove event
1946
     */
1947
    onDrag: function(e) { /* override this */ },
1948

    
1949
    /**
1950
     * Abstract method called when this element fist begins hovering over 
1951
     * another DragDrop obj
1952
    * @method onDragEnter
1953
    * @param {Event} e the mousemove event
1954
    * @param {String|DragDrop[]} id In POINT mode, the element
1955
     * id this is hovering over.  In INTERSECT mode, an array of one or more 
1956
     * dragdrop items being hovered over.
1957
     */
1958
    onDragEnter: function(e, id) { /* override this */ },
1959

    
1960
    /**
1961
     * Code that executes immediately before the onDragOver event
1962
    * @method b4DragOver
1963
    * @private
1964
     */
1965
    b4DragOver: function(e) { },
1966

    
1967
    /**
1968
     * Abstract method called when this element is hovering over another 
1969
     * DragDrop obj
1970
    * @method onDragOver
1971
    * @param {Event} e the mousemove event
1972
    * @param {String|DragDrop[]} id In POINT mode, the element
1973
     * id this is hovering over.  In INTERSECT mode, an array of dd items 
1974
     * being hovered over.
1975
     */
1976
    onDragOver: function(e, id) { /* override this */ },
1977

    
1978
    /**
1979
     * Code that executes immediately before the onDragOut event
1980
    * @method b4DragOut
1981
    * @private
1982
     */
1983
    b4DragOut: function(e) { },
1984

    
1985
    /**
1986
     * Abstract method called when we are no longer hovering over an element
1987
    * @method onDragOut
1988
    * @param {Event} e the mousemove event
1989
    * @param {String|DragDrop[]} id In POINT mode, the element
1990
     * id this was hovering over.  In INTERSECT mode, an array of dd items 
1991
     * that the mouse is no longer over.
1992
     */
1993
    onDragOut: function(e, id) { /* override this */ },
1994

    
1995
    /**
1996
     * Code that executes immediately before the onDragDrop event
1997
    * @method b4DragDrop
1998
    * @private
1999
     */
2000
    b4DragDrop: function(e) { },
2001

    
2002
    /**
2003
     * Abstract method called when this item is dropped on another DragDrop 
2004
     * obj
2005
    * @method onDragDrop
2006
    * @param {Event} e the mouseup event
2007
    * @param {String|DragDrop[]} id In POINT mode, the element
2008
     * id this was dropped on.  In INTERSECT mode, an array of dd items this 
2009
     * was dropped on.
2010
     */
2011
    onDragDrop: function(e, id) { /* override this */ },
2012

    
2013
    /**
2014
     * Abstract method called when this item is dropped on an area with no
2015
     * drop target
2016
    * @method onInvalidDrop
2017
    * @param {Event} e the mouseup event
2018
     */
2019
    onInvalidDrop: function(e) { /* override this */ },
2020

    
2021
    /**
2022
     * Code that executes immediately before the endDrag event
2023
    * @method b4EndDrag
2024
    * @private
2025
     */
2026
    b4EndDrag: function(e) { },
2027

    
2028
    /**
2029
     * Fired when we are done dragging the object
2030
    * @method endDrag
2031
    * @param {Event} e the mouseup event
2032
     */
2033
    endDrag: function(e) { /* override this */ },
2034

    
2035
    /**
2036
     * Code executed immediately before the onMouseDown event
2037
    * @method b4MouseDown
2038
    * @param {Event} e the mousedown event
2039
    * @private
2040
     */
2041
    b4MouseDown: function(e) {  },
2042

    
2043
    /**
2044
     * Event handler that fires when a drag/drop obj gets a mousedown
2045
    * @method onMouseDown
2046
    * @param {Event} e the mousedown event
2047
     */
2048
    onMouseDown: function(e) { /* override this */ },
2049

    
2050
    /**
2051
     * Event handler that fires when a drag/drop obj gets a mouseup
2052
    * @method onMouseUp
2053
    * @param {Event} e the mouseup event
2054
     */
2055
    onMouseUp: function(e) { /* override this */ },
2056
   
2057
    /**
2058
     * Override the onAvailable method to do what is needed after the initial
2059
     * position was determined.
2060
    * @method onAvailable
2061
     */
2062
    onAvailable: function () { 
2063
    },
2064

    
2065
    /**
2066
     * Returns a reference to the linked element
2067
    * @method getEl
2068
    * @return {HTMLElement} the html element 
2069
     */
2070
    getEl: function() { 
2071
        if (!this._domRef) {
2072
            this._domRef = Dom.get(this.id); 
2073
        }
2074

    
2075
        return this._domRef;
2076
    },
2077

    
2078
    /**
2079
     * Returns a reference to the actual element to drag.  By default this is
2080
     * the same as the html element, but it can be assigned to another 
2081
     * element. An example of this can be found in YAHOO.util.DDProxy
2082
    * @method getDragEl
2083
    * @return {HTMLElement} the html element 
2084
     */
2085
    getDragEl: function() {
2086
        return Dom.get(this.dragElId);
2087
    },
2088

    
2089
    /**
2090
     * Sets up the DragDrop object.  Must be called in the constructor of any
2091
     * YAHOO.util.DragDrop subclass
2092
    * @method init
2093
    * @param id the id of the linked element
2094
    * @param {String} sGroup the group of related items
2095
    * @param {object} config configuration attributes
2096
     */
2097
    init: function(id, sGroup, config) {
2098
        this.initTarget(id, sGroup, config);
2099
        Event.on(this._domRef || this.id, "mousedown", 
2100
                        this.handleMouseDown, this, true);
2101

    
2102
        // Event.on(this.id, "selectstart", Event.preventDefault);
2103
        for (var i in this.events) {
2104
            this.createEvent(i + 'Event');
2105
        }
2106
        
2107
    },
2108

    
2109
    /**
2110
     * Initializes Targeting functionality only... the object does not
2111
     * get a mousedown handler.
2112
    * @method initTarget
2113
    * @param id the id of the linked element
2114
    * @param {String} sGroup the group of related items
2115
    * @param {object} config configuration attributes
2116
     */
2117
    initTarget: function(id, sGroup, config) {
2118

    
2119
        // configuration attributes 
2120
        this.config = config || {};
2121

    
2122
        this.events = {};
2123

    
2124
        // create a local reference to the drag and drop manager
2125
        this.DDM = YAHOO.util.DDM;
2126

    
2127
        // initialize the groups object
2128
        this.groups = {};
2129

    
2130
        // assume that we have an element reference instead of an id if the
2131
        // parameter is not a string
2132
        if (typeof id !== "string") {
2133
            this._domRef = id;
2134
            id = Dom.generateId(id);
2135
        }
2136

    
2137
        // set the id
2138
        this.id = id;
2139

    
2140
        // add to an interaction group
2141
        this.addToGroup((sGroup) ? sGroup : "default");
2142

    
2143
        // We don't want to register this as the handle with the manager
2144
        // so we just set the id rather than calling the setter.
2145
        this.handleElId = id;
2146

    
2147
        Event.onAvailable(id, this.handleOnAvailable, this, true);
2148

    
2149

    
2150
        // the linked element is the element that gets dragged by default
2151
        this.setDragElId(id); 
2152

    
2153
        // by default, clicked anchors will not start drag operations. 
2154
        // @TODO what else should be here?  Probably form fields.
2155
        this.invalidHandleTypes = { A: "A" };
2156
        this.invalidHandleIds = {};
2157
        this.invalidHandleClasses = [];
2158

    
2159
        this.applyConfig();
2160
    },
2161

    
2162
    /**
2163
     * Applies the configuration parameters that were passed into the constructor.
2164
     * This is supposed to happen at each level through the inheritance chain.  So
2165
     * a DDProxy implentation will execute apply config on DDProxy, DD, and 
2166
     * DragDrop in order to get all of the parameters that are available in
2167
     * each object.
2168
    * @method applyConfig
2169
     */
2170
    applyConfig: function() {
2171
        this.events = {
2172
            mouseDown: true,
2173
            b4MouseDown: true,
2174
            mouseUp: true,
2175
            b4StartDrag: true,
2176
            startDrag: true,
2177
            b4EndDrag: true,
2178
            endDrag: true,
2179
            drag: true,
2180
            b4Drag: true,
2181
            invalidDrop: true,
2182
            b4DragOut: true,
2183
            dragOut: true,
2184
            dragEnter: true,
2185
            b4DragOver: true,
2186
            dragOver: true,
2187
            b4DragDrop: true,
2188
            dragDrop: true
2189
        };
2190
        
2191
        if (this.config.events) {
2192
            for (var i in this.config.events) {
2193
                if (this.config.events[i] === false) {
2194
                    this.events[i] = false;
2195
                }
2196
            }
2197
        }
2198

    
2199

    
2200
        // configurable properties: 
2201
        //    padding, isTarget, maintainOffset, primaryButtonOnly
2202
        this.padding           = this.config.padding || [0, 0, 0, 0];
2203
        this.isTarget          = (this.config.isTarget !== false);
2204
        this.maintainOffset    = (this.config.maintainOffset);
2205
        this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
2206
        this.dragOnly = ((this.config.dragOnly === true) ? true : false);
2207
        this.useShim = ((this.config.useShim === true) ? true : false);
2208
    },
2209

    
2210
    /**
2211
     * Executed when the linked element is available
2212
    * @method handleOnAvailable
2213
    * @private
2214
     */
2215
    handleOnAvailable: function() {
2216
        this.available = true;
2217
        this.resetConstraints();
2218
        this.onAvailable();
2219
    },
2220

    
2221
     /**
2222
     * Configures the padding for the target zone in px.  Effectively expands
2223
     * (or reduces) the virtual object size for targeting calculations.  
2224
     * Supports css-style shorthand; if only one parameter is passed, all sides
2225
     * will have that padding, and if only two are passed, the top and bottom
2226
     * will have the first param, the left and right the second.
2227
    * @method setPadding
2228
    * @param {int} iTop    Top pad
2229
    * @param {int} iRight  Right pad
2230
    * @param {int} iBot    Bot pad
2231
    * @param {int} iLeft   Left pad
2232
     */
2233
    setPadding: function(iTop, iRight, iBot, iLeft) {
2234
        // this.padding = [iLeft, iRight, iTop, iBot];
2235
        if (!iRight && 0 !== iRight) {
2236
            this.padding = [iTop, iTop, iTop, iTop];
2237
        } else if (!iBot && 0 !== iBot) {
2238
            this.padding = [iTop, iRight, iTop, iRight];
2239
        } else {
2240
            this.padding = [iTop, iRight, iBot, iLeft];
2241
        }
2242
    },
2243

    
2244
    /**
2245
     * Stores the initial placement of the linked element.
2246
    * @method setInitialPosition
2247
    * @param {int} diffX   the X offset, default 0
2248
    * @param {int} diffY   the Y offset, default 0
2249
    * @private
2250
     */
2251
    setInitPosition: function(diffX, diffY) {
2252
        var el = this.getEl();
2253

    
2254
        if (!this.DDM.verifyEl(el)) {
2255
            if (el && el.style && (el.style.display == 'none')) {
2256
            } else {
2257
            }
2258
            return;
2259
        }
2260

    
2261
        var dx = diffX || 0;
2262
        var dy = diffY || 0;
2263

    
2264
        var p = Dom.getXY( el );
2265

    
2266
        this.initPageX = p[0] - dx;
2267
        this.initPageY = p[1] - dy;
2268

    
2269
        this.lastPageX = p[0];
2270
        this.lastPageY = p[1];
2271

    
2272

    
2273

    
2274
        this.setStartPosition(p);
2275
    },
2276

    
2277
    /**
2278
     * Sets the start position of the element.  This is set when the obj
2279
     * is initialized, the reset when a drag is started.
2280
    * @method setStartPosition
2281
    * @param pos current position (from previous lookup)
2282
    * @private
2283
     */
2284
    setStartPosition: function(pos) {
2285
        var p = pos || Dom.getXY(this.getEl());
2286

    
2287
        this.deltaSetXY = null;
2288

    
2289
        this.startPageX = p[0];
2290
        this.startPageY = p[1];
2291
    },
2292

    
2293
    /**
2294
     * Add this instance to a group of related drag/drop objects.  All 
2295
     * instances belong to at least one group, and can belong to as many 
2296
     * groups as needed.
2297
    * @method addToGroup
2298
    * @param sGroup {string} the name of the group
2299
     */
2300
    addToGroup: function(sGroup) {
2301
        this.groups[sGroup] = true;
2302
        this.DDM.regDragDrop(this, sGroup);
2303
    },
2304

    
2305
    /**
2306
     * Remove's this instance from the supplied interaction group
2307
    * @method removeFromGroup
2308
    * @param {string}  sGroup  The group to drop
2309
     */
2310
    removeFromGroup: function(sGroup) {
2311
        if (this.groups[sGroup]) {
2312
            delete this.groups[sGroup];
2313
        }
2314

    
2315
        this.DDM.removeDDFromGroup(this, sGroup);
2316
    },
2317

    
2318
    /**
2319
     * Allows you to specify that an element other than the linked element 
2320
     * will be moved with the cursor during a drag
2321
    * @method setDragElId
2322
    * @param id {string} the id of the element that will be used to initiate the drag
2323
     */
2324
    setDragElId: function(id) {
2325
        this.dragElId = id;
2326
    },
2327

    
2328
    /**
2329
     * Allows you to specify a child of the linked element that should be 
2330
     * used to initiate the drag operation.  An example of this would be if 
2331
     * you have a content div with text and links.  Clicking anywhere in the 
2332
     * content area would normally start the drag operation.  Use this method
2333
     * to specify that an element inside of the content div is the element 
2334
     * that starts the drag operation.
2335
    * @method setHandleElId
2336
    * @param id {string} the id of the element that will be used to 
2337
     * initiate the drag.
2338
     */
2339
    setHandleElId: function(id) {
2340
        if (typeof id !== "string") {
2341
            id = Dom.generateId(id);
2342
        }
2343
        this.handleElId = id;
2344
        this.DDM.regHandle(this.id, id);
2345
    },
2346

    
2347
    /**
2348
     * Allows you to set an element outside of the linked element as a drag 
2349
     * handle
2350
    * @method setOuterHandleElId
2351
    * @param id the id of the element that will be used to initiate the drag
2352
     */
2353
    setOuterHandleElId: function(id) {
2354
        if (typeof id !== "string") {
2355
            id = Dom.generateId(id);
2356
        }
2357
        Event.on(id, "mousedown", 
2358
                this.handleMouseDown, this, true);
2359
        this.setHandleElId(id);
2360

    
2361
        this.hasOuterHandles = true;
2362
    },
2363

    
2364
    /**
2365
     * Remove all drag and drop hooks for this element
2366
    * @method unreg
2367
     */
2368
    unreg: function() {
2369
        Event.removeListener(this.id, "mousedown", 
2370
                this.handleMouseDown);
2371
        this._domRef = null;
2372
        this.DDM._remove(this);
2373
    },
2374

    
2375
    /**
2376
     * Returns true if this instance is locked, or the drag drop mgr is locked
2377
     * (meaning that all drag/drop is disabled on the page.)
2378
    * @method isLocked
2379
    * @return {boolean} true if this obj or all drag/drop is locked, else 
2380
     * false
2381
     */
2382
    isLocked: function() {
2383
        return (this.DDM.isLocked() || this.locked);
2384
    },
2385

    
2386
    /**
2387
     * Fired when this object is clicked
2388
    * @method handleMouseDown
2389
    * @param {Event} e 
2390
    * @param {YAHOO.util.DragDrop} oDD the clicked dd object (this dd obj)
2391
    * @private
2392
     */
2393
    handleMouseDown: function(e, oDD) {
2394

    
2395
        var button = e.which || e.button;
2396

    
2397
        if (this.primaryButtonOnly && button > 1) {
2398
            return;
2399
        }
2400

    
2401
        if (this.isLocked()) {
2402
            return;
2403
        }
2404

    
2405

    
2406

    
2407
        // firing the mousedown events prior to calculating positions
2408
        var b4Return = this.b4MouseDown(e),
2409
        b4Return2 = true;
2410

    
2411
        if (this.events.b4MouseDown) {
2412
            b4Return2 = this.fireEvent('b4MouseDownEvent', e);
2413
        }
2414
        var mDownReturn = this.onMouseDown(e),
2415
            mDownReturn2 = true;
2416
        if (this.events.mouseDown) {
2417
            mDownReturn2 = this.fireEvent('mouseDownEvent', e);
2418
        }
2419

    
2420
        if ((b4Return === false) || (mDownReturn === false) || (b4Return2 === false) || (mDownReturn2 === false)) {
2421
            return;
2422
        }
2423

    
2424
        this.DDM.refreshCache(this.groups);
2425
        // var self = this;
2426
        // setTimeout( function() { self.DDM.refreshCache(self.groups); }, 0);
2427

    
2428
        // Only process the event if we really clicked within the linked 
2429
        // element.  The reason we make this check is that in the case that 
2430
        // another element was moved between the clicked element and the 
2431
        // cursor in the time between the mousedown and mouseup events. When 
2432
        // this happens, the element gets the next mousedown event 
2433
        // regardless of where on the screen it happened.  
2434
        var pt = new YAHOO.util.Point(Event.getPageX(e), Event.getPageY(e));
2435
        if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
2436
        } else {
2437
            if (this.clickValidator(e)) {
2438

    
2439

    
2440
                // set the initial element position
2441
                this.setStartPosition();
2442

    
2443
                // start tracking mousemove distance and mousedown time to
2444
                // determine when to start the actual drag
2445
                this.DDM.handleMouseDown(e, this);
2446

    
2447
                // this mousedown is mine
2448
                this.DDM.stopEvent(e);
2449
            } else {
2450

    
2451

    
2452
            }
2453
        }
2454
    },
2455

    
2456
    /**
2457
    * @method clickValidator
2458
    * @description Method validates that the clicked element
2459
     * was indeed the handle or a valid child of the handle
2460
    * @param {Event} e 
2461
     */
2462
    clickValidator: function(e) {
2463
        var target = YAHOO.util.Event.getTarget(e);
2464
        return ( this.isValidHandleChild(target) &&
2465
                    (this.id == this.handleElId || 
2466
                        this.DDM.handleWasClicked(target, this.id)) );
2467
    },
2468

    
2469
    /**
2470
     * Finds the location the element should be placed if we want to move
2471
     * it to where the mouse location less the click offset would place us.
2472
    * @method getTargetCoord
2473
    * @param {int} iPageX the X coordinate of the click
2474
    * @param {int} iPageY the Y coordinate of the click
2475
    * @return an object that contains the coordinates (Object.x and Object.y)
2476
    * @private
2477
     */
2478
    getTargetCoord: function(iPageX, iPageY) {
2479

    
2480

    
2481
        var x = iPageX - this.deltaX;
2482
        var y = iPageY - this.deltaY;
2483

    
2484
        if (this.constrainX) {
2485
            if (x < this.minX) { x = this.minX; }
2486
            if (x > this.maxX) { x = this.maxX; }
2487
        }
2488

    
2489
        if (this.constrainY) {
2490
            if (y < this.minY) { y = this.minY; }
2491
            if (y > this.maxY) { y = this.maxY; }
2492
        }
2493

    
2494
        x = this.getTick(x, this.xTicks);
2495
        y = this.getTick(y, this.yTicks);
2496

    
2497

    
2498
        return {x:x, y:y};
2499
    },
2500

    
2501
    /**
2502
     * Allows you to specify a tag name that should not start a drag operation
2503
     * when clicked.  This is designed to facilitate embedding links within a
2504
     * drag handle that do something other than start the drag.
2505
    * @method addInvalidHandleType
2506
    * @param {string} tagName the type of element to exclude
2507
     */
2508
    addInvalidHandleType: function(tagName) {
2509
        var type = tagName.toUpperCase();
2510
        this.invalidHandleTypes[type] = type;
2511
    },
2512

    
2513
    /**
2514
     * Lets you to specify an element id for a child of a drag handle
2515
     * that should not initiate a drag
2516
    * @method addInvalidHandleId
2517
    * @param {string} id the element id of the element you wish to ignore
2518
     */
2519
    addInvalidHandleId: function(id) {
2520
        if (typeof id !== "string") {
2521
            id = Dom.generateId(id);
2522
        }
2523
        this.invalidHandleIds[id] = id;
2524
    },
2525

    
2526

    
2527
    /**
2528
     * Lets you specify a css class of elements that will not initiate a drag
2529
    * @method addInvalidHandleClass
2530
    * @param {string} cssClass the class of the elements you wish to ignore
2531
     */
2532
    addInvalidHandleClass: function(cssClass) {
2533
        this.invalidHandleClasses.push(cssClass);
2534
    },
2535

    
2536
    /**
2537
     * Unsets an excluded tag name set by addInvalidHandleType
2538
    * @method removeInvalidHandleType
2539
    * @param {string} tagName the type of element to unexclude
2540
     */
2541
    removeInvalidHandleType: function(tagName) {
2542
        var type = tagName.toUpperCase();
2543
        // this.invalidHandleTypes[type] = null;
2544
        delete this.invalidHandleTypes[type];
2545
    },
2546
    
2547
    /**
2548
     * Unsets an invalid handle id
2549
    * @method removeInvalidHandleId
2550
    * @param {string} id the id of the element to re-enable
2551
     */
2552
    removeInvalidHandleId: function(id) {
2553
        if (typeof id !== "string") {
2554
            id = Dom.generateId(id);
2555
        }
2556
        delete this.invalidHandleIds[id];
2557
    },
2558

    
2559
    /**
2560
     * Unsets an invalid css class
2561
    * @method removeInvalidHandleClass
2562
    * @param {string} cssClass the class of the element(s) you wish to 
2563
     * re-enable
2564
     */
2565
    removeInvalidHandleClass: function(cssClass) {
2566
        for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
2567
            if (this.invalidHandleClasses[i] == cssClass) {
2568
                delete this.invalidHandleClasses[i];
2569
            }
2570
        }
2571
    },
2572

    
2573
    /**
2574
     * Checks the tag exclusion list to see if this click should be ignored
2575
    * @method isValidHandleChild
2576
    * @param {HTMLElement} node the HTMLElement to evaluate
2577
    * @return {boolean} true if this is a valid tag type, false if not
2578
     */
2579
    isValidHandleChild: function(node) {
2580

    
2581
        var valid = true;
2582
        // var n = (node.nodeName == "#text") ? node.parentNode : node;
2583
        var nodeName;
2584
        try {
2585
            nodeName = node.nodeName.toUpperCase();
2586
        } catch(e) {
2587
            nodeName = node.nodeName;
2588
        }
2589
        valid = valid && !this.invalidHandleTypes[nodeName];
2590
        valid = valid && !this.invalidHandleIds[node.id];
2591

    
2592
        for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
2593
            valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
2594
        }
2595

    
2596

    
2597
        return valid;
2598

    
2599
    },
2600

    
2601
    /**
2602
     * Create the array of horizontal tick marks if an interval was specified
2603
     * in setXConstraint().
2604
    * @method setXTicks
2605
    * @private
2606
     */
2607
    setXTicks: function(iStartX, iTickSize) {
2608
        this.xTicks = [];
2609
        this.xTickSize = iTickSize;
2610
        
2611
        var tickMap = {};
2612

    
2613
        for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
2614
            if (!tickMap[i]) {
2615
                this.xTicks[this.xTicks.length] = i;
2616
                tickMap[i] = true;
2617
            }
2618
        }
2619

    
2620
        for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
2621
            if (!tickMap[i]) {
2622
                this.xTicks[this.xTicks.length] = i;
2623
                tickMap[i] = true;
2624
            }
2625
        }
2626

    
2627
        this.xTicks.sort(this.DDM.numericSort) ;
2628
    },
2629

    
2630
    /**
2631
     * Create the array of vertical tick marks if an interval was specified in 
2632
     * setYConstraint().
2633
    * @method setYTicks
2634
    * @private
2635
     */
2636
    setYTicks: function(iStartY, iTickSize) {
2637
        this.yTicks = [];
2638
        this.yTickSize = iTickSize;
2639

    
2640
        var tickMap = {};
2641

    
2642
        for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
2643
            if (!tickMap[i]) {
2644
                this.yTicks[this.yTicks.length] = i;
2645
                tickMap[i] = true;
2646
            }
2647
        }
2648

    
2649
        for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
2650
            if (!tickMap[i]) {
2651
                this.yTicks[this.yTicks.length] = i;
2652
                tickMap[i] = true;
2653
            }
2654
        }
2655

    
2656
        this.yTicks.sort(this.DDM.numericSort) ;
2657
    },
2658

    
2659
    /**
2660
     * By default, the element can be dragged any place on the screen.  Use 
2661
     * this method to limit the horizontal travel of the element.  Pass in 
2662
     * 0,0 for the parameters if you want to lock the drag to the y axis.
2663
    * @method setXConstraint
2664
    * @param {int} iLeft the number of pixels the element can move to the left
2665
    * @param {int} iRight the number of pixels the element can move to the 
2666
     * right
2667
    * @param {int} iTickSize optional parameter for specifying that the 
2668
     * element
2669
     * should move iTickSize pixels at a time.
2670
     */
2671
    setXConstraint: function(iLeft, iRight, iTickSize) {
2672
        this.leftConstraint = parseInt(iLeft, 10);
2673
        this.rightConstraint = parseInt(iRight, 10);
2674

    
2675
        this.minX = this.initPageX - this.leftConstraint;
2676
        this.maxX = this.initPageX + this.rightConstraint;
2677
        if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
2678

    
2679
        this.constrainX = true;
2680
    },
2681

    
2682
    /**
2683
     * Clears any constraints applied to this instance.  Also clears ticks
2684
     * since they can't exist independent of a constraint at this time.
2685
    * @method clearConstraints
2686
     */
2687
    clearConstraints: function() {
2688
        this.constrainX = false;
2689
        this.constrainY = false;
2690
        this.clearTicks();
2691
    },
2692

    
2693
    /**
2694
     * Clears any tick interval defined for this instance
2695
    * @method clearTicks
2696
     */
2697
    clearTicks: function() {
2698
        this.xTicks = null;
2699
        this.yTicks = null;
2700
        this.xTickSize = 0;
2701
        this.yTickSize = 0;
2702
    },
2703

    
2704
    /**
2705
     * By default, the element can be dragged any place on the screen.  Set 
2706
     * this to limit the vertical travel of the element.  Pass in 0,0 for the
2707
     * parameters if you want to lock the drag to the x axis.
2708
    * @method setYConstraint
2709
    * @param {int} iUp the number of pixels the element can move up
2710
    * @param {int} iDown the number of pixels the element can move down
2711
    * @param {int} iTickSize optional parameter for specifying that the 
2712
     * element should move iTickSize pixels at a time.
2713
     */
2714
    setYConstraint: function(iUp, iDown, iTickSize) {
2715
        this.topConstraint = parseInt(iUp, 10);
2716
        this.bottomConstraint = parseInt(iDown, 10);
2717

    
2718
        this.minY = this.initPageY - this.topConstraint;
2719
        this.maxY = this.initPageY + this.bottomConstraint;
2720
        if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
2721

    
2722
        this.constrainY = true;
2723
        
2724
    },
2725

    
2726
    /**
2727
     * resetConstraints must be called if you manually reposition a dd element.
2728
    * @method resetConstraints
2729
     */
2730
    resetConstraints: function() {
2731

    
2732

    
2733
        // Maintain offsets if necessary
2734
        if (this.initPageX || this.initPageX === 0) {
2735
            // figure out how much this thing has moved
2736
            var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
2737
            var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
2738

    
2739
            this.setInitPosition(dx, dy);
2740

    
2741
        // This is the first time we have detected the element's position
2742
        } else {
2743
            this.setInitPosition();
2744
        }
2745

    
2746
        if (this.constrainX) {
2747
            this.setXConstraint( this.leftConstraint, 
2748
                                 this.rightConstraint, 
2749
                                 this.xTickSize        );
2750
        }
2751

    
2752
        if (this.constrainY) {
2753
            this.setYConstraint( this.topConstraint, 
2754
                                 this.bottomConstraint, 
2755
                                 this.yTickSize         );
2756
        }
2757
    },
2758

    
2759
    /**
2760
     * Normally the drag element is moved pixel by pixel, but we can specify 
2761
     * that it move a number of pixels at a time.  This method resolves the 
2762
     * location when we have it set up like this.
2763
    * @method getTick
2764
    * @param {int} val where we want to place the object
2765
    * @param {int[]} tickArray sorted array of valid points
2766
    * @return {int} the closest tick
2767
    * @private
2768
     */
2769
    getTick: function(val, tickArray) {
2770

    
2771
        if (!tickArray) {
2772
            // If tick interval is not defined, it is effectively 1 pixel, 
2773
            // so we return the value passed to us.
2774
            return val; 
2775
        } else if (tickArray[0] >= val) {
2776
            // The value is lower than the first tick, so we return the first
2777
            // tick.
2778
            return tickArray[0];
2779
        } else {
2780
            for (var i=0, len=tickArray.length; i<len; ++i) {
2781
                var next = i + 1;
2782
                if (tickArray[next] && tickArray[next] >= val) {
2783
                    var diff1 = val - tickArray[i];
2784
                    var diff2 = tickArray[next] - val;
2785
                    return (diff2 > diff1) ? tickArray[i] : tickArray[next];
2786
                }
2787
            }
2788

    
2789
            // The value is larger than the last tick, so we return the last
2790
            // tick.
2791
            return tickArray[tickArray.length - 1];
2792
        }
2793
    },
2794

    
2795
    /**
2796
     * toString method
2797
    * @method toString
2798
    * @return {string} string representation of the dd obj
2799
     */
2800
    toString: function() {
2801
        return ("DragDrop " + this.id);
2802
    }
2803

    
2804
};
2805
YAHOO.augment(YAHOO.util.DragDrop, YAHOO.util.EventProvider);
2806

    
2807
/**
2808
* @event mouseDownEvent
2809
* @description Provides access to the mousedown event. The mousedown does not always result in a drag operation.
2810
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2811
*/
2812

    
2813
/**
2814
* @event b4MouseDownEvent
2815
* @description Provides access to the mousedown event, before the mouseDownEvent gets fired. Returning false will cancel the drag.
2816
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2817
*/
2818

    
2819
/**
2820
* @event mouseUpEvent
2821
* @description Fired from inside DragDropMgr when the drag operation is finished.
2822
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2823
*/
2824

    
2825
/**
2826
* @event b4StartDragEvent
2827
* @description Fires before the startDragEvent, returning false will cancel the startDrag Event.
2828
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2829
*/
2830

    
2831
/**
2832
* @event startDragEvent
2833
* @description Occurs after a mouse down and the drag threshold has been met. The drag threshold default is either 3 pixels of mouse movement or 1 full second of holding the mousedown. 
2834
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2835
*/
2836

    
2837
/**
2838
* @event b4EndDragEvent
2839
* @description Fires before the endDragEvent. Returning false will cancel.
2840
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2841
*/
2842

    
2843
/**
2844
* @event endDragEvent
2845
* @description Fires on the mouseup event after a drag has been initiated (startDrag fired).
2846
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2847
*/
2848

    
2849
/**
2850
* @event dragEvent
2851
* @description Occurs every mousemove event while dragging.
2852
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2853
*/
2854
/**
2855
* @event b4DragEvent
2856
* @description Fires before the dragEvent.
2857
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2858
*/
2859
/**
2860
* @event invalidDropEvent
2861
* @description Fires when the dragged objects is dropped in a location that contains no drop targets.
2862
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2863
*/
2864
/**
2865
* @event b4DragOutEvent
2866
* @description Fires before the dragOutEvent
2867
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2868
*/
2869
/**
2870
* @event dragOutEvent
2871
* @description Fires when a dragged object is no longer over an object that had the onDragEnter fire. 
2872
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2873
*/
2874
/**
2875
* @event dragEnterEvent
2876
* @description Occurs when the dragged object first interacts with another targettable drag and drop object.
2877
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2878
*/
2879
/**
2880
* @event b4DragOverEvent
2881
* @description Fires before the dragOverEvent.
2882
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2883
*/
2884
/**
2885
* @event dragOverEvent
2886
* @description Fires every mousemove event while over a drag and drop object.
2887
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2888
*/
2889
/**
2890
* @event b4DragDropEvent 
2891
* @description Fires before the dragDropEvent
2892
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2893
*/
2894
/**
2895
* @event dragDropEvent
2896
* @description Fires when the dragged objects is dropped on another.
2897
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2898
*/
2899
})();
2900
/**
2901
 * A DragDrop implementation where the linked element follows the 
2902
 * mouse cursor during a drag.
2903
 * @class DD
2904
 * @extends YAHOO.util.DragDrop
2905
 * @constructor
2906
 * @param {String} id the id of the linked element 
2907
 * @param {String} sGroup the group of related DragDrop items
2908
 * @param {object} config an object containing configurable attributes
2909
 *                Valid properties for DD: 
2910
 *                    scroll
2911
 */
2912
YAHOO.util.DD = function(id, sGroup, config) {
2913
    if (id) {
2914
        this.init(id, sGroup, config);
2915
    }
2916
};
2917

    
2918
YAHOO.extend(YAHOO.util.DD, YAHOO.util.DragDrop, {
2919

    
2920
    /**
2921
     * When set to true, the utility automatically tries to scroll the browser
2922
     * window when a drag and drop element is dragged near the viewport boundary.
2923
     * Defaults to true.
2924
    * @property scroll
2925
    * @type boolean
2926
     */
2927
    scroll: true, 
2928

    
2929
    /**
2930
     * Sets the pointer offset to the distance between the linked element's top 
2931
     * left corner and the location the element was clicked
2932
    * @method autoOffset
2933
    * @param {int} iPageX the X coordinate of the click
2934
    * @param {int} iPageY the Y coordinate of the click
2935
     */
2936
    autoOffset: function(iPageX, iPageY) {
2937
        var x = iPageX - this.startPageX;
2938
        var y = iPageY - this.startPageY;
2939
        this.setDelta(x, y);
2940
    },
2941

    
2942
    /** 
2943
     * Sets the pointer offset.  You can call this directly to force the 
2944
     * offset to be in a particular location (e.g., pass in 0,0 to set it 
2945
     * to the center of the object, as done in YAHOO.widget.Slider)
2946
    * @method setDelta
2947
    * @param {int} iDeltaX the distance from the left
2948
    * @param {int} iDeltaY the distance from the top
2949
     */
2950
    setDelta: function(iDeltaX, iDeltaY) {
2951
        this.deltaX = iDeltaX;
2952
        this.deltaY = iDeltaY;
2953
    },
2954

    
2955
    /**
2956
     * Sets the drag element to the location of the mousedown or click event, 
2957
     * maintaining the cursor location relative to the location on the element 
2958
     * that was clicked.  Override this if you want to place the element in a 
2959
     * location other than where the cursor is.
2960
    * @method setDragElPos
2961
    * @param {int} iPageX the X coordinate of the mousedown or drag event
2962
    * @param {int} iPageY the Y coordinate of the mousedown or drag event
2963
     */
2964
    setDragElPos: function(iPageX, iPageY) {
2965
        // the first time we do this, we are going to check to make sure
2966
        // the element has css positioning
2967

    
2968
        var el = this.getDragEl();
2969
        this.alignElWithMouse(el, iPageX, iPageY);
2970
    },
2971

    
2972
    /**
2973
     * Sets the element to the location of the mousedown or click event, 
2974
     * maintaining the cursor location relative to the location on the element 
2975
     * that was clicked.  Override this if you want to place the element in a 
2976
     * location other than where the cursor is.
2977
    * @method alignElWithMouse
2978
    * @param {HTMLElement} el the element to move
2979
    * @param {int} iPageX the X coordinate of the mousedown or drag event
2980
    * @param {int} iPageY the Y coordinate of the mousedown or drag event
2981
     */
2982
    alignElWithMouse: function(el, iPageX, iPageY) {
2983
        var oCoord = this.getTargetCoord(iPageX, iPageY);
2984

    
2985
        if (!this.deltaSetXY) {
2986
            var aCoord = [oCoord.x, oCoord.y];
2987
            YAHOO.util.Dom.setXY(el, aCoord);
2988

    
2989
            var newLeft = parseInt( YAHOO.util.Dom.getStyle(el, "left"), 10 );
2990
            var newTop  = parseInt( YAHOO.util.Dom.getStyle(el, "top" ), 10 );
2991

    
2992
            this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2993
        } else {
2994
            YAHOO.util.Dom.setStyle(el, "left", (oCoord.x + this.deltaSetXY[0]) + "px");
2995
            YAHOO.util.Dom.setStyle(el, "top",  (oCoord.y + this.deltaSetXY[1]) + "px");
2996
        }
2997
        
2998
        this.cachePosition(oCoord.x, oCoord.y);
2999
        var self = this;
3000
        setTimeout(function() {
3001
            self.autoScroll.call(self, oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
3002
        }, 0);
3003
    },
3004

    
3005
    /**
3006
     * Saves the most recent position so that we can reset the constraints and
3007
     * tick marks on-demand.  We need to know this so that we can calculate the
3008
     * number of pixels the element is offset from its original position.
3009
    * @method cachePosition
3010
    * @param iPageX the current x position (optional, this just makes it so we
3011
     * don't have to look it up again)
3012
    * @param iPageY the current y position (optional, this just makes it so we
3013
     * don't have to look it up again)
3014
     */
3015
    cachePosition: function(iPageX, iPageY) {
3016
        if (iPageX) {
3017
            this.lastPageX = iPageX;
3018
            this.lastPageY = iPageY;
3019
        } else {
3020
            var aCoord = YAHOO.util.Dom.getXY(this.getEl());
3021
            this.lastPageX = aCoord[0];
3022
            this.lastPageY = aCoord[1];
3023
        }
3024
    },
3025

    
3026
    /**
3027
     * Auto-scroll the window if the dragged object has been moved beyond the 
3028
     * visible window boundary.
3029
    * @method autoScroll
3030
    * @param {int} x the drag element's x position
3031
    * @param {int} y the drag element's y position
3032
    * @param {int} h the height of the drag element
3033
    * @param {int} w the width of the drag element
3034
    * @private
3035
     */
3036
    autoScroll: function(x, y, h, w) {
3037

    
3038
        if (this.scroll) {
3039
            // The client height
3040
            var clientH = this.DDM.getClientHeight();
3041

    
3042
            // The client width
3043
            var clientW = this.DDM.getClientWidth();
3044

    
3045
            // The amt scrolled down
3046
            var st = this.DDM.getScrollTop();
3047

    
3048
            // The amt scrolled right
3049
            var sl = this.DDM.getScrollLeft();
3050

    
3051
            // Location of the bottom of the element
3052
            var bot = h + y;
3053

    
3054
            // Location of the right of the element
3055
            var right = w + x;
3056

    
3057
            // The distance from the cursor to the bottom of the visible area, 
3058
            // adjusted so that we don't scroll if the cursor is beyond the
3059
            // element drag constraints
3060
            var toBot = (clientH + st - y - this.deltaY);
3061

    
3062
            // The distance from the cursor to the right of the visible area
3063
            var toRight = (clientW + sl - x - this.deltaX);
3064

    
3065

    
3066
            // How close to the edge the cursor must be before we scroll
3067
            // var thresh = (document.all) ? 100 : 40;
3068
            var thresh = 40;
3069

    
3070
            // How many pixels to scroll per autoscroll op.  This helps to reduce 
3071
            // clunky scrolling. IE is more sensitive about this ... it needs this 
3072
            // value to be higher.
3073
            var scrAmt = (document.all) ? 80 : 30;
3074

    
3075
            // Scroll down if we are near the bottom of the visible page and the 
3076
            // obj extends below the crease
3077
            if ( bot > clientH && toBot < thresh ) { 
3078
                window.scrollTo(sl, st + scrAmt); 
3079
            }
3080

    
3081
            // Scroll up if the window is scrolled down and the top of the object
3082
            // goes above the top border
3083
            if ( y < st && st > 0 && y - st < thresh ) { 
3084
                window.scrollTo(sl, st - scrAmt); 
3085
            }
3086

    
3087
            // Scroll right if the obj is beyond the right border and the cursor is
3088
            // near the border.
3089
            if ( right > clientW && toRight < thresh ) { 
3090
                window.scrollTo(sl + scrAmt, st); 
3091
            }
3092

    
3093
            // Scroll left if the window has been scrolled to the right and the obj
3094
            // extends past the left border
3095
            if ( x < sl && sl > 0 && x - sl < thresh ) { 
3096
                window.scrollTo(sl - scrAmt, st);
3097
            }
3098
        }
3099
    },
3100

    
3101
    /*
3102
     * Sets up config options specific to this class. Overrides
3103
     * YAHOO.util.DragDrop, but all versions of this method through the 
3104
     * inheritance chain are called
3105
     */
3106
    applyConfig: function() {
3107
        YAHOO.util.DD.superclass.applyConfig.call(this);
3108
        this.scroll = (this.config.scroll !== false);
3109
    },
3110

    
3111
    /*
3112
     * Event that fires prior to the onMouseDown event.  Overrides 
3113
     * YAHOO.util.DragDrop.
3114
     */
3115
    b4MouseDown: function(e) {
3116
        this.setStartPosition();
3117
        // this.resetConstraints();
3118
        this.autoOffset(YAHOO.util.Event.getPageX(e), 
3119
                            YAHOO.util.Event.getPageY(e));
3120
    },
3121

    
3122
    /*
3123
     * Event that fires prior to the onDrag event.  Overrides 
3124
     * YAHOO.util.DragDrop.
3125
     */
3126
    b4Drag: function(e) {
3127
        this.setDragElPos(YAHOO.util.Event.getPageX(e), 
3128
                            YAHOO.util.Event.getPageY(e));
3129
    },
3130

    
3131
    toString: function() {
3132
        return ("DD " + this.id);
3133
    }
3134

    
3135
    //////////////////////////////////////////////////////////////////////////
3136
    // Debugging ygDragDrop events that can be overridden
3137
    //////////////////////////////////////////////////////////////////////////
3138
    /*
3139
    startDrag: function(x, y) {
3140
    },
3141

    
3142
    onDrag: function(e) {
3143
    },
3144

    
3145
    onDragEnter: function(e, id) {
3146
    },
3147

    
3148
    onDragOver: function(e, id) {
3149
    },
3150

    
3151
    onDragOut: function(e, id) {
3152
    },
3153

    
3154
    onDragDrop: function(e, id) {
3155
    },
3156

    
3157
    endDrag: function(e) {
3158
    }
3159

    
3160
    */
3161

    
3162
/**
3163
* @event mouseDownEvent
3164
* @description Provides access to the mousedown event. The mousedown does not always result in a drag operation.
3165
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3166
*/
3167

    
3168
/**
3169
* @event b4MouseDownEvent
3170
* @description Provides access to the mousedown event, before the mouseDownEvent gets fired. Returning false will cancel the drag.
3171
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3172
*/
3173

    
3174
/**
3175
* @event mouseUpEvent
3176
* @description Fired from inside DragDropMgr when the drag operation is finished.
3177
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3178
*/
3179

    
3180
/**
3181
* @event b4StartDragEvent
3182
* @description Fires before the startDragEvent, returning false will cancel the startDrag Event.
3183
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3184
*/
3185

    
3186
/**
3187
* @event startDragEvent
3188
* @description Occurs after a mouse down and the drag threshold has been met. The drag threshold default is either 3 pixels of mouse movement or 1 full second of holding the mousedown. 
3189
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3190
*/
3191

    
3192
/**
3193
* @event b4EndDragEvent
3194
* @description Fires before the endDragEvent. Returning false will cancel.
3195
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3196
*/
3197

    
3198
/**
3199
* @event endDragEvent
3200
* @description Fires on the mouseup event after a drag has been initiated (startDrag fired).
3201
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3202
*/
3203

    
3204
/**
3205
* @event dragEvent
3206
* @description Occurs every mousemove event while dragging.
3207
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3208
*/
3209
/**
3210
* @event b4DragEvent
3211
* @description Fires before the dragEvent.
3212
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3213
*/
3214
/**
3215
* @event invalidDropEvent
3216
* @description Fires when the dragged objects is dropped in a location that contains no drop targets.
3217
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3218
*/
3219
/**
3220
* @event b4DragOutEvent
3221
* @description Fires before the dragOutEvent
3222
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3223
*/
3224
/**
3225
* @event dragOutEvent
3226
* @description Fires when a dragged object is no longer over an object that had the onDragEnter fire. 
3227
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3228
*/
3229
/**
3230
* @event dragEnterEvent
3231
* @description Occurs when the dragged object first interacts with another targettable drag and drop object.
3232
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3233
*/
3234
/**
3235
* @event b4DragOverEvent
3236
* @description Fires before the dragOverEvent.
3237
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3238
*/
3239
/**
3240
* @event dragOverEvent
3241
* @description Fires every mousemove event while over a drag and drop object.
3242
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3243
*/
3244
/**
3245
* @event b4DragDropEvent 
3246
* @description Fires before the dragDropEvent
3247
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3248
*/
3249
/**
3250
* @event dragDropEvent
3251
* @description Fires when the dragged objects is dropped on another.
3252
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3253
*/
3254
});
3255
/**
3256
 * A DragDrop implementation that inserts an empty, bordered div into
3257
 * the document that follows the cursor during drag operations.  At the time of
3258
 * the click, the frame div is resized to the dimensions of the linked html
3259
 * element, and moved to the exact location of the linked element.
3260
 *
3261
 * References to the "frame" element refer to the single proxy element that
3262
 * was created to be dragged in place of all DDProxy elements on the
3263
 * page.
3264
 *
3265
 * @class DDProxy
3266
 * @extends YAHOO.util.DD
3267
 * @constructor
3268
 * @param {String} id the id of the linked html element
3269
 * @param {String} sGroup the group of related DragDrop objects
3270
 * @param {object} config an object containing configurable attributes
3271
 *                Valid properties for DDProxy in addition to those in DragDrop: 
3272
 *                   resizeFrame, centerFrame, dragElId
3273
 */
3274
YAHOO.util.DDProxy = function(id, sGroup, config) {
3275
    if (id) {
3276
        this.init(id, sGroup, config);
3277
        this.initFrame(); 
3278
    }
3279
};
3280

    
3281
/**
3282
 * The default drag frame div id
3283
 * @property YAHOO.util.DDProxy.dragElId
3284
 * @type String
3285
 * @static
3286
 */
3287
YAHOO.util.DDProxy.dragElId = "ygddfdiv";
3288

    
3289
YAHOO.extend(YAHOO.util.DDProxy, YAHOO.util.DD, {
3290

    
3291
    /**
3292
     * By default we resize the drag frame to be the same size as the element
3293
     * we want to drag (this is to get the frame effect).  We can turn it off
3294
     * if we want a different behavior.
3295
    * @property resizeFrame
3296
    * @type boolean
3297
     */
3298
    resizeFrame: true,
3299

    
3300
    /**
3301
     * By default the frame is positioned exactly where the drag element is, so
3302
     * we use the cursor offset provided by YAHOO.util.DD.  Another option that works only if
3303
     * you do not have constraints on the obj is to have the drag frame centered
3304
     * around the cursor.  Set centerFrame to true for this effect.
3305
    * @property centerFrame
3306
    * @type boolean
3307
     */
3308
    centerFrame: false,
3309

    
3310
    /**
3311
     * Creates the proxy element if it does not yet exist
3312
    * @method createFrame
3313
     */
3314
    createFrame: function() {
3315
        var self=this, body=document.body;
3316

    
3317
        if (!body || !body.firstChild) {
3318
            setTimeout( function() { self.createFrame(); }, 50 );
3319
            return;
3320
        }
3321

    
3322
        var div=this.getDragEl(), Dom=YAHOO.util.Dom;
3323

    
3324
        if (!div) {
3325
            div    = document.createElement("div");
3326
            div.id = this.dragElId;
3327
            var s  = div.style;
3328

    
3329
            s.position   = "absolute";
3330
            s.visibility = "hidden";
3331
            s.cursor     = "move";
3332
            s.border     = "2px solid #aaa";
3333
            s.zIndex     = 999;
3334
            s.height     = "25px";
3335
            s.width      = "25px";
3336

    
3337
            var _data = document.createElement('div');
3338
            Dom.setStyle(_data, 'height', '100%');
3339
            Dom.setStyle(_data, 'width', '100%');
3340
            /**
3341
            * If the proxy element has no background-color, then it is considered to the "transparent" by Internet Explorer.
3342
            * Since it is "transparent" then the events pass through it to the iframe below.
3343
            * So creating a "fake" div inside the proxy element and giving it a background-color, then setting it to an
3344
            * opacity of 0, it appears to not be there, however IE still thinks that it is so the events never pass through.
3345
            */
3346
            Dom.setStyle(_data, 'background-color', '#ccc');
3347
            Dom.setStyle(_data, 'opacity', '0');
3348
            div.appendChild(_data);
3349

    
3350
            // appendChild can blow up IE if invoked prior to the window load event
3351
            // while rendering a table.  It is possible there are other scenarios 
3352
            // that would cause this to happen as well.
3353
            body.insertBefore(div, body.firstChild);
3354
        }
3355
    },
3356

    
3357
    /**
3358
     * Initialization for the drag frame element.  Must be called in the
3359
     * constructor of all subclasses
3360
    * @method initFrame
3361
     */
3362
    initFrame: function() {
3363
        this.createFrame();
3364
    },
3365

    
3366
    applyConfig: function() {
3367
        YAHOO.util.DDProxy.superclass.applyConfig.call(this);
3368

    
3369
        this.resizeFrame = (this.config.resizeFrame !== false);
3370
        this.centerFrame = (this.config.centerFrame);
3371
        this.setDragElId(this.config.dragElId || YAHOO.util.DDProxy.dragElId);
3372
    },
3373

    
3374
    /**
3375
     * Resizes the drag frame to the dimensions of the clicked object, positions 
3376
     * it over the object, and finally displays it
3377
    * @method showFrame
3378
    * @param {int} iPageX X click position
3379
    * @param {int} iPageY Y click position
3380
    * @private
3381
     */
3382
    showFrame: function(iPageX, iPageY) {
3383
        var el = this.getEl();
3384
        var dragEl = this.getDragEl();
3385
        var s = dragEl.style;
3386

    
3387
        this._resizeProxy();
3388

    
3389
        if (this.centerFrame) {
3390
            this.setDelta( Math.round(parseInt(s.width,  10)/2), 
3391
                           Math.round(parseInt(s.height, 10)/2) );
3392
        }
3393

    
3394
        this.setDragElPos(iPageX, iPageY);
3395

    
3396
        YAHOO.util.Dom.setStyle(dragEl, "visibility", "visible"); 
3397
    },
3398

    
3399
    /**
3400
     * The proxy is automatically resized to the dimensions of the linked
3401
     * element when a drag is initiated, unless resizeFrame is set to false
3402
    * @method _resizeProxy
3403
    * @private
3404
     */
3405
    _resizeProxy: function() {
3406
        if (this.resizeFrame) {
3407
            var DOM    = YAHOO.util.Dom;
3408
            var el     = this.getEl();
3409
            var dragEl = this.getDragEl();
3410

    
3411
            var bt = parseInt( DOM.getStyle(dragEl, "borderTopWidth"    ), 10);
3412
            var br = parseInt( DOM.getStyle(dragEl, "borderRightWidth"  ), 10);
3413
            var bb = parseInt( DOM.getStyle(dragEl, "borderBottomWidth" ), 10);
3414
            var bl = parseInt( DOM.getStyle(dragEl, "borderLeftWidth"   ), 10);
3415

    
3416
            if (isNaN(bt)) { bt = 0; }
3417
            if (isNaN(br)) { br = 0; }
3418
            if (isNaN(bb)) { bb = 0; }
3419
            if (isNaN(bl)) { bl = 0; }
3420

    
3421

    
3422
            var newWidth  = Math.max(0, el.offsetWidth  - br - bl);                                                                                           
3423
            var newHeight = Math.max(0, el.offsetHeight - bt - bb);
3424

    
3425

    
3426
            DOM.setStyle( dragEl, "width",  newWidth  + "px" );
3427
            DOM.setStyle( dragEl, "height", newHeight + "px" );
3428
        }
3429
    },
3430

    
3431
    // overrides YAHOO.util.DragDrop
3432
    b4MouseDown: function(e) {
3433
        this.setStartPosition();
3434
        var x = YAHOO.util.Event.getPageX(e);
3435
        var y = YAHOO.util.Event.getPageY(e);
3436
        this.autoOffset(x, y);
3437

    
3438
        // This causes the autoscroll code to kick off, which means autoscroll can
3439
        // happen prior to the check for a valid drag handle.
3440
        // this.setDragElPos(x, y);
3441
    },
3442

    
3443
    // overrides YAHOO.util.DragDrop
3444
    b4StartDrag: function(x, y) {
3445
        // show the drag frame
3446
        this.showFrame(x, y);
3447
    },
3448

    
3449
    // overrides YAHOO.util.DragDrop
3450
    b4EndDrag: function(e) {
3451
        YAHOO.util.Dom.setStyle(this.getDragEl(), "visibility", "hidden"); 
3452
    },
3453

    
3454
    // overrides YAHOO.util.DragDrop
3455
    // By default we try to move the element to the last location of the frame.  
3456
    // This is so that the default behavior mirrors that of YAHOO.util.DD.  
3457
    endDrag: function(e) {
3458
        var DOM = YAHOO.util.Dom;
3459
        var lel = this.getEl();
3460
        var del = this.getDragEl();
3461

    
3462
        // Show the drag frame briefly so we can get its position
3463
        // del.style.visibility = "";
3464
        DOM.setStyle(del, "visibility", ""); 
3465

    
3466
        // Hide the linked element before the move to get around a Safari 
3467
        // rendering bug.
3468
        //lel.style.visibility = "hidden";
3469
        DOM.setStyle(lel, "visibility", "hidden"); 
3470
        YAHOO.util.DDM.moveToEl(lel, del);
3471
        //del.style.visibility = "hidden";
3472
        DOM.setStyle(del, "visibility", "hidden"); 
3473
        //lel.style.visibility = "";
3474
        DOM.setStyle(lel, "visibility", ""); 
3475
    },
3476

    
3477
    toString: function() {
3478
        return ("DDProxy " + this.id);
3479
    }
3480
/**
3481
* @event mouseDownEvent
3482
* @description Provides access to the mousedown event. The mousedown does not always result in a drag operation.
3483
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3484
*/
3485

    
3486
/**
3487
* @event b4MouseDownEvent
3488
* @description Provides access to the mousedown event, before the mouseDownEvent gets fired. Returning false will cancel the drag.
3489
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3490
*/
3491

    
3492
/**
3493
* @event mouseUpEvent
3494
* @description Fired from inside DragDropMgr when the drag operation is finished.
3495
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3496
*/
3497

    
3498
/**
3499
* @event b4StartDragEvent
3500
* @description Fires before the startDragEvent, returning false will cancel the startDrag Event.
3501
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3502
*/
3503

    
3504
/**
3505
* @event startDragEvent
3506
* @description Occurs after a mouse down and the drag threshold has been met. The drag threshold default is either 3 pixels of mouse movement or 1 full second of holding the mousedown. 
3507
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3508
*/
3509

    
3510
/**
3511
* @event b4EndDragEvent
3512
* @description Fires before the endDragEvent. Returning false will cancel.
3513
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3514
*/
3515

    
3516
/**
3517
* @event endDragEvent
3518
* @description Fires on the mouseup event after a drag has been initiated (startDrag fired).
3519
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3520
*/
3521

    
3522
/**
3523
* @event dragEvent
3524
* @description Occurs every mousemove event while dragging.
3525
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3526
*/
3527
/**
3528
* @event b4DragEvent
3529
* @description Fires before the dragEvent.
3530
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3531
*/
3532
/**
3533
* @event invalidDropEvent
3534
* @description Fires when the dragged objects is dropped in a location that contains no drop targets.
3535
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3536
*/
3537
/**
3538
* @event b4DragOutEvent
3539
* @description Fires before the dragOutEvent
3540
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3541
*/
3542
/**
3543
* @event dragOutEvent
3544
* @description Fires when a dragged object is no longer over an object that had the onDragEnter fire. 
3545
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3546
*/
3547
/**
3548
* @event dragEnterEvent
3549
* @description Occurs when the dragged object first interacts with another targettable drag and drop object.
3550
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3551
*/
3552
/**
3553
* @event b4DragOverEvent
3554
* @description Fires before the dragOverEvent.
3555
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3556
*/
3557
/**
3558
* @event dragOverEvent
3559
* @description Fires every mousemove event while over a drag and drop object.
3560
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3561
*/
3562
/**
3563
* @event b4DragDropEvent 
3564
* @description Fires before the dragDropEvent
3565
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3566
*/
3567
/**
3568
* @event dragDropEvent
3569
* @description Fires when the dragged objects is dropped on another.
3570
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3571
*/
3572

    
3573
});
3574
/**
3575
 * A DragDrop implementation that does not move, but can be a drop 
3576
 * target.  You would get the same result by simply omitting implementation 
3577
 * for the event callbacks, but this way we reduce the processing cost of the 
3578
 * event listener and the callbacks.
3579
 * @class DDTarget
3580
 * @extends YAHOO.util.DragDrop 
3581
 * @constructor
3582
 * @param {String} id the id of the element that is a drop target
3583
 * @param {String} sGroup the group of related DragDrop objects
3584
 * @param {object} config an object containing configurable attributes
3585
 *                 Valid properties for DDTarget in addition to those in 
3586
 *                 DragDrop: 
3587
 *                    none
3588
 */
3589
YAHOO.util.DDTarget = function(id, sGroup, config) {
3590
    if (id) {
3591
        this.initTarget(id, sGroup, config);
3592
    }
3593
};
3594

    
3595
// YAHOO.util.DDTarget.prototype = new YAHOO.util.DragDrop();
3596
YAHOO.extend(YAHOO.util.DDTarget, YAHOO.util.DragDrop, {
3597
    toString: function() {
3598
        return ("DDTarget " + this.id);
3599
    }
3600
});
3601
YAHOO.register("dragdrop", YAHOO.util.DragDropMgr, {version: "2.8.0r4", build: "2449"});
(4-4/5)