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
            YAHOO.log('Creating Shim Element', 'info', 'DragDropMgr');
76
            var s = document.createElement('div');
77
            s.id = 'yui-ddm-shim';
78
            if (document.body.firstChild) {
79
                document.body.insertBefore(s, document.body.firstChild);
80
            } else {
81
                document.body.appendChild(s);
82
            }
83
            s.style.display = 'none';
84
            s.style.backgroundColor = 'red';
85
            s.style.position = 'absolute';
86
            s.style.zIndex = '99999';
87
            Dom.setStyle(s, 'opacity', '0');
88
            this._shim = s;
89
            Event.on(s, "mouseup",   this.handleMouseUp, this, true);
90
            Event.on(s, "mousemove", this.handleMouseMove, this, true);
91
            Event.on(window, 'scroll', this._sizeShim, this, true);
92
        },
93
        /**
94
        * This method will size the shim, called from activate and on window scroll event
95
       * @private
96
       * @method _sizeShim
97
       * @static
98
        */
99
        _sizeShim: function() {
100
            if (this._shimActive) {
101
                YAHOO.log('Sizing Shim', 'info', 'DragDropMgr');
102
                var s = this._shim;
103
                s.style.height = Dom.getDocumentHeight() + 'px';
104
                s.style.width = Dom.getDocumentWidth() + 'px';
105
                s.style.top = '0';
106
                s.style.left = '0';
107
            }
108
        },
109
        /**
110
        * This method will create the shim element if needed, then show the shim element, size the element and set the _shimActive property to true
111
       * @private
112
       * @method _activateShim
113
       * @static
114
        */
115
        _activateShim: function() {
116
            if (this.useShim) {
117
                YAHOO.log('Activating Shim', 'info', 'DragDropMgr');
118
                if (!this._shim) {
119
                    this._createShim();
120
                }
121
                this._shimActive = true;
122
                var s = this._shim,
123
                    o = '0';
124
                if (this._debugShim) {
125
                    o = '.5';
126
                }
127
                Dom.setStyle(s, 'opacity', o);
128
                this._sizeShim();
129
                s.style.display = 'block';
130
            }
131
        },
132
        /**
133
        * This method will hide the shim element and set the _shimActive property to false
134
       * @private
135
       * @method _deactivateShim
136
       * @static
137
        */
138
        _deactivateShim: function() {
139
            YAHOO.log('Deactivating Shim', 'info', 'DragDropMgr');
140
            this._shim.style.display = 'none';
141
            this._shimActive = false;
142
        },
143
        /**
144
        * The HTML element created to use as a shim over the document to track mouse movements
145
       * @private
146
       * @property _shim
147
       * @type HTMLElement
148
       * @static
149
        */
150
        _shim: null,
151
        /**
152
         * Two dimensional Array of registered DragDrop objects.  The first 
153
         * dimension is the DragDrop item group, the second the DragDrop 
154
         * object.
155
        * @property ids
156
        * @type {string: string}
157
        * @private
158
        * @static
159
         */
160
        ids: {},
161

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

    
173
        /**
174
         * the DragDrop object that is currently being dragged
175
        * @property dragCurrent
176
        * @type DragDrop
177
        * @private
178
        * @static
179
         **/
180
        dragCurrent: null,
181

    
182
        /**
183
         * the DragDrop object(s) that are being hovered over
184
        * @property dragOvers
185
        * @type Array
186
        * @private
187
        * @static
188
         */
189
        dragOvers: {},
190

    
191
        /**
192
         * the X distance between the cursor and the object being dragged
193
        * @property deltaX
194
        * @type int
195
        * @private
196
        * @static
197
         */
198
        deltaX: 0,
199

    
200
        /**
201
         * the Y distance between the cursor and the object being dragged
202
        * @property deltaY
203
        * @type int
204
        * @private
205
        * @static
206
         */
207
        deltaY: 0,
208

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

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

    
230
        /**
231
         * Internal flag that is set to true when drag and drop has been
232
         * initialized
233
        * @property initialized
234
        * @private
235
        * @static
236
         */
237
        initialized: false,
238

    
239
        /**
240
         * All drag and drop can be disabled.
241
        * @property locked
242
        * @private
243
        * @static
244
         */
245
        locked: false,
246

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

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

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

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

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

    
309
        /**
310
         * The current drag and drop mode.  Default: POINT
311
        * @property mode
312
        * @type int
313
        * @static
314
         */
315
        mode: 0,
316

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

    
335
        /**
336
         * Drag and drop initialization.  Sets up the global event handlers
337
        * @method _onLoad
338
        * @private
339
        * @static
340
         */
341
        _onLoad: function() {
342

    
343
            this.init();
344

    
345
            YAHOO.log("DragDropMgr onload", "info", "DragDropMgr");
346
            Event.on(document, "mouseup",   this.handleMouseUp, this, true);
347
            Event.on(document, "mousemove", this.handleMouseMove, this, true);
348
            Event.on(window,   "unload",    this._onUnload, this, true);
349
            Event.on(window,   "resize",    this._onResize, this, true);
350
            // Event.on(window,   "mouseout",    this._test);
351

    
352
        },
353

    
354
        /**
355
         * Reset constraints on all drag and drop objs
356
        * @method _onResize
357
        * @private
358
        * @static
359
         */
360
        _onResize: function(e) {
361
            YAHOO.log("window resize", "info", "DragDropMgr");
362
            this._execOnAll("resetConstraints", []);
363
        },
364

    
365
        /**
366
         * Lock all drag and drop functionality
367
        * @method lock
368
        * @static
369
         */
370
        lock: function() { this.locked = true; },
371

    
372
        /**
373
         * Unlock all drag and drop functionality
374
        * @method unlock
375
        * @static
376
         */
377
        unlock: function() { this.locked = false; },
378

    
379
        /**
380
         * Is drag and drop locked?
381
        * @method isLocked
382
        * @return {boolean} True if drag and drop is locked, false otherwise.
383
        * @static
384
         */
385
        isLocked: function() { return this.locked; },
386

    
387
        /**
388
         * Location cache that is set for all drag drop objects when a drag is
389
         * initiated, cleared when the drag is finished.
390
        * @property locationCache
391
        * @private
392
        * @static
393
         */
394
        locationCache: {},
395

    
396
        /**
397
         * Set useCache to false if you want to force object the lookup of each
398
         * drag and drop linked element constantly during a drag.
399
        * @property useCache
400
        * @type boolean
401
        * @static
402
         */
403
        useCache: true,
404

    
405
        /**
406
         * The number of pixels that the mouse needs to move after the 
407
         * mousedown before the drag is initiated.  Default=3;
408
        * @property clickPixelThresh
409
        * @type int
410
        * @static
411
         */
412
        clickPixelThresh: 3,
413

    
414
        /**
415
         * The number of milliseconds after the mousedown event to initiate the
416
         * drag if we don't get a mouseup event. Default=1000
417
        * @property clickTimeThresh
418
        * @type int
419
        * @static
420
         */
421
        clickTimeThresh: 1000,
422

    
423
        /**
424
         * Flag that indicates that either the drag pixel threshold or the 
425
         * mousdown time threshold has been met
426
        * @property dragThreshMet
427
        * @type boolean
428
        * @private
429
        * @static
430
         */
431
        dragThreshMet: false,
432

    
433
        /**
434
         * Timeout used for the click time threshold
435
        * @property clickTimeout
436
        * @type Object
437
        * @private
438
        * @static
439
         */
440
        clickTimeout: null,
441

    
442
        /**
443
         * The X position of the mousedown event stored for later use when a 
444
         * drag threshold is met.
445
        * @property startX
446
        * @type int
447
        * @private
448
        * @static
449
         */
450
        startX: 0,
451

    
452
        /**
453
         * The Y position of the mousedown event stored for later use when a 
454
         * drag threshold is met.
455
        * @property startY
456
        * @type int
457
        * @private
458
        * @static
459
         */
460
        startY: 0,
461

    
462
        /**
463
         * Flag to determine if the drag event was fired from the click timeout and
464
         * not the mouse move threshold.
465
        * @property fromTimeout
466
        * @type boolean
467
        * @private
468
        * @static
469
         */
470
        fromTimeout: false,
471

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

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

    
501
            var obj = this.ids[sGroup];
502
            if (obj && obj[oDD.id]) {
503
                delete obj[oDD.id];
504
            }
505
        },
506

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

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

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

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

    
579
            return oDDs;
580
        },
581

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

    
600
            return false;
601
        },
602

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

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

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

    
648
        /**
649
         * Fired after a registered DragDrop object gets the mousedown event.
650
         * Sets up the events required to track the object being dragged
651
        * @method handleMouseDown
652
        * @param {Event} e the event
653
        * @param oDD the DragDrop object being dragged
654
        * @private
655
        * @static
656
         */
657
        handleMouseDown: function(e, oDD) {
658
            //this._activateShim();
659

    
660
            this.currentTarget = YAHOO.util.Event.getTarget(e);
661

    
662
            this.dragCurrent = oDD;
663

    
664
            var el = oDD.getEl();
665

    
666
            // track start position
667
            this.startX = YAHOO.util.Event.getPageX(e);
668
            this.startY = YAHOO.util.Event.getPageY(e);
669

    
670
            this.deltaX = this.startX - el.offsetLeft;
671
            this.deltaY = this.startY - el.offsetTop;
672

    
673
            this.dragThreshMet = false;
674

    
675
            this.clickTimeout = setTimeout( 
676
                    function() { 
677
                        var DDM = YAHOO.util.DDM;
678
                        DDM.startDrag(DDM.startX, DDM.startY);
679
                        DDM.fromTimeout = true;
680
                    }, 
681
                    this.clickTimeThresh );
682
        },
683

    
684
        /**
685
         * Fired when either the drag pixel threshold or the mousedown hold 
686
         * time threshold has been met.
687
        * @method startDrag
688
        * @param x {int} the X position of the original mousedown
689
        * @param y {int} the Y position of the original mousedown
690
        * @static
691
         */
692
        startDrag: function(x, y) {
693
            if (this.dragCurrent && this.dragCurrent.useShim) {
694
                this._shimState = this.useShim;
695
                this.useShim = true;
696
            }
697
            this._activateShim();
698
            YAHOO.log("firing drag start events", "info", "DragDropMgr");
699
            clearTimeout(this.clickTimeout);
700
            var dc = this.dragCurrent;
701
            if (dc && dc.events.b4StartDrag) {
702
                dc.b4StartDrag(x, y);
703
                dc.fireEvent('b4StartDragEvent', { x: x, y: y });
704
            }
705
            if (dc && dc.events.startDrag) {
706
                dc.startDrag(x, y);
707
                dc.fireEvent('startDragEvent', { x: x, y: y });
708
            }
709
            this.dragThreshMet = true;
710
        },
711

    
712
        /**
713
         * Internal function to handle the mouseup event.  Will be invoked 
714
         * from the context of the document.
715
        * @method handleMouseUp
716
        * @param {Event} e the event
717
        * @private
718
        * @static
719
         */
720
        handleMouseUp: function(e) {
721
            if (this.dragCurrent) {
722
                clearTimeout(this.clickTimeout);
723

    
724
                if (this.dragThreshMet) {
725
                    YAHOO.log("mouseup detected - completing drag", "info", "DragDropMgr");
726
                    if (this.fromTimeout) {
727
                        YAHOO.log('fromTimeout is true (mouse didn\'t move), call handleMouseMove so we can get the dragOver event', 'info', 'DragDropMgr');
728
                        this.fromTimeout = false;
729
                        this.handleMouseMove(e);
730
                    }
731
                    this.fromTimeout = false;
732
                    this.fireEvents(e, true);
733
                } else {
734
                    YAHOO.log("drag threshold not met", "info", "DragDropMgr");
735
                }
736

    
737
                this.stopDrag(e);
738

    
739
                this.stopEvent(e);
740
            }
741
        },
742

    
743
        /**
744
         * Utility to stop event propagation and event default, if these 
745
         * features are turned on.
746
        * @method stopEvent
747
        * @param {Event} e the event as returned by this.getEvent()
748
        * @static
749
         */
750
        stopEvent: function(e) {
751
            if (this.stopPropagation) {
752
                YAHOO.util.Event.stopPropagation(e);
753
            }
754

    
755
            if (this.preventDefault) {
756
                YAHOO.util.Event.preventDefault(e);
757
            }
758
        },
759

    
760
        /** 
761
         * Ends the current drag, cleans up the state, and fires the endDrag
762
         * and mouseUp events.  Called internally when a mouseup is detected
763
         * during the drag.  Can be fired manually during the drag by passing
764
         * either another event (such as the mousemove event received in onDrag)
765
         * or a fake event with pageX and pageY defined (so that endDrag and
766
         * onMouseUp have usable position data.).  Alternatively, pass true
767
         * for the silent parameter so that the endDrag and onMouseUp events
768
         * are skipped (so no event data is needed.)
769
         *
770
        * @method stopDrag
771
        * @param {Event} e the mouseup event, another event (or a fake event) 
772
         *                  with pageX and pageY defined, or nothing if the 
773
         *                  silent parameter is true
774
        * @param {boolean} silent skips the enddrag and mouseup events if true
775
        * @static
776
         */
777
        stopDrag: function(e, silent) {
778
            // YAHOO.log("mouseup - removing event handlers");
779
            var dc = this.dragCurrent;
780
            // Fire the drag end event for the item that was dragged
781
            if (dc && !silent) {
782
                if (this.dragThreshMet) {
783
                    YAHOO.log("firing endDrag events", "info", "DragDropMgr");
784
                    if (dc.events.b4EndDrag) {
785
                        dc.b4EndDrag(e);
786
                        dc.fireEvent('b4EndDragEvent', { e: e });
787
                    }
788
                    if (dc.events.endDrag) {
789
                        dc.endDrag(e);
790
                        dc.fireEvent('endDragEvent', { e: e });
791
                    }
792
                }
793
                if (dc.events.mouseUp) {
794
                    YAHOO.log("firing dragdrop onMouseUp event", "info", "DragDropMgr");
795
                    dc.onMouseUp(e);
796
                    dc.fireEvent('mouseUpEvent', { e: e });
797
                }
798
            }
799

    
800
            if (this._shimActive) {
801
                this._deactivateShim();
802
                if (this.dragCurrent && this.dragCurrent.useShim) {
803
                    this.useShim = this._shimState;
804
                    this._shimState = false;
805
                }
806
            }
807

    
808
            this.dragCurrent = null;
809
            this.dragOvers = {};
810
        },
811

    
812
        /** 
813
         * Internal function to handle the mousemove event.  Will be invoked 
814
         * from the context of the html element.
815
         *
816
        * @TODO figure out what we can do about mouse events lost when the 
817
         * user drags objects beyond the window boundary.  Currently we can 
818
         * detect this in internet explorer by verifying that the mouse is 
819
         * down during the mousemove event.  Firefox doesn't give us the 
820
         * button state on the mousemove event.
821
        * @method handleMouseMove
822
        * @param {Event} e the event
823
        * @private
824
        * @static
825
         */
826
        handleMouseMove: function(e) {
827
            //YAHOO.log("handlemousemove");
828

    
829
            var dc = this.dragCurrent;
830
            if (dc) {
831
                // YAHOO.log("no current drag obj");
832

    
833
                // var button = e.which || e.button;
834
                // YAHOO.log("which: " + e.which + ", button: "+ e.button);
835

    
836
                // check for IE mouseup outside of page boundary
837
                if (YAHOO.util.Event.isIE && !e.button) {
838
                    YAHOO.log("button failure", "info", "DragDropMgr");
839
                    this.stopEvent(e);
840
                    return this.handleMouseUp(e);
841
                } else {
842
                    if (e.clientX < 0 || e.clientY < 0) {
843
                        //This will stop the element from leaving the viewport in FF, Opera & Safari
844
                        //Not turned on yet
845
                        //YAHOO.log("Either clientX or clientY is negative, stop the event.", "info", "DragDropMgr");
846
                        //this.stopEvent(e);
847
                        //return false;
848
                    }
849
                }
850

    
851
                if (!this.dragThreshMet) {
852
                    var diffX = Math.abs(this.startX - YAHOO.util.Event.getPageX(e));
853
                    var diffY = Math.abs(this.startY - YAHOO.util.Event.getPageY(e));
854
                    // YAHOO.log("diffX: " + diffX + "diffY: " + diffY);
855
                    if (diffX > this.clickPixelThresh || 
856
                                diffY > this.clickPixelThresh) {
857
                        YAHOO.log("pixel threshold met", "info", "DragDropMgr");
858
                        this.startDrag(this.startX, this.startY);
859
                    }
860
                }
861

    
862
                if (this.dragThreshMet) {
863
                    if (dc && dc.events.b4Drag) {
864
                        dc.b4Drag(e);
865
                        dc.fireEvent('b4DragEvent', { e: e});
866
                    }
867
                    if (dc && dc.events.drag) {
868
                        dc.onDrag(e);
869
                        dc.fireEvent('dragEvent', { e: e});
870
                    }
871
                    if (dc) {
872
                        this.fireEvents(e, false);
873
                    }
874
                }
875

    
876
                this.stopEvent(e);
877
            }
878
        },
879
        
880
        /**
881
         * Iterates over all of the DragDrop elements to find ones we are 
882
         * hovering over or dropping on
883
        * @method fireEvents
884
        * @param {Event} e the event
885
        * @param {boolean} isDrop is this a drop op or a mouseover op?
886
        * @private
887
        * @static
888
         */
889
        fireEvents: function(e, isDrop) {
890
            var dc = this.dragCurrent;
891

    
892
            // If the user did the mouse up outside of the window, we could 
893
            // get here even though we have ended the drag.
894
            // If the config option dragOnly is true, bail out and don't fire the events
895
            if (!dc || dc.isLocked() || dc.dragOnly) {
896
                return;
897
            }
898

    
899
            var x = YAHOO.util.Event.getPageX(e),
900
                y = YAHOO.util.Event.getPageY(e),
901
                pt = new YAHOO.util.Point(x,y),
902
                pos = dc.getTargetCoord(pt.x, pt.y),
903
                el = dc.getDragEl(),
904
                events = ['out', 'over', 'drop', 'enter'],
905
                curRegion = new YAHOO.util.Region( pos.y, 
906
                                               pos.x + el.offsetWidth,
907
                                               pos.y + el.offsetHeight, 
908
                                               pos.x ),
909
            
910
                oldOvers = [], // cache the previous dragOver array
911
                inGroupsObj  = {},
912
                inGroups  = [],
913
                data = {
914
                    outEvts: [],
915
                    overEvts: [],
916
                    dropEvts: [],
917
                    enterEvts: []
918
                };
919

    
920

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

    
925
                var ddo = this.dragOvers[i];
926

    
927
                if (! this.isTypeOfDD(ddo)) {
928
                    continue;
929
                }
930
                if (! this.isOverTarget(pt, ddo, this.mode, curRegion)) {
931
                    data.outEvts.push( ddo );
932
                }
933

    
934
                oldOvers[i] = true;
935
                delete this.dragOvers[i];
936
            }
937

    
938
            for (var sGroup in dc.groups) {
939
                // YAHOO.log("Processing group " + sGroup);
940
                
941
                if ("string" != typeof sGroup) {
942
                    continue;
943
                }
944

    
945
                for (i in this.ids[sGroup]) {
946
                    var oDD = this.ids[sGroup][i];
947
                    if (! this.isTypeOfDD(oDD)) {
948
                        continue;
949
                    }
950

    
951
                    if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
952
                        if (this.isOverTarget(pt, oDD, this.mode, curRegion)) {
953
                            inGroupsObj[sGroup] = true;
954
                            // look for drop interactions
955
                            if (isDrop) {
956
                                data.dropEvts.push( oDD );
957
                            // look for drag enter and drag over interactions
958
                            } else {
959

    
960
                                // initial drag over: dragEnter fires
961
                                if (!oldOvers[oDD.id]) {
962
                                    data.enterEvts.push( oDD );
963
                                // subsequent drag overs: dragOver fires
964
                                } else {
965
                                    data.overEvts.push( oDD );
966
                                }
967

    
968
                                this.dragOvers[oDD.id] = oDD;
969
                            }
970
                        }
971
                    }
972
                }
973
            }
974

    
975
            this.interactionInfo = {
976
                out:       data.outEvts,
977
                enter:     data.enterEvts,
978
                over:      data.overEvts,
979
                drop:      data.dropEvts,
980
                point:     pt,
981
                draggedRegion:    curRegion,
982
                sourceRegion: this.locationCache[dc.id],
983
                validDrop: isDrop
984
            };
985

    
986
            
987
            for (var inG in inGroupsObj) {
988
                inGroups.push(inG);
989
            }
990

    
991
            // notify about a drop that did not find a target
992
            if (isDrop && !data.dropEvts.length) {
993
                YAHOO.log(dc.id + " dropped, but not on a target", "info", "DragDropMgr");
994
                this.interactionInfo.validDrop = false;
995
                if (dc.events.invalidDrop) {
996
                    dc.onInvalidDrop(e);
997
                    dc.fireEvent('invalidDropEvent', { e: e });
998
                }
999
            }
1000
            for (i = 0; i < events.length; i++) {
1001
                var tmp = null;
1002
                if (data[events[i] + 'Evts']) {
1003
                    tmp = data[events[i] + 'Evts'];
1004
                }
1005
                if (tmp && tmp.length) {
1006
                    var type = events[i].charAt(0).toUpperCase() + events[i].substr(1),
1007
                        ev = 'onDrag' + type,
1008
                        b4 = 'b4Drag' + type,
1009
                        cev = 'drag' + type + 'Event',
1010
                        check = 'drag' + type;
1011
                    if (this.mode) {
1012
                        YAHOO.log(dc.id + ' ' + ev + ': ' + tmp, "info", "DragDropMgr");
1013
                        if (dc.events[b4]) {
1014
                            dc[b4](e, tmp, inGroups);
1015
                            dc.fireEvent(b4 + 'Event', { event: e, info: tmp, group: inGroups });
1016
                            
1017
                        }
1018
                        if (dc.events[check]) {
1019
                            dc[ev](e, tmp, inGroups);
1020
                            dc.fireEvent(cev, { event: e, info: tmp, group: inGroups });
1021
                        }
1022
                    } else {
1023
                        for (var b = 0, len = tmp.length; b < len; ++b) {
1024
                            YAHOO.log(dc.id + ' ' + ev + ': ' + tmp[b].id, "info", "DragDropMgr");
1025
                            if (dc.events[b4]) {
1026
                                dc[b4](e, tmp[b].id, inGroups[0]);
1027
                                dc.fireEvent(b4 + 'Event', { event: e, info: tmp[b].id, group: inGroups[0] });
1028
                            }
1029
                            if (dc.events[check]) {
1030
                                dc[ev](e, tmp[b].id, inGroups[0]);
1031
                                dc.fireEvent(cev, { event: e, info: tmp[b].id, group: inGroups[0] });
1032
                            }
1033
                        }
1034
                    }
1035
                }
1036
            }
1037
        },
1038

    
1039
        /**
1040
         * Helper function for getting the best match from the list of drag 
1041
         * and drop objects returned by the drag and drop events when we are 
1042
         * in INTERSECT mode.  It returns either the first object that the 
1043
         * cursor is over, or the object that has the greatest overlap with 
1044
         * the dragged element.
1045
        * @method getBestMatch
1046
        * @param  {DragDrop[]} dds The array of drag and drop objects 
1047
         * targeted
1048
        * @return {DragDrop}       The best single match
1049
        * @static
1050
         */
1051
        getBestMatch: function(dds) {
1052
            var winner = null;
1053

    
1054
            var len = dds.length;
1055

    
1056
            if (len == 1) {
1057
                winner = dds[0];
1058
            } else {
1059
                // Loop through the targeted items
1060
                for (var i=0; i<len; ++i) {
1061
                    var dd = dds[i];
1062
                    // If the cursor is over the object, it wins.  If the 
1063
                    // cursor is over multiple matches, the first one we come
1064
                    // to wins.
1065
                    if (this.mode == this.INTERSECT && dd.cursorIsOver) {
1066
                        winner = dd;
1067
                        break;
1068
                    // Otherwise the object with the most overlap wins
1069
                    } else {
1070
                        if (!winner || !winner.overlap || (dd.overlap &&
1071
                            winner.overlap.getArea() < dd.overlap.getArea())) {
1072
                            winner = dd;
1073
                        }
1074
                    }
1075
                }
1076
            }
1077

    
1078
            return winner;
1079
        },
1080

    
1081
        /**
1082
         * Refreshes the cache of the top-left and bottom-right points of the 
1083
         * drag and drop objects in the specified group(s).  This is in the
1084
         * format that is stored in the drag and drop instance, so typical 
1085
         * usage is:
1086
         * <code>
1087
         * YAHOO.util.DragDropMgr.refreshCache(ddinstance.groups);
1088
         * </code>
1089
         * Alternatively:
1090
         * <code>
1091
         * YAHOO.util.DragDropMgr.refreshCache({group1:true, group2:true});
1092
         * </code>
1093
        * @TODO this really should be an indexed array.  Alternatively this
1094
         * method could accept both.
1095
        * @method refreshCache
1096
        * @param {Object} groups an associative array of groups to refresh
1097
        * @static
1098
         */
1099
        refreshCache: function(groups) {
1100
            YAHOO.log("refreshing element location cache", "info", "DragDropMgr");
1101

    
1102
            // refresh everything if group array is not provided
1103
            var g = groups || this.ids;
1104

    
1105
            for (var sGroup in g) {
1106
                if ("string" != typeof sGroup) {
1107
                    continue;
1108
                }
1109
                for (var i in this.ids[sGroup]) {
1110
                    var oDD = this.ids[sGroup][i];
1111

    
1112
                    if (this.isTypeOfDD(oDD)) {
1113
                        var loc = this.getLocation(oDD);
1114
                        if (loc) {
1115
                            this.locationCache[oDD.id] = loc;
1116
                        } else {
1117
                            delete this.locationCache[oDD.id];
1118
YAHOO.log("Could not get the loc for " + oDD.id, "warn", "DragDropMgr");
1119
                        }
1120
                    }
1121
                }
1122
            }
1123
        },
1124

    
1125
        /**
1126
         * This checks to make sure an element exists and is in the DOM.  The
1127
         * main purpose is to handle cases where innerHTML is used to remove
1128
         * drag and drop objects from the DOM.  IE provides an 'unspecified
1129
         * error' when trying to access the offsetParent of such an element
1130
        * @method verifyEl
1131
        * @param {HTMLElement} el the element to check
1132
        * @return {boolean} true if the element looks usable
1133
        * @static
1134
         */
1135
        verifyEl: function(el) {
1136
            try {
1137
                if (el) {
1138
                    var parent = el.offsetParent;
1139
                    if (parent) {
1140
                        return true;
1141
                    }
1142
                }
1143
            } catch(e) {
1144
                YAHOO.log("detected problem with an element", "info", "DragDropMgr");
1145
            }
1146

    
1147
            return false;
1148
        },
1149
        
1150
        /**
1151
         * Returns a Region object containing the drag and drop element's position
1152
         * and size, including the padding configured for it
1153
        * @method getLocation
1154
        * @param {DragDrop} oDD the drag and drop object to get the 
1155
         *                       location for
1156
        * @return {YAHOO.util.Region} a Region object representing the total area
1157
         *                             the element occupies, including any padding
1158
         *                             the instance is configured for.
1159
        * @static
1160
         */
1161
        getLocation: function(oDD) {
1162
            if (! this.isTypeOfDD(oDD)) {
1163
                YAHOO.log(oDD + " is not a DD obj", "info", "DragDropMgr");
1164
                return null;
1165
            }
1166

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

    
1169
            try {
1170
                pos= YAHOO.util.Dom.getXY(el);
1171
            } catch (e) { }
1172

    
1173
            if (!pos) {
1174
                YAHOO.log("getXY failed", "info", "DragDropMgr");
1175
                return null;
1176
            }
1177

    
1178
            x1 = pos[0];
1179
            x2 = x1 + el.offsetWidth;
1180
            y1 = pos[1];
1181
            y2 = y1 + el.offsetHeight;
1182

    
1183
            t = y1 - oDD.padding[0];
1184
            r = x2 + oDD.padding[1];
1185
            b = y2 + oDD.padding[2];
1186
            l = x1 - oDD.padding[3];
1187

    
1188
            return new YAHOO.util.Region( t, r, b, l );
1189
        },
1190

    
1191
        /**
1192
         * Checks the cursor location to see if it over the target
1193
        * @method isOverTarget
1194
        * @param {YAHOO.util.Point} pt The point to evaluate
1195
        * @param {DragDrop} oTarget the DragDrop object we are inspecting
1196
        * @param {boolean} intersect true if we are in intersect mode
1197
        * @param {YAHOO.util.Region} pre-cached location of the dragged element
1198
        * @return {boolean} true if the mouse is over the target
1199
        * @private
1200
        * @static
1201
         */
1202
        isOverTarget: function(pt, oTarget, intersect, curRegion) {
1203
            // use cache if available
1204
            var loc = this.locationCache[oTarget.id];
1205
            if (!loc || !this.useCache) {
1206
                YAHOO.log("cache not populated", "info", "DragDropMgr");
1207
                loc = this.getLocation(oTarget);
1208
                this.locationCache[oTarget.id] = loc;
1209

    
1210
                YAHOO.log("cache: " + loc, "info", "DragDropMgr");
1211
            }
1212

    
1213
            if (!loc) {
1214
                YAHOO.log("could not get the location of the element", "info", "DragDropMgr");
1215
                return false;
1216
            }
1217

    
1218
            //YAHOO.log("loc: " + loc + ", pt: " + pt);
1219
            oTarget.cursorIsOver = loc.contains( pt );
1220

    
1221
            // DragDrop is using this as a sanity check for the initial mousedown
1222
            // in this case we are done.  In POINT mode, if the drag obj has no
1223
            // contraints, we are done. Otherwise we need to evaluate the 
1224
            // region the target as occupies to determine if the dragged element
1225
            // overlaps with it.
1226
            
1227
            var dc = this.dragCurrent;
1228
            if (!dc || (!intersect && !dc.constrainX && !dc.constrainY)) {
1229

    
1230
                //if (oTarget.cursorIsOver) {
1231
                    //YAHOO.log("over " + oTarget + ", " + loc + ", " + pt, "warn");
1232
                //}
1233
                return oTarget.cursorIsOver;
1234
            }
1235

    
1236
            oTarget.overlap = null;
1237

    
1238

    
1239
            // Get the current location of the drag element, this is the
1240
            // location of the mouse event less the delta that represents
1241
            // where the original mousedown happened on the element.  We
1242
            // need to consider constraints and ticks as well.
1243

    
1244
            if (!curRegion) {
1245
                var pos = dc.getTargetCoord(pt.x, pt.y);
1246
                var el = dc.getDragEl();
1247
                curRegion = new YAHOO.util.Region( pos.y, 
1248
                                                   pos.x + el.offsetWidth,
1249
                                                   pos.y + el.offsetHeight, 
1250
                                                   pos.x );
1251
            }
1252

    
1253
            var overlap = curRegion.intersect(loc);
1254

    
1255
            if (overlap) {
1256
                oTarget.overlap = overlap;
1257
                return (intersect) ? true : oTarget.cursorIsOver;
1258
            } else {
1259
                return false;
1260
            }
1261
        },
1262

    
1263
        /**
1264
         * unload event handler
1265
        * @method _onUnload
1266
        * @private
1267
        * @static
1268
         */
1269
        _onUnload: function(e, me) {
1270
            this.unregAll();
1271
        },
1272

    
1273
        /**
1274
         * Cleans up the drag and drop events and objects.
1275
        * @method unregAll
1276
        * @private
1277
        * @static
1278
         */
1279
        unregAll: function() {
1280
            YAHOO.log("unregister all", "info", "DragDropMgr");
1281

    
1282
            if (this.dragCurrent) {
1283
                this.stopDrag();
1284
                this.dragCurrent = null;
1285
            }
1286

    
1287
            this._execOnAll("unreg", []);
1288

    
1289
            //for (var i in this.elementCache) {
1290
                //delete this.elementCache[i];
1291
            //}
1292
            //this.elementCache = {};
1293

    
1294
            this.ids = {};
1295
        },
1296

    
1297
        /**
1298
         * A cache of DOM elements
1299
        * @property elementCache
1300
        * @private
1301
        * @static
1302
        * @deprecated elements are not cached now
1303
         */
1304
        elementCache: {},
1305
        
1306
        /**
1307
         * Get the wrapper for the DOM element specified
1308
        * @method getElWrapper
1309
        * @param {String} id the id of the element to get
1310
        * @return {YAHOO.util.DDM.ElementWrapper} the wrapped element
1311
        * @private
1312
        * @deprecated This wrapper isn't that useful
1313
        * @static
1314
         */
1315
        getElWrapper: function(id) {
1316
            var oWrapper = this.elementCache[id];
1317
            if (!oWrapper || !oWrapper.el) {
1318
                oWrapper = this.elementCache[id] = 
1319
                    new this.ElementWrapper(YAHOO.util.Dom.get(id));
1320
            }
1321
            return oWrapper;
1322
        },
1323

    
1324
        /**
1325
         * Returns the actual DOM element
1326
        * @method getElement
1327
        * @param {String} id the id of the elment to get
1328
        * @return {Object} The element
1329
        * @deprecated use YAHOO.util.Dom.get instead
1330
        * @static
1331
         */
1332
        getElement: function(id) {
1333
            return YAHOO.util.Dom.get(id);
1334
        },
1335
        
1336
        /**
1337
         * Returns the style property for the DOM element (i.e., 
1338
         * document.getElById(id).style)
1339
        * @method getCss
1340
        * @param {String} id the id of the elment to get
1341
        * @return {Object} The style property of the element
1342
        * @deprecated use YAHOO.util.Dom instead
1343
        * @static
1344
         */
1345
        getCss: function(id) {
1346
            var el = YAHOO.util.Dom.get(id);
1347
            return (el) ? el.style : null;
1348
        },
1349

    
1350
        /**
1351
         * Inner class for cached elements
1352
        * @class DragDropMgr.ElementWrapper
1353
        * @for DragDropMgr
1354
        * @private
1355
        * @deprecated
1356
         */
1357
        ElementWrapper: function(el) {
1358
                /**
1359
                 * The element
1360
                * @property el
1361
                 */
1362
                this.el = el || null;
1363
                /**
1364
                 * The element id
1365
                * @property id
1366
                 */
1367
                this.id = this.el && el.id;
1368
                /**
1369
                 * A reference to the style property
1370
                * @property css
1371
                 */
1372
                this.css = this.el && el.style;
1373
            },
1374

    
1375
        /**
1376
         * Returns the X position of an html element
1377
        * @method getPosX
1378
        * @param el the element for which to get the position
1379
        * @return {int} the X coordinate
1380
        * @for DragDropMgr
1381
        * @deprecated use YAHOO.util.Dom.getX instead
1382
        * @static
1383
         */
1384
        getPosX: function(el) {
1385
            return YAHOO.util.Dom.getX(el);
1386
        },
1387

    
1388
        /**
1389
         * Returns the Y position of an html element
1390
        * @method getPosY
1391
        * @param el the element for which to get the position
1392
        * @return {int} the Y coordinate
1393
        * @deprecated use YAHOO.util.Dom.getY instead
1394
        * @static
1395
         */
1396
        getPosY: function(el) {
1397
            return YAHOO.util.Dom.getY(el); 
1398
        },
1399

    
1400
        /**
1401
         * Swap two nodes.  In IE, we use the native method, for others we 
1402
         * emulate the IE behavior
1403
        * @method swapNode
1404
        * @param n1 the first node to swap
1405
        * @param n2 the other node to swap
1406
        * @static
1407
         */
1408
        swapNode: function(n1, n2) {
1409
            if (n1.swapNode) {
1410
                n1.swapNode(n2);
1411
            } else {
1412
                var p = n2.parentNode;
1413
                var s = n2.nextSibling;
1414

    
1415
                if (s == n1) {
1416
                    p.insertBefore(n1, n2);
1417
                } else if (n2 == n1.nextSibling) {
1418
                    p.insertBefore(n2, n1);
1419
                } else {
1420
                    n1.parentNode.replaceChild(n2, n1);
1421
                    p.insertBefore(n1, s);
1422
                }
1423
            }
1424
        },
1425

    
1426
        /**
1427
         * Returns the current scroll position
1428
        * @method getScroll
1429
        * @private
1430
        * @static
1431
         */
1432
        getScroll: function () {
1433
            var t, l, dde=document.documentElement, db=document.body;
1434
            if (dde && (dde.scrollTop || dde.scrollLeft)) {
1435
                t = dde.scrollTop;
1436
                l = dde.scrollLeft;
1437
            } else if (db) {
1438
                t = db.scrollTop;
1439
                l = db.scrollLeft;
1440
            } else {
1441
                YAHOO.log("could not get scroll property", "info", "DragDropMgr");
1442
            }
1443
            return { top: t, left: l };
1444
        },
1445

    
1446
        /**
1447
         * Returns the specified element style property
1448
        * @method getStyle
1449
        * @param {HTMLElement} el          the element
1450
        * @param {string}      styleProp   the style property
1451
        * @return {string} The value of the style property
1452
        * @deprecated use YAHOO.util.Dom.getStyle
1453
        * @static
1454
         */
1455
        getStyle: function(el, styleProp) {
1456
            return YAHOO.util.Dom.getStyle(el, styleProp);
1457
        },
1458

    
1459
        /**
1460
         * Gets the scrollTop
1461
        * @method getScrollTop
1462
        * @return {int} the document's scrollTop
1463
        * @static
1464
         */
1465
        getScrollTop: function () { return this.getScroll().top; },
1466

    
1467
        /**
1468
         * Gets the scrollLeft
1469
        * @method getScrollLeft
1470
        * @return {int} the document's scrollTop
1471
        * @static
1472
         */
1473
        getScrollLeft: function () { return this.getScroll().left; },
1474

    
1475
        /**
1476
         * Sets the x/y position of an element to the location of the
1477
         * target element.
1478
        * @method moveToEl
1479
        * @param {HTMLElement} moveEl      The element to move
1480
        * @param {HTMLElement} targetEl    The position reference element
1481
        * @static
1482
         */
1483
        moveToEl: function (moveEl, targetEl) {
1484
            var aCoord = YAHOO.util.Dom.getXY(targetEl);
1485
            YAHOO.log("moveToEl: " + aCoord, "info", "DragDropMgr");
1486
            YAHOO.util.Dom.setXY(moveEl, aCoord);
1487
        },
1488

    
1489
        /**
1490
         * Gets the client height
1491
        * @method getClientHeight
1492
        * @return {int} client height in px
1493
        * @deprecated use YAHOO.util.Dom.getViewportHeight instead
1494
        * @static
1495
         */
1496
        getClientHeight: function() {
1497
            return YAHOO.util.Dom.getViewportHeight();
1498
        },
1499

    
1500
        /**
1501
         * Gets the client width
1502
        * @method getClientWidth
1503
        * @return {int} client width in px
1504
        * @deprecated use YAHOO.util.Dom.getViewportWidth instead
1505
        * @static
1506
         */
1507
        getClientWidth: function() {
1508
            return YAHOO.util.Dom.getViewportWidth();
1509
        },
1510

    
1511
        /**
1512
         * Numeric array sort function
1513
        * @method numericSort
1514
        * @static
1515
         */
1516
        numericSort: function(a, b) { return (a - b); },
1517

    
1518
        /**
1519
         * Internal counter
1520
        * @property _timeoutCount
1521
        * @private
1522
        * @static
1523
         */
1524
        _timeoutCount: 0,
1525

    
1526
        /**
1527
         * Trying to make the load order less important.  Without this we get
1528
         * an error if this file is loaded before the Event Utility.
1529
        * @method _addListeners
1530
        * @private
1531
        * @static
1532
         */
1533
        _addListeners: function() {
1534
            var DDM = YAHOO.util.DDM;
1535
            if ( YAHOO.util.Event && document ) {
1536
                DDM._onLoad();
1537
            } else {
1538
                if (DDM._timeoutCount > 2000) {
1539
                    YAHOO.log("DragDrop requires the Event Utility", "error", "DragDropMgr");
1540
                } else {
1541
                    setTimeout(DDM._addListeners, 10);
1542
                    if (document && document.body) {
1543
                        DDM._timeoutCount += 1;
1544
                    }
1545
                }
1546
            }
1547
        },
1548

    
1549
        /**
1550
         * Recursively searches the immediate parent and all child nodes for 
1551
         * the handle element in order to determine wheter or not it was 
1552
         * clicked.
1553
        * @method handleWasClicked
1554
        * @param node the html element to inspect
1555
        * @static
1556
         */
1557
        handleWasClicked: function(node, id) {
1558
            if (this.isHandle(id, node.id)) {
1559
                YAHOO.log("clicked node is a handle", "info", "DragDropMgr");
1560
                return true;
1561
            } else {
1562
                // check to see if this is a text node child of the one we want
1563
                var p = node.parentNode;
1564
                // YAHOO.log("p: " + p);
1565

    
1566
                while (p) {
1567
                    if (this.isHandle(id, p.id)) {
1568
                        return true;
1569
                    } else {
1570
                        YAHOO.log(p.id + " is not a handle", "info", "DragDropMgr");
1571
                        p = p.parentNode;
1572
                    }
1573
                }
1574
            }
1575

    
1576
            return false;
1577
        }
1578

    
1579
    };
1580

    
1581
}();
1582

    
1583
// shorter alias, save a few bytes
1584
YAHOO.util.DDM = YAHOO.util.DragDropMgr;
1585
YAHOO.util.DDM._addListeners();
1586

    
1587
}
1588

    
1589
(function() {
1590

    
1591
var Event=YAHOO.util.Event; 
1592
var Dom=YAHOO.util.Dom;
1593

    
1594
/**
1595
 * Defines the interface and base operation of items that that can be 
1596
 * dragged or can be drop targets.  It was designed to be extended, overriding
1597
 * the event handlers for startDrag, onDrag, onDragOver, onDragOut.
1598
 * Up to three html elements can be associated with a DragDrop instance:
1599
 * <ul>
1600
 * <li>linked element: the element that is passed into the constructor.
1601
 * This is the element which defines the boundaries for interaction with 
1602
 * other DragDrop objects.</li>
1603
 * <li>handle element(s): The drag operation only occurs if the element that 
1604
 * was clicked matches a handle element.  By default this is the linked 
1605
 * element, but there are times that you will want only a portion of the 
1606
 * linked element to initiate the drag operation, and the setHandleElId() 
1607
 * method provides a way to define this.</li>
1608
 * <li>drag element: this represents an the element that would be moved along
1609
 * with the cursor during a drag operation.  By default, this is the linked
1610
 * element itself as in {@link YAHOO.util.DD}.  setDragElId() lets you define
1611
 * a separate element that would be moved, as in {@link YAHOO.util.DDProxy}
1612
 * </li>
1613
 * </ul>
1614
 * This class should not be instantiated until the onload event to ensure that
1615
 * the associated elements are available.
1616
 * The following would define a DragDrop obj that would interact with any 
1617
 * other DragDrop obj in the "group1" group:
1618
 * <pre>
1619
 *  dd = new YAHOO.util.DragDrop("div1", "group1");
1620
 * </pre>
1621
 * Since none of the event handlers have been implemented, nothing would 
1622
 * actually happen if you were to run the code above.  Normally you would 
1623
 * override this class or one of the default implementations, but you can 
1624
 * also override the methods you want on an instance of the class...
1625
 * <pre>
1626
 *  dd.onDragDrop = function(e, id) {
1627
 *  &nbsp;&nbsp;alert("dd was dropped on " + id);
1628
 *  }
1629
 * </pre>
1630
 * @namespace YAHOO.util
1631
 * @class DragDrop
1632
 * @constructor
1633
 * @param {String} id of the element that is linked to this instance
1634
 * @param {String} sGroup the group of related DragDrop objects
1635
 * @param {object} config an object containing configurable attributes
1636
 *                Valid properties for DragDrop: 
1637
 *                    padding, isTarget, maintainOffset, primaryButtonOnly,
1638
 */
1639
YAHOO.util.DragDrop = function(id, sGroup, config) {
1640
    if (id) {
1641
        this.init(id, sGroup, config); 
1642
    }
1643
};
1644

    
1645
YAHOO.util.DragDrop.prototype = {
1646
    /**
1647
     * An Object Literal containing the events that we will be using: mouseDown, b4MouseDown, mouseUp, b4StartDrag, startDrag, b4EndDrag, endDrag, mouseUp, drag, b4Drag, invalidDrop, b4DragOut, dragOut, dragEnter, b4DragOver, dragOver, b4DragDrop, dragDrop
1648
     * By setting any of these to false, then event will not be fired.
1649
    * @property events
1650
    * @type object
1651
     */
1652
    events: null,
1653
    /**
1654
   * @method on
1655
   * @description Shortcut for EventProvider.subscribe, see <a href="YAHOO.util.EventProvider.html#subscribe">YAHOO.util.EventProvider.subscribe</a>
1656
    */
1657
    on: function() {
1658
        this.subscribe.apply(this, arguments);
1659
    },
1660
    /**
1661
     * The id of the element associated with this object.  This is what we 
1662
     * refer to as the "linked element" because the size and position of 
1663
     * this element is used to determine when the drag and drop objects have 
1664
     * interacted.
1665
    * @property id
1666
    * @type String
1667
     */
1668
    id: null,
1669

    
1670
    /**
1671
     * Configuration attributes passed into the constructor
1672
    * @property config
1673
    * @type object
1674
     */
1675
    config: null,
1676

    
1677
    /**
1678
     * The id of the element that will be dragged.  By default this is same 
1679
     * as the linked element , but could be changed to another element. Ex: 
1680
     * YAHOO.util.DDProxy
1681
    * @property dragElId
1682
    * @type String
1683
    * @private
1684
     */
1685
    dragElId: null, 
1686

    
1687
    /**
1688
     * the id of the element that initiates the drag operation.  By default 
1689
     * this is the linked element, but could be changed to be a child of this
1690
     * element.  This lets us do things like only starting the drag when the 
1691
     * header element within the linked html element is clicked.
1692
    * @property handleElId
1693
    * @type String
1694
    * @private
1695
     */
1696
    handleElId: null, 
1697

    
1698
    /**
1699
     * An associative array of HTML tags that will be ignored if clicked.
1700
    * @property invalidHandleTypes
1701
    * @type {string: string}
1702
     */
1703
    invalidHandleTypes: null, 
1704

    
1705
    /**
1706
     * An associative array of ids for elements that will be ignored if clicked
1707
    * @property invalidHandleIds
1708
    * @type {string: string}
1709
     */
1710
    invalidHandleIds: null, 
1711

    
1712
    /**
1713
     * An indexted array of css class names for elements that will be ignored
1714
     * if clicked.
1715
    * @property invalidHandleClasses
1716
    * @type string[]
1717
     */
1718
    invalidHandleClasses: null, 
1719

    
1720
    /**
1721
     * The linked element's absolute X position at the time the drag was 
1722
     * started
1723
    * @property startPageX
1724
    * @type int
1725
    * @private
1726
     */
1727
    startPageX: 0,
1728

    
1729
    /**
1730
     * The linked element's absolute X position at the time the drag was 
1731
     * started
1732
    * @property startPageY
1733
    * @type int
1734
    * @private
1735
     */
1736
    startPageY: 0,
1737

    
1738
    /**
1739
     * The group defines a logical collection of DragDrop objects that are 
1740
     * related.  Instances only get events when interacting with other 
1741
     * DragDrop object in the same group.  This lets us define multiple 
1742
     * groups using a single DragDrop subclass if we want.
1743
    * @property groups
1744
    * @type {string: string}
1745
     */
1746
    groups: null,
1747

    
1748
    /**
1749
     * Individual drag/drop instances can be locked.  This will prevent 
1750
     * onmousedown start drag.
1751
    * @property locked
1752
    * @type boolean
1753
    * @private
1754
     */
1755
    locked: false,
1756

    
1757
    /**
1758
     * Lock this instance
1759
    * @method lock
1760
     */
1761
    lock: function() { this.locked = true; },
1762

    
1763
    /**
1764
     * Unlock this instace
1765
    * @method unlock
1766
     */
1767
    unlock: function() { this.locked = false; },
1768

    
1769
    /**
1770
     * By default, all instances can be a drop target.  This can be disabled by
1771
     * setting isTarget to false.
1772
    * @property isTarget
1773
    * @type boolean
1774
     */
1775
    isTarget: true,
1776

    
1777
    /**
1778
     * The padding configured for this drag and drop object for calculating
1779
     * the drop zone intersection with this object.
1780
    * @property padding
1781
    * @type int[]
1782
     */
1783
    padding: null,
1784
    /**
1785
     * If this flag is true, do not fire drop events. The element is a drag only element (for movement not dropping)
1786
    * @property dragOnly
1787
    * @type Boolean
1788
     */
1789
    dragOnly: false,
1790

    
1791
    /**
1792
     * If this flag is true, a shim will be placed over the screen/viewable area to track mouse events. Should help with dragging elements over iframes and other controls.
1793
    * @property useShim
1794
    * @type Boolean
1795
     */
1796
    useShim: false,
1797

    
1798
    /**
1799
     * Cached reference to the linked element
1800
    * @property _domRef
1801
    * @private
1802
     */
1803
    _domRef: null,
1804

    
1805
    /**
1806
     * Internal typeof flag
1807
    * @property __ygDragDrop
1808
    * @private
1809
     */
1810
    __ygDragDrop: true,
1811

    
1812
    /**
1813
     * Set to true when horizontal contraints are applied
1814
    * @property constrainX
1815
    * @type boolean
1816
    * @private
1817
     */
1818
    constrainX: false,
1819

    
1820
    /**
1821
     * Set to true when vertical contraints are applied
1822
    * @property constrainY
1823
    * @type boolean
1824
    * @private
1825
     */
1826
    constrainY: false,
1827

    
1828
    /**
1829
     * The left constraint
1830
    * @property minX
1831
    * @type int
1832
    * @private
1833
     */
1834
    minX: 0,
1835

    
1836
    /**
1837
     * The right constraint
1838
    * @property maxX
1839
    * @type int
1840
    * @private
1841
     */
1842
    maxX: 0,
1843

    
1844
    /**
1845
     * The up constraint 
1846
    * @property minY
1847
    * @type int
1848
    * @type int
1849
    * @private
1850
     */
1851
    minY: 0,
1852

    
1853
    /**
1854
     * The down constraint 
1855
    * @property maxY
1856
    * @type int
1857
    * @private
1858
     */
1859
    maxY: 0,
1860

    
1861
    /**
1862
     * The difference between the click position and the source element's location
1863
    * @property deltaX
1864
    * @type int
1865
    * @private
1866
     */
1867
    deltaX: 0,
1868

    
1869
    /**
1870
     * The difference between the click position and the source element's location
1871
    * @property deltaY
1872
    * @type int
1873
    * @private
1874
     */
1875
    deltaY: 0,
1876

    
1877
    /**
1878
     * Maintain offsets when we resetconstraints.  Set to true when you want
1879
     * the position of the element relative to its parent to stay the same
1880
     * when the page changes
1881
     *
1882
    * @property maintainOffset
1883
    * @type boolean
1884
     */
1885
    maintainOffset: false,
1886

    
1887
    /**
1888
     * Array of pixel locations the element will snap to if we specified a 
1889
     * horizontal graduation/interval.  This array is generated automatically
1890
     * when you define a tick interval.
1891
    * @property xTicks
1892
    * @type int[]
1893
     */
1894
    xTicks: null,
1895

    
1896
    /**
1897
     * Array of pixel locations the element will snap to if we specified a 
1898
     * vertical graduation/interval.  This array is generated automatically 
1899
     * when you define a tick interval.
1900
    * @property yTicks
1901
    * @type int[]
1902
     */
1903
    yTicks: null,
1904

    
1905
    /**
1906
     * By default the drag and drop instance will only respond to the primary
1907
     * button click (left button for a right-handed mouse).  Set to true to
1908
     * allow drag and drop to start with any mouse click that is propogated
1909
     * by the browser
1910
    * @property primaryButtonOnly
1911
    * @type boolean
1912
     */
1913
    primaryButtonOnly: true,
1914

    
1915
    /**
1916
     * The availabe property is false until the linked dom element is accessible.
1917
    * @property available
1918
    * @type boolean
1919
     */
1920
    available: false,
1921

    
1922
    /**
1923
     * By default, drags can only be initiated if the mousedown occurs in the
1924
     * region the linked element is.  This is done in part to work around a
1925
     * bug in some browsers that mis-report the mousedown if the previous
1926
     * mouseup happened outside of the window.  This property is set to true
1927
     * if outer handles are defined.
1928
     *
1929
    * @property hasOuterHandles
1930
    * @type boolean
1931
    * @default false
1932
     */
1933
    hasOuterHandles: false,
1934

    
1935
    /**
1936
     * Property that is assigned to a drag and drop object when testing to
1937
     * see if it is being targeted by another dd object.  This property
1938
     * can be used in intersect mode to help determine the focus of
1939
     * the mouse interaction.  DDM.getBestMatch uses this property first to
1940
     * determine the closest match in INTERSECT mode when multiple targets
1941
     * are part of the same interaction.
1942
    * @property cursorIsOver
1943
    * @type boolean
1944
     */
1945
    cursorIsOver: false,
1946

    
1947
    /**
1948
     * Property that is assigned to a drag and drop object when testing to
1949
     * see if it is being targeted by another dd object.  This is a region
1950
     * that represents the area the draggable element overlaps this target.
1951
     * DDM.getBestMatch uses this property to compare the size of the overlap
1952
     * to that of other targets in order to determine the closest match in
1953
     * INTERSECT mode when multiple targets are part of the same interaction.
1954
    * @property overlap 
1955
    * @type YAHOO.util.Region
1956
     */
1957
    overlap: null,
1958

    
1959
    /**
1960
     * Code that executes immediately before the startDrag event
1961
    * @method b4StartDrag
1962
    * @private
1963
     */
1964
    b4StartDrag: function(x, y) { },
1965

    
1966
    /**
1967
     * Abstract method called after a drag/drop object is clicked
1968
     * and the drag or mousedown time thresholds have beeen met.
1969
    * @method startDrag
1970
    * @param {int} X click location
1971
    * @param {int} Y click location
1972
     */
1973
    startDrag: function(x, y) { /* override this */ },
1974

    
1975
    /**
1976
     * Code that executes immediately before the onDrag event
1977
    * @method b4Drag
1978
    * @private
1979
     */
1980
    b4Drag: function(e) { },
1981

    
1982
    /**
1983
     * Abstract method called during the onMouseMove event while dragging an 
1984
     * object.
1985
    * @method onDrag
1986
    * @param {Event} e the mousemove event
1987
     */
1988
    onDrag: function(e) { /* override this */ },
1989

    
1990
    /**
1991
     * Abstract method called when this element fist begins hovering over 
1992
     * another DragDrop obj
1993
    * @method onDragEnter
1994
    * @param {Event} e the mousemove event
1995
    * @param {String|DragDrop[]} id In POINT mode, the element
1996
     * id this is hovering over.  In INTERSECT mode, an array of one or more 
1997
     * dragdrop items being hovered over.
1998
     */
1999
    onDragEnter: function(e, id) { /* override this */ },
2000

    
2001
    /**
2002
     * Code that executes immediately before the onDragOver event
2003
    * @method b4DragOver
2004
    * @private
2005
     */
2006
    b4DragOver: function(e) { },
2007

    
2008
    /**
2009
     * Abstract method called when this element is hovering over another 
2010
     * DragDrop obj
2011
    * @method onDragOver
2012
    * @param {Event} e the mousemove event
2013
    * @param {String|DragDrop[]} id In POINT mode, the element
2014
     * id this is hovering over.  In INTERSECT mode, an array of dd items 
2015
     * being hovered over.
2016
     */
2017
    onDragOver: function(e, id) { /* override this */ },
2018

    
2019
    /**
2020
     * Code that executes immediately before the onDragOut event
2021
    * @method b4DragOut
2022
    * @private
2023
     */
2024
    b4DragOut: function(e) { },
2025

    
2026
    /**
2027
     * Abstract method called when we are no longer hovering over an element
2028
    * @method onDragOut
2029
    * @param {Event} e the mousemove event
2030
    * @param {String|DragDrop[]} id In POINT mode, the element
2031
     * id this was hovering over.  In INTERSECT mode, an array of dd items 
2032
     * that the mouse is no longer over.
2033
     */
2034
    onDragOut: function(e, id) { /* override this */ },
2035

    
2036
    /**
2037
     * Code that executes immediately before the onDragDrop event
2038
    * @method b4DragDrop
2039
    * @private
2040
     */
2041
    b4DragDrop: function(e) { },
2042

    
2043
    /**
2044
     * Abstract method called when this item is dropped on another DragDrop 
2045
     * obj
2046
    * @method onDragDrop
2047
    * @param {Event} e the mouseup event
2048
    * @param {String|DragDrop[]} id In POINT mode, the element
2049
     * id this was dropped on.  In INTERSECT mode, an array of dd items this 
2050
     * was dropped on.
2051
     */
2052
    onDragDrop: function(e, id) { /* override this */ },
2053

    
2054
    /**
2055
     * Abstract method called when this item is dropped on an area with no
2056
     * drop target
2057
    * @method onInvalidDrop
2058
    * @param {Event} e the mouseup event
2059
     */
2060
    onInvalidDrop: function(e) { /* override this */ },
2061

    
2062
    /**
2063
     * Code that executes immediately before the endDrag event
2064
    * @method b4EndDrag
2065
    * @private
2066
     */
2067
    b4EndDrag: function(e) { },
2068

    
2069
    /**
2070
     * Fired when we are done dragging the object
2071
    * @method endDrag
2072
    * @param {Event} e the mouseup event
2073
     */
2074
    endDrag: function(e) { /* override this */ },
2075

    
2076
    /**
2077
     * Code executed immediately before the onMouseDown event
2078
    * @method b4MouseDown
2079
    * @param {Event} e the mousedown event
2080
    * @private
2081
     */
2082
    b4MouseDown: function(e) {  },
2083

    
2084
    /**
2085
     * Event handler that fires when a drag/drop obj gets a mousedown
2086
    * @method onMouseDown
2087
    * @param {Event} e the mousedown event
2088
     */
2089
    onMouseDown: function(e) { /* override this */ },
2090

    
2091
    /**
2092
     * Event handler that fires when a drag/drop obj gets a mouseup
2093
    * @method onMouseUp
2094
    * @param {Event} e the mouseup event
2095
     */
2096
    onMouseUp: function(e) { /* override this */ },
2097
   
2098
    /**
2099
     * Override the onAvailable method to do what is needed after the initial
2100
     * position was determined.
2101
    * @method onAvailable
2102
     */
2103
    onAvailable: function () { 
2104
        //this.logger.log("onAvailable (base)"); 
2105
    },
2106

    
2107
    /**
2108
     * Returns a reference to the linked element
2109
    * @method getEl
2110
    * @return {HTMLElement} the html element 
2111
     */
2112
    getEl: function() { 
2113
        if (!this._domRef) {
2114
            this._domRef = Dom.get(this.id); 
2115
        }
2116

    
2117
        return this._domRef;
2118
    },
2119

    
2120
    /**
2121
     * Returns a reference to the actual element to drag.  By default this is
2122
     * the same as the html element, but it can be assigned to another 
2123
     * element. An example of this can be found in YAHOO.util.DDProxy
2124
    * @method getDragEl
2125
    * @return {HTMLElement} the html element 
2126
     */
2127
    getDragEl: function() {
2128
        return Dom.get(this.dragElId);
2129
    },
2130

    
2131
    /**
2132
     * Sets up the DragDrop object.  Must be called in the constructor of any
2133
     * YAHOO.util.DragDrop subclass
2134
    * @method init
2135
    * @param id the id of the linked element
2136
    * @param {String} sGroup the group of related items
2137
    * @param {object} config configuration attributes
2138
     */
2139
    init: function(id, sGroup, config) {
2140
        this.initTarget(id, sGroup, config);
2141
        Event.on(this._domRef || this.id, "mousedown", 
2142
                        this.handleMouseDown, this, true);
2143

    
2144
        // Event.on(this.id, "selectstart", Event.preventDefault);
2145
        for (var i in this.events) {
2146
            this.createEvent(i + 'Event');
2147
        }
2148
        
2149
    },
2150

    
2151
    /**
2152
     * Initializes Targeting functionality only... the object does not
2153
     * get a mousedown handler.
2154
    * @method initTarget
2155
    * @param id the id of the linked element
2156
    * @param {String} sGroup the group of related items
2157
    * @param {object} config configuration attributes
2158
     */
2159
    initTarget: function(id, sGroup, config) {
2160

    
2161
        // configuration attributes 
2162
        this.config = config || {};
2163

    
2164
        this.events = {};
2165

    
2166
        // create a local reference to the drag and drop manager
2167
        this.DDM = YAHOO.util.DDM;
2168

    
2169
        // initialize the groups object
2170
        this.groups = {};
2171

    
2172
        // assume that we have an element reference instead of an id if the
2173
        // parameter is not a string
2174
        if (typeof id !== "string") {
2175
            YAHOO.log("id is not a string, assuming it is an HTMLElement");
2176
            this._domRef = id;
2177
            id = Dom.generateId(id);
2178
        }
2179

    
2180
        // set the id
2181
        this.id = id;
2182

    
2183
        // add to an interaction group
2184
        this.addToGroup((sGroup) ? sGroup : "default");
2185

    
2186
        // We don't want to register this as the handle with the manager
2187
        // so we just set the id rather than calling the setter.
2188
        this.handleElId = id;
2189

    
2190
        Event.onAvailable(id, this.handleOnAvailable, this, true);
2191

    
2192
        // create a logger instance
2193
        this.logger = (YAHOO.widget.LogWriter) ? 
2194
                new YAHOO.widget.LogWriter(this.toString()) : YAHOO;
2195

    
2196
        // the linked element is the element that gets dragged by default
2197
        this.setDragElId(id); 
2198

    
2199
        // by default, clicked anchors will not start drag operations. 
2200
        // @TODO what else should be here?  Probably form fields.
2201
        this.invalidHandleTypes = { A: "A" };
2202
        this.invalidHandleIds = {};
2203
        this.invalidHandleClasses = [];
2204

    
2205
        this.applyConfig();
2206
    },
2207

    
2208
    /**
2209
     * Applies the configuration parameters that were passed into the constructor.
2210
     * This is supposed to happen at each level through the inheritance chain.  So
2211
     * a DDProxy implentation will execute apply config on DDProxy, DD, and 
2212
     * DragDrop in order to get all of the parameters that are available in
2213
     * each object.
2214
    * @method applyConfig
2215
     */
2216
    applyConfig: function() {
2217
        this.events = {
2218
            mouseDown: true,
2219
            b4MouseDown: true,
2220
            mouseUp: true,
2221
            b4StartDrag: true,
2222
            startDrag: true,
2223
            b4EndDrag: true,
2224
            endDrag: true,
2225
            drag: true,
2226
            b4Drag: true,
2227
            invalidDrop: true,
2228
            b4DragOut: true,
2229
            dragOut: true,
2230
            dragEnter: true,
2231
            b4DragOver: true,
2232
            dragOver: true,
2233
            b4DragDrop: true,
2234
            dragDrop: true
2235
        };
2236
        
2237
        if (this.config.events) {
2238
            for (var i in this.config.events) {
2239
                if (this.config.events[i] === false) {
2240
                    this.events[i] = false;
2241
                }
2242
            }
2243
        }
2244

    
2245

    
2246
        // configurable properties: 
2247
        //    padding, isTarget, maintainOffset, primaryButtonOnly
2248
        this.padding           = this.config.padding || [0, 0, 0, 0];
2249
        this.isTarget          = (this.config.isTarget !== false);
2250
        this.maintainOffset    = (this.config.maintainOffset);
2251
        this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
2252
        this.dragOnly = ((this.config.dragOnly === true) ? true : false);
2253
        this.useShim = ((this.config.useShim === true) ? true : false);
2254
    },
2255

    
2256
    /**
2257
     * Executed when the linked element is available
2258
    * @method handleOnAvailable
2259
    * @private
2260
     */
2261
    handleOnAvailable: function() {
2262
        //this.logger.log("handleOnAvailable");
2263
        this.available = true;
2264
        this.resetConstraints();
2265
        this.onAvailable();
2266
    },
2267

    
2268
     /**
2269
     * Configures the padding for the target zone in px.  Effectively expands
2270
     * (or reduces) the virtual object size for targeting calculations.  
2271
     * Supports css-style shorthand; if only one parameter is passed, all sides
2272
     * will have that padding, and if only two are passed, the top and bottom
2273
     * will have the first param, the left and right the second.
2274
    * @method setPadding
2275
    * @param {int} iTop    Top pad
2276
    * @param {int} iRight  Right pad
2277
    * @param {int} iBot    Bot pad
2278
    * @param {int} iLeft   Left pad
2279
     */
2280
    setPadding: function(iTop, iRight, iBot, iLeft) {
2281
        // this.padding = [iLeft, iRight, iTop, iBot];
2282
        if (!iRight && 0 !== iRight) {
2283
            this.padding = [iTop, iTop, iTop, iTop];
2284
        } else if (!iBot && 0 !== iBot) {
2285
            this.padding = [iTop, iRight, iTop, iRight];
2286
        } else {
2287
            this.padding = [iTop, iRight, iBot, iLeft];
2288
        }
2289
    },
2290

    
2291
    /**
2292
     * Stores the initial placement of the linked element.
2293
    * @method setInitialPosition
2294
    * @param {int} diffX   the X offset, default 0
2295
    * @param {int} diffY   the Y offset, default 0
2296
    * @private
2297
     */
2298
    setInitPosition: function(diffX, diffY) {
2299
        var el = this.getEl();
2300

    
2301
        if (!this.DDM.verifyEl(el)) {
2302
            if (el && el.style && (el.style.display == 'none')) {
2303
                this.logger.log(this.id + " can not get initial position, element style is display: none");
2304
            } else {
2305
                this.logger.log(this.id + " element is broken");
2306
            }
2307
            return;
2308
        }
2309

    
2310
        var dx = diffX || 0;
2311
        var dy = diffY || 0;
2312

    
2313
        var p = Dom.getXY( el );
2314

    
2315
        this.initPageX = p[0] - dx;
2316
        this.initPageY = p[1] - dy;
2317

    
2318
        this.lastPageX = p[0];
2319
        this.lastPageY = p[1];
2320

    
2321
        this.logger.log(this.id + " initial position: " + this.initPageX + 
2322
                ", " + this.initPageY);
2323

    
2324

    
2325
        this.setStartPosition(p);
2326
    },
2327

    
2328
    /**
2329
     * Sets the start position of the element.  This is set when the obj
2330
     * is initialized, the reset when a drag is started.
2331
    * @method setStartPosition
2332
    * @param pos current position (from previous lookup)
2333
    * @private
2334
     */
2335
    setStartPosition: function(pos) {
2336
        var p = pos || Dom.getXY(this.getEl());
2337

    
2338
        this.deltaSetXY = null;
2339

    
2340
        this.startPageX = p[0];
2341
        this.startPageY = p[1];
2342
    },
2343

    
2344
    /**
2345
     * Add this instance to a group of related drag/drop objects.  All 
2346
     * instances belong to at least one group, and can belong to as many 
2347
     * groups as needed.
2348
    * @method addToGroup
2349
    * @param sGroup {string} the name of the group
2350
     */
2351
    addToGroup: function(sGroup) {
2352
        this.groups[sGroup] = true;
2353
        this.DDM.regDragDrop(this, sGroup);
2354
    },
2355

    
2356
    /**
2357
     * Remove's this instance from the supplied interaction group
2358
    * @method removeFromGroup
2359
    * @param {string}  sGroup  The group to drop
2360
     */
2361
    removeFromGroup: function(sGroup) {
2362
        this.logger.log("Removing from group: " + sGroup);
2363
        if (this.groups[sGroup]) {
2364
            delete this.groups[sGroup];
2365
        }
2366

    
2367
        this.DDM.removeDDFromGroup(this, sGroup);
2368
    },
2369

    
2370
    /**
2371
     * Allows you to specify that an element other than the linked element 
2372
     * will be moved with the cursor during a drag
2373
    * @method setDragElId
2374
    * @param id {string} the id of the element that will be used to initiate the drag
2375
     */
2376
    setDragElId: function(id) {
2377
        this.dragElId = id;
2378
    },
2379

    
2380
    /**
2381
     * Allows you to specify a child of the linked element that should be 
2382
     * used to initiate the drag operation.  An example of this would be if 
2383
     * you have a content div with text and links.  Clicking anywhere in the 
2384
     * content area would normally start the drag operation.  Use this method
2385
     * to specify that an element inside of the content div is the element 
2386
     * that starts the drag operation.
2387
    * @method setHandleElId
2388
    * @param id {string} the id of the element that will be used to 
2389
     * initiate the drag.
2390
     */
2391
    setHandleElId: function(id) {
2392
        if (typeof id !== "string") {
2393
            YAHOO.log("id is not a string, assuming it is an HTMLElement");
2394
            id = Dom.generateId(id);
2395
        }
2396
        this.handleElId = id;
2397
        this.DDM.regHandle(this.id, id);
2398
    },
2399

    
2400
    /**
2401
     * Allows you to set an element outside of the linked element as a drag 
2402
     * handle
2403
    * @method setOuterHandleElId
2404
    * @param id the id of the element that will be used to initiate the drag
2405
     */
2406
    setOuterHandleElId: function(id) {
2407
        if (typeof id !== "string") {
2408
            YAHOO.log("id is not a string, assuming it is an HTMLElement");
2409
            id = Dom.generateId(id);
2410
        }
2411
        this.logger.log("Adding outer handle event: " + id);
2412
        Event.on(id, "mousedown", 
2413
                this.handleMouseDown, this, true);
2414
        this.setHandleElId(id);
2415

    
2416
        this.hasOuterHandles = true;
2417
    },
2418

    
2419
    /**
2420
     * Remove all drag and drop hooks for this element
2421
    * @method unreg
2422
     */
2423
    unreg: function() {
2424
        this.logger.log("DragDrop obj cleanup " + this.id);
2425
        Event.removeListener(this.id, "mousedown", 
2426
                this.handleMouseDown);
2427
        this._domRef = null;
2428
        this.DDM._remove(this);
2429
    },
2430

    
2431
    /**
2432
     * Returns true if this instance is locked, or the drag drop mgr is locked
2433
     * (meaning that all drag/drop is disabled on the page.)
2434
    * @method isLocked
2435
    * @return {boolean} true if this obj or all drag/drop is locked, else 
2436
     * false
2437
     */
2438
    isLocked: function() {
2439
        return (this.DDM.isLocked() || this.locked);
2440
    },
2441

    
2442
    /**
2443
     * Fired when this object is clicked
2444
    * @method handleMouseDown
2445
    * @param {Event} e 
2446
    * @param {YAHOO.util.DragDrop} oDD the clicked dd object (this dd obj)
2447
    * @private
2448
     */
2449
    handleMouseDown: function(e, oDD) {
2450

    
2451
        var button = e.which || e.button;
2452
        this.logger.log("button: " + button);
2453

    
2454
        if (this.primaryButtonOnly && button > 1) {
2455
            this.logger.log("Mousedown was not produced by the primary button");
2456
            return;
2457
        }
2458

    
2459
        if (this.isLocked()) {
2460
            this.logger.log("Drag and drop is disabled, aborting");
2461
            return;
2462
        }
2463

    
2464
        this.logger.log("mousedown " + this.id);
2465

    
2466
        this.logger.log("firing onMouseDown events");
2467

    
2468
        // firing the mousedown events prior to calculating positions
2469
        var b4Return = this.b4MouseDown(e),
2470
        b4Return2 = true;
2471

    
2472
        if (this.events.b4MouseDown) {
2473
            b4Return2 = this.fireEvent('b4MouseDownEvent', e);
2474
        }
2475
        var mDownReturn = this.onMouseDown(e),
2476
            mDownReturn2 = true;
2477
        if (this.events.mouseDown) {
2478
            mDownReturn2 = this.fireEvent('mouseDownEvent', e);
2479
        }
2480

    
2481
        if ((b4Return === false) || (mDownReturn === false) || (b4Return2 === false) || (mDownReturn2 === false)) {
2482
            this.logger.log('b4MouseDown or onMouseDown returned false, exiting drag');
2483
            return;
2484
        }
2485

    
2486
        this.DDM.refreshCache(this.groups);
2487
        // var self = this;
2488
        // setTimeout( function() { self.DDM.refreshCache(self.groups); }, 0);
2489

    
2490
        // Only process the event if we really clicked within the linked 
2491
        // element.  The reason we make this check is that in the case that 
2492
        // another element was moved between the clicked element and the 
2493
        // cursor in the time between the mousedown and mouseup events. When 
2494
        // this happens, the element gets the next mousedown event 
2495
        // regardless of where on the screen it happened.  
2496
        var pt = new YAHOO.util.Point(Event.getPageX(e), Event.getPageY(e));
2497
        if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
2498
                this.logger.log("Click was not over the element: " + this.id);
2499
        } else {
2500
            if (this.clickValidator(e)) {
2501

    
2502
                this.logger.log("click was a valid handle");
2503

    
2504
                // set the initial element position
2505
                this.setStartPosition();
2506

    
2507
                // start tracking mousemove distance and mousedown time to
2508
                // determine when to start the actual drag
2509
                this.DDM.handleMouseDown(e, this);
2510

    
2511
                // this mousedown is mine
2512
                this.DDM.stopEvent(e);
2513
            } else {
2514

    
2515
this.logger.log("clickValidator returned false, drag not initiated");
2516

    
2517
            }
2518
        }
2519
    },
2520

    
2521
    /**
2522
    * @method clickValidator
2523
    * @description Method validates that the clicked element
2524
     * was indeed the handle or a valid child of the handle
2525
    * @param {Event} e 
2526
     */
2527
    clickValidator: function(e) {
2528
        var target = YAHOO.util.Event.getTarget(e);
2529
        return ( this.isValidHandleChild(target) &&
2530
                    (this.id == this.handleElId || 
2531
                        this.DDM.handleWasClicked(target, this.id)) );
2532
    },
2533

    
2534
    /**
2535
     * Finds the location the element should be placed if we want to move
2536
     * it to where the mouse location less the click offset would place us.
2537
    * @method getTargetCoord
2538
    * @param {int} iPageX the X coordinate of the click
2539
    * @param {int} iPageY the Y coordinate of the click
2540
    * @return an object that contains the coordinates (Object.x and Object.y)
2541
    * @private
2542
     */
2543
    getTargetCoord: function(iPageX, iPageY) {
2544

    
2545
        // this.logger.log("getTargetCoord: " + iPageX + ", " + iPageY);
2546

    
2547
        var x = iPageX - this.deltaX;
2548
        var y = iPageY - this.deltaY;
2549

    
2550
        if (this.constrainX) {
2551
            if (x < this.minX) { x = this.minX; }
2552
            if (x > this.maxX) { x = this.maxX; }
2553
        }
2554

    
2555
        if (this.constrainY) {
2556
            if (y < this.minY) { y = this.minY; }
2557
            if (y > this.maxY) { y = this.maxY; }
2558
        }
2559

    
2560
        x = this.getTick(x, this.xTicks);
2561
        y = this.getTick(y, this.yTicks);
2562

    
2563
        // this.logger.log("getTargetCoord " + 
2564
                // " iPageX: " + iPageX +
2565
                // " iPageY: " + iPageY +
2566
                // " x: " + x + ", y: " + y);
2567

    
2568
        return {x:x, y:y};
2569
    },
2570

    
2571
    /**
2572
     * Allows you to specify a tag name that should not start a drag operation
2573
     * when clicked.  This is designed to facilitate embedding links within a
2574
     * drag handle that do something other than start the drag.
2575
    * @method addInvalidHandleType
2576
    * @param {string} tagName the type of element to exclude
2577
     */
2578
    addInvalidHandleType: function(tagName) {
2579
        var type = tagName.toUpperCase();
2580
        this.invalidHandleTypes[type] = type;
2581
    },
2582

    
2583
    /**
2584
     * Lets you to specify an element id for a child of a drag handle
2585
     * that should not initiate a drag
2586
    * @method addInvalidHandleId
2587
    * @param {string} id the element id of the element you wish to ignore
2588
     */
2589
    addInvalidHandleId: function(id) {
2590
        if (typeof id !== "string") {
2591
            YAHOO.log("id is not a string, assuming it is an HTMLElement");
2592
            id = Dom.generateId(id);
2593
        }
2594
        this.invalidHandleIds[id] = id;
2595
    },
2596

    
2597

    
2598
    /**
2599
     * Lets you specify a css class of elements that will not initiate a drag
2600
    * @method addInvalidHandleClass
2601
    * @param {string} cssClass the class of the elements you wish to ignore
2602
     */
2603
    addInvalidHandleClass: function(cssClass) {
2604
        this.invalidHandleClasses.push(cssClass);
2605
    },
2606

    
2607
    /**
2608
     * Unsets an excluded tag name set by addInvalidHandleType
2609
    * @method removeInvalidHandleType
2610
    * @param {string} tagName the type of element to unexclude
2611
     */
2612
    removeInvalidHandleType: function(tagName) {
2613
        var type = tagName.toUpperCase();
2614
        // this.invalidHandleTypes[type] = null;
2615
        delete this.invalidHandleTypes[type];
2616
    },
2617
    
2618
    /**
2619
     * Unsets an invalid handle id
2620
    * @method removeInvalidHandleId
2621
    * @param {string} id the id of the element to re-enable
2622
     */
2623
    removeInvalidHandleId: function(id) {
2624
        if (typeof id !== "string") {
2625
            YAHOO.log("id is not a string, assuming it is an HTMLElement");
2626
            id = Dom.generateId(id);
2627
        }
2628
        delete this.invalidHandleIds[id];
2629
    },
2630

    
2631
    /**
2632
     * Unsets an invalid css class
2633
    * @method removeInvalidHandleClass
2634
    * @param {string} cssClass the class of the element(s) you wish to 
2635
     * re-enable
2636
     */
2637
    removeInvalidHandleClass: function(cssClass) {
2638
        for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
2639
            if (this.invalidHandleClasses[i] == cssClass) {
2640
                delete this.invalidHandleClasses[i];
2641
            }
2642
        }
2643
    },
2644

    
2645
    /**
2646
     * Checks the tag exclusion list to see if this click should be ignored
2647
    * @method isValidHandleChild
2648
    * @param {HTMLElement} node the HTMLElement to evaluate
2649
    * @return {boolean} true if this is a valid tag type, false if not
2650
     */
2651
    isValidHandleChild: function(node) {
2652

    
2653
        var valid = true;
2654
        // var n = (node.nodeName == "#text") ? node.parentNode : node;
2655
        var nodeName;
2656
        try {
2657
            nodeName = node.nodeName.toUpperCase();
2658
        } catch(e) {
2659
            nodeName = node.nodeName;
2660
        }
2661
        valid = valid && !this.invalidHandleTypes[nodeName];
2662
        valid = valid && !this.invalidHandleIds[node.id];
2663

    
2664
        for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
2665
            valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
2666
        }
2667

    
2668
        this.logger.log("Valid handle? ... " + valid);
2669

    
2670
        return valid;
2671

    
2672
    },
2673

    
2674
    /**
2675
     * Create the array of horizontal tick marks if an interval was specified
2676
     * in setXConstraint().
2677
    * @method setXTicks
2678
    * @private
2679
     */
2680
    setXTicks: function(iStartX, iTickSize) {
2681
        this.xTicks = [];
2682
        this.xTickSize = iTickSize;
2683
        
2684
        var tickMap = {};
2685

    
2686
        for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
2687
            if (!tickMap[i]) {
2688
                this.xTicks[this.xTicks.length] = i;
2689
                tickMap[i] = true;
2690
            }
2691
        }
2692

    
2693
        for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
2694
            if (!tickMap[i]) {
2695
                this.xTicks[this.xTicks.length] = i;
2696
                tickMap[i] = true;
2697
            }
2698
        }
2699

    
2700
        this.xTicks.sort(this.DDM.numericSort) ;
2701
        this.logger.log("xTicks: " + this.xTicks.join());
2702
    },
2703

    
2704
    /**
2705
     * Create the array of vertical tick marks if an interval was specified in 
2706
     * setYConstraint().
2707
    * @method setYTicks
2708
    * @private
2709
     */
2710
    setYTicks: function(iStartY, iTickSize) {
2711
        // this.logger.log("setYTicks: " + iStartY + ", " + iTickSize
2712
               // + ", " + this.initPageY + ", " + this.minY + ", " + this.maxY );
2713
        this.yTicks = [];
2714
        this.yTickSize = iTickSize;
2715

    
2716
        var tickMap = {};
2717

    
2718
        for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
2719
            if (!tickMap[i]) {
2720
                this.yTicks[this.yTicks.length] = i;
2721
                tickMap[i] = true;
2722
            }
2723
        }
2724

    
2725
        for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
2726
            if (!tickMap[i]) {
2727
                this.yTicks[this.yTicks.length] = i;
2728
                tickMap[i] = true;
2729
            }
2730
        }
2731

    
2732
        this.yTicks.sort(this.DDM.numericSort) ;
2733
        this.logger.log("yTicks: " + this.yTicks.join());
2734
    },
2735

    
2736
    /**
2737
     * By default, the element can be dragged any place on the screen.  Use 
2738
     * this method to limit the horizontal travel of the element.  Pass in 
2739
     * 0,0 for the parameters if you want to lock the drag to the y axis.
2740
    * @method setXConstraint
2741
    * @param {int} iLeft the number of pixels the element can move to the left
2742
    * @param {int} iRight the number of pixels the element can move to the 
2743
     * right
2744
    * @param {int} iTickSize optional parameter for specifying that the 
2745
     * element
2746
     * should move iTickSize pixels at a time.
2747
     */
2748
    setXConstraint: function(iLeft, iRight, iTickSize) {
2749
        this.leftConstraint = parseInt(iLeft, 10);
2750
        this.rightConstraint = parseInt(iRight, 10);
2751

    
2752
        this.minX = this.initPageX - this.leftConstraint;
2753
        this.maxX = this.initPageX + this.rightConstraint;
2754
        if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
2755

    
2756
        this.constrainX = true;
2757
        this.logger.log("initPageX:" + this.initPageX + " minX:" + this.minX + 
2758
                " maxX:" + this.maxX);
2759
    },
2760

    
2761
    /**
2762
     * Clears any constraints applied to this instance.  Also clears ticks
2763
     * since they can't exist independent of a constraint at this time.
2764
    * @method clearConstraints
2765
     */
2766
    clearConstraints: function() {
2767
        this.logger.log("Clearing constraints");
2768
        this.constrainX = false;
2769
        this.constrainY = false;
2770
        this.clearTicks();
2771
    },
2772

    
2773
    /**
2774
     * Clears any tick interval defined for this instance
2775
    * @method clearTicks
2776
     */
2777
    clearTicks: function() {
2778
        this.logger.log("Clearing ticks");
2779
        this.xTicks = null;
2780
        this.yTicks = null;
2781
        this.xTickSize = 0;
2782
        this.yTickSize = 0;
2783
    },
2784

    
2785
    /**
2786
     * By default, the element can be dragged any place on the screen.  Set 
2787
     * this to limit the vertical travel of the element.  Pass in 0,0 for the
2788
     * parameters if you want to lock the drag to the x axis.
2789
    * @method setYConstraint
2790
    * @param {int} iUp the number of pixels the element can move up
2791
    * @param {int} iDown the number of pixels the element can move down
2792
    * @param {int} iTickSize optional parameter for specifying that the 
2793
     * element should move iTickSize pixels at a time.
2794
     */
2795
    setYConstraint: function(iUp, iDown, iTickSize) {
2796
        this.logger.log("setYConstraint: " + iUp + "," + iDown + "," + iTickSize);
2797
        this.topConstraint = parseInt(iUp, 10);
2798
        this.bottomConstraint = parseInt(iDown, 10);
2799

    
2800
        this.minY = this.initPageY - this.topConstraint;
2801
        this.maxY = this.initPageY + this.bottomConstraint;
2802
        if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
2803

    
2804
        this.constrainY = true;
2805
        
2806
        this.logger.log("initPageY:" + this.initPageY + " minY:" + this.minY + 
2807
                " maxY:" + this.maxY);
2808
    },
2809

    
2810
    /**
2811
     * resetConstraints must be called if you manually reposition a dd element.
2812
    * @method resetConstraints
2813
     */
2814
    resetConstraints: function() {
2815

    
2816
        //this.logger.log("resetConstraints");
2817

    
2818
        // Maintain offsets if necessary
2819
        if (this.initPageX || this.initPageX === 0) {
2820
            //this.logger.log("init pagexy: " + this.initPageX + ", " + 
2821
                               //this.initPageY);
2822
            //this.logger.log("last pagexy: " + this.lastPageX + ", " + 
2823
                               //this.lastPageY);
2824
            // figure out how much this thing has moved
2825
            var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
2826
            var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
2827

    
2828
            this.setInitPosition(dx, dy);
2829

    
2830
        // This is the first time we have detected the element's position
2831
        } else {
2832
            this.setInitPosition();
2833
        }
2834

    
2835
        if (this.constrainX) {
2836
            this.setXConstraint( this.leftConstraint, 
2837
                                 this.rightConstraint, 
2838
                                 this.xTickSize        );
2839
        }
2840

    
2841
        if (this.constrainY) {
2842
            this.setYConstraint( this.topConstraint, 
2843
                                 this.bottomConstraint, 
2844
                                 this.yTickSize         );
2845
        }
2846
    },
2847

    
2848
    /**
2849
     * Normally the drag element is moved pixel by pixel, but we can specify 
2850
     * that it move a number of pixels at a time.  This method resolves the 
2851
     * location when we have it set up like this.
2852
    * @method getTick
2853
    * @param {int} val where we want to place the object
2854
    * @param {int[]} tickArray sorted array of valid points
2855
    * @return {int} the closest tick
2856
    * @private
2857
     */
2858
    getTick: function(val, tickArray) {
2859

    
2860
        if (!tickArray) {
2861
            // If tick interval is not defined, it is effectively 1 pixel, 
2862
            // so we return the value passed to us.
2863
            return val; 
2864
        } else if (tickArray[0] >= val) {
2865
            // The value is lower than the first tick, so we return the first
2866
            // tick.
2867
            return tickArray[0];
2868
        } else {
2869
            for (var i=0, len=tickArray.length; i<len; ++i) {
2870
                var next = i + 1;
2871
                if (tickArray[next] && tickArray[next] >= val) {
2872
                    var diff1 = val - tickArray[i];
2873
                    var diff2 = tickArray[next] - val;
2874
                    return (diff2 > diff1) ? tickArray[i] : tickArray[next];
2875
                }
2876
            }
2877

    
2878
            // The value is larger than the last tick, so we return the last
2879
            // tick.
2880
            return tickArray[tickArray.length - 1];
2881
        }
2882
    },
2883

    
2884
    /**
2885
     * toString method
2886
    * @method toString
2887
    * @return {string} string representation of the dd obj
2888
     */
2889
    toString: function() {
2890
        return ("DragDrop " + this.id);
2891
    }
2892

    
2893
};
2894
YAHOO.augment(YAHOO.util.DragDrop, YAHOO.util.EventProvider);
2895

    
2896
/**
2897
* @event mouseDownEvent
2898
* @description Provides access to the mousedown event. The mousedown does not always result in a drag operation.
2899
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2900
*/
2901

    
2902
/**
2903
* @event b4MouseDownEvent
2904
* @description Provides access to the mousedown event, before the mouseDownEvent gets fired. Returning false will cancel the drag.
2905
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2906
*/
2907

    
2908
/**
2909
* @event mouseUpEvent
2910
* @description Fired from inside DragDropMgr when the drag operation is finished.
2911
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2912
*/
2913

    
2914
/**
2915
* @event b4StartDragEvent
2916
* @description Fires before the startDragEvent, returning false will cancel the startDrag Event.
2917
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2918
*/
2919

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

    
2926
/**
2927
* @event b4EndDragEvent
2928
* @description Fires before the endDragEvent. Returning false will cancel.
2929
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2930
*/
2931

    
2932
/**
2933
* @event endDragEvent
2934
* @description Fires on the mouseup event after a drag has been initiated (startDrag fired).
2935
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2936
*/
2937

    
2938
/**
2939
* @event dragEvent
2940
* @description Occurs every mousemove event while dragging.
2941
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2942
*/
2943
/**
2944
* @event b4DragEvent
2945
* @description Fires before the dragEvent.
2946
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2947
*/
2948
/**
2949
* @event invalidDropEvent
2950
* @description Fires when the dragged objects is dropped in a location that contains no drop targets.
2951
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2952
*/
2953
/**
2954
* @event b4DragOutEvent
2955
* @description Fires before the dragOutEvent
2956
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2957
*/
2958
/**
2959
* @event dragOutEvent
2960
* @description Fires when a dragged object is no longer over an object that had the onDragEnter fire. 
2961
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2962
*/
2963
/**
2964
* @event dragEnterEvent
2965
* @description Occurs when the dragged object first interacts with another targettable drag and drop object.
2966
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2967
*/
2968
/**
2969
* @event b4DragOverEvent
2970
* @description Fires before the dragOverEvent.
2971
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2972
*/
2973
/**
2974
* @event dragOverEvent
2975
* @description Fires every mousemove event while over a drag and drop object.
2976
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2977
*/
2978
/**
2979
* @event b4DragDropEvent 
2980
* @description Fires before the dragDropEvent
2981
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2982
*/
2983
/**
2984
* @event dragDropEvent
2985
* @description Fires when the dragged objects is dropped on another.
2986
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2987
*/
2988
})();
2989
/**
2990
 * A DragDrop implementation where the linked element follows the 
2991
 * mouse cursor during a drag.
2992
 * @class DD
2993
 * @extends YAHOO.util.DragDrop
2994
 * @constructor
2995
 * @param {String} id the id of the linked element 
2996
 * @param {String} sGroup the group of related DragDrop items
2997
 * @param {object} config an object containing configurable attributes
2998
 *                Valid properties for DD: 
2999
 *                    scroll
3000
 */
3001
YAHOO.util.DD = function(id, sGroup, config) {
3002
    if (id) {
3003
        this.init(id, sGroup, config);
3004
    }
3005
};
3006

    
3007
YAHOO.extend(YAHOO.util.DD, YAHOO.util.DragDrop, {
3008

    
3009
    /**
3010
     * When set to true, the utility automatically tries to scroll the browser
3011
     * window when a drag and drop element is dragged near the viewport boundary.
3012
     * Defaults to true.
3013
    * @property scroll
3014
    * @type boolean
3015
     */
3016
    scroll: true, 
3017

    
3018
    /**
3019
     * Sets the pointer offset to the distance between the linked element's top 
3020
     * left corner and the location the element was clicked
3021
    * @method autoOffset
3022
    * @param {int} iPageX the X coordinate of the click
3023
    * @param {int} iPageY the Y coordinate of the click
3024
     */
3025
    autoOffset: function(iPageX, iPageY) {
3026
        var x = iPageX - this.startPageX;
3027
        var y = iPageY - this.startPageY;
3028
        this.setDelta(x, y);
3029
        // this.logger.log("autoOffset el pos: " + aCoord + ", delta: " + x + "," + y);
3030
    },
3031

    
3032
    /** 
3033
     * Sets the pointer offset.  You can call this directly to force the 
3034
     * offset to be in a particular location (e.g., pass in 0,0 to set it 
3035
     * to the center of the object, as done in YAHOO.widget.Slider)
3036
    * @method setDelta
3037
    * @param {int} iDeltaX the distance from the left
3038
    * @param {int} iDeltaY the distance from the top
3039
     */
3040
    setDelta: function(iDeltaX, iDeltaY) {
3041
        this.deltaX = iDeltaX;
3042
        this.deltaY = iDeltaY;
3043
        this.logger.log("deltaX:" + this.deltaX + ", deltaY:" + this.deltaY);
3044
    },
3045

    
3046
    /**
3047
     * Sets the drag element to the location of the mousedown or click event, 
3048
     * maintaining the cursor location relative to the location on the element 
3049
     * that was clicked.  Override this if you want to place the element in a 
3050
     * location other than where the cursor is.
3051
    * @method setDragElPos
3052
    * @param {int} iPageX the X coordinate of the mousedown or drag event
3053
    * @param {int} iPageY the Y coordinate of the mousedown or drag event
3054
     */
3055
    setDragElPos: function(iPageX, iPageY) {
3056
        // the first time we do this, we are going to check to make sure
3057
        // the element has css positioning
3058

    
3059
        var el = this.getDragEl();
3060
        this.alignElWithMouse(el, iPageX, iPageY);
3061
    },
3062

    
3063
    /**
3064
     * Sets the element to the location of the mousedown or click event, 
3065
     * maintaining the cursor location relative to the location on the element 
3066
     * that was clicked.  Override this if you want to place the element in a 
3067
     * location other than where the cursor is.
3068
    * @method alignElWithMouse
3069
    * @param {HTMLElement} el the element to move
3070
    * @param {int} iPageX the X coordinate of the mousedown or drag event
3071
    * @param {int} iPageY the Y coordinate of the mousedown or drag event
3072
     */
3073
    alignElWithMouse: function(el, iPageX, iPageY) {
3074
        var oCoord = this.getTargetCoord(iPageX, iPageY);
3075
        // this.logger.log("****alignElWithMouse : " + el.id + ", " + aCoord + ", " + el.style.display);
3076

    
3077
        if (!this.deltaSetXY) {
3078
            var aCoord = [oCoord.x, oCoord.y];
3079
            YAHOO.util.Dom.setXY(el, aCoord);
3080

    
3081
            var newLeft = parseInt( YAHOO.util.Dom.getStyle(el, "left"), 10 );
3082
            var newTop  = parseInt( YAHOO.util.Dom.getStyle(el, "top" ), 10 );
3083

    
3084
            this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
3085
        } else {
3086
            YAHOO.util.Dom.setStyle(el, "left", (oCoord.x + this.deltaSetXY[0]) + "px");
3087
            YAHOO.util.Dom.setStyle(el, "top",  (oCoord.y + this.deltaSetXY[1]) + "px");
3088
        }
3089
        
3090
        this.cachePosition(oCoord.x, oCoord.y);
3091
        var self = this;
3092
        setTimeout(function() {
3093
            self.autoScroll.call(self, oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
3094
        }, 0);
3095
    },
3096

    
3097
    /**
3098
     * Saves the most recent position so that we can reset the constraints and
3099
     * tick marks on-demand.  We need to know this so that we can calculate the
3100
     * number of pixels the element is offset from its original position.
3101
    * @method cachePosition
3102
    * @param iPageX the current x position (optional, this just makes it so we
3103
     * don't have to look it up again)
3104
    * @param iPageY the current y position (optional, this just makes it so we
3105
     * don't have to look it up again)
3106
     */
3107
    cachePosition: function(iPageX, iPageY) {
3108
        if (iPageX) {
3109
            this.lastPageX = iPageX;
3110
            this.lastPageY = iPageY;
3111
        } else {
3112
            var aCoord = YAHOO.util.Dom.getXY(this.getEl());
3113
            this.lastPageX = aCoord[0];
3114
            this.lastPageY = aCoord[1];
3115
        }
3116
    },
3117

    
3118
    /**
3119
     * Auto-scroll the window if the dragged object has been moved beyond the 
3120
     * visible window boundary.
3121
    * @method autoScroll
3122
    * @param {int} x the drag element's x position
3123
    * @param {int} y the drag element's y position
3124
    * @param {int} h the height of the drag element
3125
    * @param {int} w the width of the drag element
3126
    * @private
3127
     */
3128
    autoScroll: function(x, y, h, w) {
3129

    
3130
        if (this.scroll) {
3131
            // The client height
3132
            var clientH = this.DDM.getClientHeight();
3133

    
3134
            // The client width
3135
            var clientW = this.DDM.getClientWidth();
3136

    
3137
            // The amt scrolled down
3138
            var st = this.DDM.getScrollTop();
3139

    
3140
            // The amt scrolled right
3141
            var sl = this.DDM.getScrollLeft();
3142

    
3143
            // Location of the bottom of the element
3144
            var bot = h + y;
3145

    
3146
            // Location of the right of the element
3147
            var right = w + x;
3148

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

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

    
3157
            // this.logger.log( " x: " + x + " y: " + y + " h: " + h + 
3158
            // " clientH: " + clientH + " clientW: " + clientW + 
3159
            // " st: " + st + " sl: " + sl + " bot: " + bot + 
3160
            // " right: " + right + " toBot: " + toBot + " toRight: " + toRight);
3161

    
3162
            // How close to the edge the cursor must be before we scroll
3163
            // var thresh = (document.all) ? 100 : 40;
3164
            var thresh = 40;
3165

    
3166
            // How many pixels to scroll per autoscroll op.  This helps to reduce 
3167
            // clunky scrolling. IE is more sensitive about this ... it needs this 
3168
            // value to be higher.
3169
            var scrAmt = (document.all) ? 80 : 30;
3170

    
3171
            // Scroll down if we are near the bottom of the visible page and the 
3172
            // obj extends below the crease
3173
            if ( bot > clientH && toBot < thresh ) { 
3174
                window.scrollTo(sl, st + scrAmt); 
3175
            }
3176

    
3177
            // Scroll up if the window is scrolled down and the top of the object
3178
            // goes above the top border
3179
            if ( y < st && st > 0 && y - st < thresh ) { 
3180
                window.scrollTo(sl, st - scrAmt); 
3181
            }
3182

    
3183
            // Scroll right if the obj is beyond the right border and the cursor is
3184
            // near the border.
3185
            if ( right > clientW && toRight < thresh ) { 
3186
                window.scrollTo(sl + scrAmt, st); 
3187
            }
3188

    
3189
            // Scroll left if the window has been scrolled to the right and the obj
3190
            // extends past the left border
3191
            if ( x < sl && sl > 0 && x - sl < thresh ) { 
3192
                window.scrollTo(sl - scrAmt, st);
3193
            }
3194
        }
3195
    },
3196

    
3197
    /*
3198
     * Sets up config options specific to this class. Overrides
3199
     * YAHOO.util.DragDrop, but all versions of this method through the 
3200
     * inheritance chain are called
3201
     */
3202
    applyConfig: function() {
3203
        YAHOO.util.DD.superclass.applyConfig.call(this);
3204
        this.scroll = (this.config.scroll !== false);
3205
    },
3206

    
3207
    /*
3208
     * Event that fires prior to the onMouseDown event.  Overrides 
3209
     * YAHOO.util.DragDrop.
3210
     */
3211
    b4MouseDown: function(e) {
3212
        this.setStartPosition();
3213
        // this.resetConstraints();
3214
        this.autoOffset(YAHOO.util.Event.getPageX(e), 
3215
                            YAHOO.util.Event.getPageY(e));
3216
    },
3217

    
3218
    /*
3219
     * Event that fires prior to the onDrag event.  Overrides 
3220
     * YAHOO.util.DragDrop.
3221
     */
3222
    b4Drag: function(e) {
3223
        this.setDragElPos(YAHOO.util.Event.getPageX(e), 
3224
                            YAHOO.util.Event.getPageY(e));
3225
    },
3226

    
3227
    toString: function() {
3228
        return ("DD " + this.id);
3229
    }
3230

    
3231
    //////////////////////////////////////////////////////////////////////////
3232
    // Debugging ygDragDrop events that can be overridden
3233
    //////////////////////////////////////////////////////////////////////////
3234
    /*
3235
    startDrag: function(x, y) {
3236
        this.logger.log(this.id.toString()  + " startDrag");
3237
    },
3238

    
3239
    onDrag: function(e) {
3240
        this.logger.log(this.id.toString() + " onDrag");
3241
    },
3242

    
3243
    onDragEnter: function(e, id) {
3244
        this.logger.log(this.id.toString() + " onDragEnter: " + id);
3245
    },
3246

    
3247
    onDragOver: function(e, id) {
3248
        this.logger.log(this.id.toString() + " onDragOver: " + id);
3249
    },
3250

    
3251
    onDragOut: function(e, id) {
3252
        this.logger.log(this.id.toString() + " onDragOut: " + id);
3253
    },
3254

    
3255
    onDragDrop: function(e, id) {
3256
        this.logger.log(this.id.toString() + " onDragDrop: " + id);
3257
    },
3258

    
3259
    endDrag: function(e) {
3260
        this.logger.log(this.id.toString() + " endDrag");
3261
    }
3262

    
3263
    */
3264

    
3265
/**
3266
* @event mouseDownEvent
3267
* @description Provides access to the mousedown event. The mousedown does not always result in a drag operation.
3268
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3269
*/
3270

    
3271
/**
3272
* @event b4MouseDownEvent
3273
* @description Provides access to the mousedown event, before the mouseDownEvent gets fired. Returning false will cancel the drag.
3274
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3275
*/
3276

    
3277
/**
3278
* @event mouseUpEvent
3279
* @description Fired from inside DragDropMgr when the drag operation is finished.
3280
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3281
*/
3282

    
3283
/**
3284
* @event b4StartDragEvent
3285
* @description Fires before the startDragEvent, returning false will cancel the startDrag Event.
3286
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3287
*/
3288

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

    
3295
/**
3296
* @event b4EndDragEvent
3297
* @description Fires before the endDragEvent. Returning false will cancel.
3298
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3299
*/
3300

    
3301
/**
3302
* @event endDragEvent
3303
* @description Fires on the mouseup event after a drag has been initiated (startDrag fired).
3304
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3305
*/
3306

    
3307
/**
3308
* @event dragEvent
3309
* @description Occurs every mousemove event while dragging.
3310
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3311
*/
3312
/**
3313
* @event b4DragEvent
3314
* @description Fires before the dragEvent.
3315
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3316
*/
3317
/**
3318
* @event invalidDropEvent
3319
* @description Fires when the dragged objects is dropped in a location that contains no drop targets.
3320
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3321
*/
3322
/**
3323
* @event b4DragOutEvent
3324
* @description Fires before the dragOutEvent
3325
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3326
*/
3327
/**
3328
* @event dragOutEvent
3329
* @description Fires when a dragged object is no longer over an object that had the onDragEnter fire. 
3330
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3331
*/
3332
/**
3333
* @event dragEnterEvent
3334
* @description Occurs when the dragged object first interacts with another targettable drag and drop object.
3335
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3336
*/
3337
/**
3338
* @event b4DragOverEvent
3339
* @description Fires before the dragOverEvent.
3340
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3341
*/
3342
/**
3343
* @event dragOverEvent
3344
* @description Fires every mousemove event while over a drag and drop object.
3345
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3346
*/
3347
/**
3348
* @event b4DragDropEvent 
3349
* @description Fires before the dragDropEvent
3350
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3351
*/
3352
/**
3353
* @event dragDropEvent
3354
* @description Fires when the dragged objects is dropped on another.
3355
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3356
*/
3357
});
3358
/**
3359
 * A DragDrop implementation that inserts an empty, bordered div into
3360
 * the document that follows the cursor during drag operations.  At the time of
3361
 * the click, the frame div is resized to the dimensions of the linked html
3362
 * element, and moved to the exact location of the linked element.
3363
 *
3364
 * References to the "frame" element refer to the single proxy element that
3365
 * was created to be dragged in place of all DDProxy elements on the
3366
 * page.
3367
 *
3368
 * @class DDProxy
3369
 * @extends YAHOO.util.DD
3370
 * @constructor
3371
 * @param {String} id the id of the linked html element
3372
 * @param {String} sGroup the group of related DragDrop objects
3373
 * @param {object} config an object containing configurable attributes
3374
 *                Valid properties for DDProxy in addition to those in DragDrop: 
3375
 *                   resizeFrame, centerFrame, dragElId
3376
 */
3377
YAHOO.util.DDProxy = function(id, sGroup, config) {
3378
    if (id) {
3379
        this.init(id, sGroup, config);
3380
        this.initFrame(); 
3381
    }
3382
};
3383

    
3384
/**
3385
 * The default drag frame div id
3386
 * @property YAHOO.util.DDProxy.dragElId
3387
 * @type String
3388
 * @static
3389
 */
3390
YAHOO.util.DDProxy.dragElId = "ygddfdiv";
3391

    
3392
YAHOO.extend(YAHOO.util.DDProxy, YAHOO.util.DD, {
3393

    
3394
    /**
3395
     * By default we resize the drag frame to be the same size as the element
3396
     * we want to drag (this is to get the frame effect).  We can turn it off
3397
     * if we want a different behavior.
3398
    * @property resizeFrame
3399
    * @type boolean
3400
     */
3401
    resizeFrame: true,
3402

    
3403
    /**
3404
     * By default the frame is positioned exactly where the drag element is, so
3405
     * we use the cursor offset provided by YAHOO.util.DD.  Another option that works only if
3406
     * you do not have constraints on the obj is to have the drag frame centered
3407
     * around the cursor.  Set centerFrame to true for this effect.
3408
    * @property centerFrame
3409
    * @type boolean
3410
     */
3411
    centerFrame: false,
3412

    
3413
    /**
3414
     * Creates the proxy element if it does not yet exist
3415
    * @method createFrame
3416
     */
3417
    createFrame: function() {
3418
        var self=this, body=document.body;
3419

    
3420
        if (!body || !body.firstChild) {
3421
            setTimeout( function() { self.createFrame(); }, 50 );
3422
            return;
3423
        }
3424

    
3425
        var div=this.getDragEl(), Dom=YAHOO.util.Dom;
3426

    
3427
        if (!div) {
3428
            div    = document.createElement("div");
3429
            div.id = this.dragElId;
3430
            var s  = div.style;
3431

    
3432
            s.position   = "absolute";
3433
            s.visibility = "hidden";
3434
            s.cursor     = "move";
3435
            s.border     = "2px solid #aaa";
3436
            s.zIndex     = 999;
3437
            s.height     = "25px";
3438
            s.width      = "25px";
3439

    
3440
            var _data = document.createElement('div');
3441
            Dom.setStyle(_data, 'height', '100%');
3442
            Dom.setStyle(_data, 'width', '100%');
3443
            /**
3444
            * If the proxy element has no background-color, then it is considered to the "transparent" by Internet Explorer.
3445
            * Since it is "transparent" then the events pass through it to the iframe below.
3446
            * So creating a "fake" div inside the proxy element and giving it a background-color, then setting it to an
3447
            * opacity of 0, it appears to not be there, however IE still thinks that it is so the events never pass through.
3448
            */
3449
            Dom.setStyle(_data, 'background-color', '#ccc');
3450
            Dom.setStyle(_data, 'opacity', '0');
3451
            div.appendChild(_data);
3452

    
3453
            // appendChild can blow up IE if invoked prior to the window load event
3454
            // while rendering a table.  It is possible there are other scenarios 
3455
            // that would cause this to happen as well.
3456
            body.insertBefore(div, body.firstChild);
3457
        }
3458
    },
3459

    
3460
    /**
3461
     * Initialization for the drag frame element.  Must be called in the
3462
     * constructor of all subclasses
3463
    * @method initFrame
3464
     */
3465
    initFrame: function() {
3466
        this.createFrame();
3467
    },
3468

    
3469
    applyConfig: function() {
3470
        //this.logger.log("DDProxy applyConfig");
3471
        YAHOO.util.DDProxy.superclass.applyConfig.call(this);
3472

    
3473
        this.resizeFrame = (this.config.resizeFrame !== false);
3474
        this.centerFrame = (this.config.centerFrame);
3475
        this.setDragElId(this.config.dragElId || YAHOO.util.DDProxy.dragElId);
3476
    },
3477

    
3478
    /**
3479
     * Resizes the drag frame to the dimensions of the clicked object, positions 
3480
     * it over the object, and finally displays it
3481
    * @method showFrame
3482
    * @param {int} iPageX X click position
3483
    * @param {int} iPageY Y click position
3484
    * @private
3485
     */
3486
    showFrame: function(iPageX, iPageY) {
3487
        var el = this.getEl();
3488
        var dragEl = this.getDragEl();
3489
        var s = dragEl.style;
3490

    
3491
        this._resizeProxy();
3492

    
3493
        if (this.centerFrame) {
3494
            this.setDelta( Math.round(parseInt(s.width,  10)/2), 
3495
                           Math.round(parseInt(s.height, 10)/2) );
3496
        }
3497

    
3498
        this.setDragElPos(iPageX, iPageY);
3499

    
3500
        YAHOO.util.Dom.setStyle(dragEl, "visibility", "visible"); 
3501
    },
3502

    
3503
    /**
3504
     * The proxy is automatically resized to the dimensions of the linked
3505
     * element when a drag is initiated, unless resizeFrame is set to false
3506
    * @method _resizeProxy
3507
    * @private
3508
     */
3509
    _resizeProxy: function() {
3510
        if (this.resizeFrame) {
3511
            var DOM    = YAHOO.util.Dom;
3512
            var el     = this.getEl();
3513
            var dragEl = this.getDragEl();
3514

    
3515
            var bt = parseInt( DOM.getStyle(dragEl, "borderTopWidth"    ), 10);
3516
            var br = parseInt( DOM.getStyle(dragEl, "borderRightWidth"  ), 10);
3517
            var bb = parseInt( DOM.getStyle(dragEl, "borderBottomWidth" ), 10);
3518
            var bl = parseInt( DOM.getStyle(dragEl, "borderLeftWidth"   ), 10);
3519

    
3520
            if (isNaN(bt)) { bt = 0; }
3521
            if (isNaN(br)) { br = 0; }
3522
            if (isNaN(bb)) { bb = 0; }
3523
            if (isNaN(bl)) { bl = 0; }
3524

    
3525
            this.logger.log("proxy size: " + bt + "  " + br + " " + bb + " " + bl);
3526

    
3527
            var newWidth  = Math.max(0, el.offsetWidth  - br - bl);                                                                                           
3528
            var newHeight = Math.max(0, el.offsetHeight - bt - bb);
3529

    
3530
            this.logger.log("Resizing proxy element");
3531

    
3532
            DOM.setStyle( dragEl, "width",  newWidth  + "px" );
3533
            DOM.setStyle( dragEl, "height", newHeight + "px" );
3534
        }
3535
    },
3536

    
3537
    // overrides YAHOO.util.DragDrop
3538
    b4MouseDown: function(e) {
3539
        this.setStartPosition();
3540
        var x = YAHOO.util.Event.getPageX(e);
3541
        var y = YAHOO.util.Event.getPageY(e);
3542
        this.autoOffset(x, y);
3543

    
3544
        // This causes the autoscroll code to kick off, which means autoscroll can
3545
        // happen prior to the check for a valid drag handle.
3546
        // this.setDragElPos(x, y);
3547
    },
3548

    
3549
    // overrides YAHOO.util.DragDrop
3550
    b4StartDrag: function(x, y) {
3551
        // show the drag frame
3552
        this.logger.log("start drag show frame, x: " + x + ", y: " + y);
3553
        this.showFrame(x, y);
3554
    },
3555

    
3556
    // overrides YAHOO.util.DragDrop
3557
    b4EndDrag: function(e) {
3558
        this.logger.log(this.id + " b4EndDrag");
3559
        YAHOO.util.Dom.setStyle(this.getDragEl(), "visibility", "hidden"); 
3560
    },
3561

    
3562
    // overrides YAHOO.util.DragDrop
3563
    // By default we try to move the element to the last location of the frame.  
3564
    // This is so that the default behavior mirrors that of YAHOO.util.DD.  
3565
    endDrag: function(e) {
3566
        var DOM = YAHOO.util.Dom;
3567
        this.logger.log(this.id + " endDrag");
3568
        var lel = this.getEl();
3569
        var del = this.getDragEl();
3570

    
3571
        // Show the drag frame briefly so we can get its position
3572
        // del.style.visibility = "";
3573
        DOM.setStyle(del, "visibility", ""); 
3574

    
3575
        // Hide the linked element before the move to get around a Safari 
3576
        // rendering bug.
3577
        //lel.style.visibility = "hidden";
3578
        DOM.setStyle(lel, "visibility", "hidden"); 
3579
        YAHOO.util.DDM.moveToEl(lel, del);
3580
        //del.style.visibility = "hidden";
3581
        DOM.setStyle(del, "visibility", "hidden"); 
3582
        //lel.style.visibility = "";
3583
        DOM.setStyle(lel, "visibility", ""); 
3584
    },
3585

    
3586
    toString: function() {
3587
        return ("DDProxy " + this.id);
3588
    }
3589
/**
3590
* @event mouseDownEvent
3591
* @description Provides access to the mousedown event. The mousedown does not always result in a drag operation.
3592
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3593
*/
3594

    
3595
/**
3596
* @event b4MouseDownEvent
3597
* @description Provides access to the mousedown event, before the mouseDownEvent gets fired. Returning false will cancel the drag.
3598
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3599
*/
3600

    
3601
/**
3602
* @event mouseUpEvent
3603
* @description Fired from inside DragDropMgr when the drag operation is finished.
3604
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3605
*/
3606

    
3607
/**
3608
* @event b4StartDragEvent
3609
* @description Fires before the startDragEvent, returning false will cancel the startDrag Event.
3610
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3611
*/
3612

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

    
3619
/**
3620
* @event b4EndDragEvent
3621
* @description Fires before the endDragEvent. Returning false will cancel.
3622
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3623
*/
3624

    
3625
/**
3626
* @event endDragEvent
3627
* @description Fires on the mouseup event after a drag has been initiated (startDrag fired).
3628
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3629
*/
3630

    
3631
/**
3632
* @event dragEvent
3633
* @description Occurs every mousemove event while dragging.
3634
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3635
*/
3636
/**
3637
* @event b4DragEvent
3638
* @description Fires before the dragEvent.
3639
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3640
*/
3641
/**
3642
* @event invalidDropEvent
3643
* @description Fires when the dragged objects is dropped in a location that contains no drop targets.
3644
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3645
*/
3646
/**
3647
* @event b4DragOutEvent
3648
* @description Fires before the dragOutEvent
3649
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3650
*/
3651
/**
3652
* @event dragOutEvent
3653
* @description Fires when a dragged object is no longer over an object that had the onDragEnter fire. 
3654
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3655
*/
3656
/**
3657
* @event dragEnterEvent
3658
* @description Occurs when the dragged object first interacts with another targettable drag and drop object.
3659
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3660
*/
3661
/**
3662
* @event b4DragOverEvent
3663
* @description Fires before the dragOverEvent.
3664
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3665
*/
3666
/**
3667
* @event dragOverEvent
3668
* @description Fires every mousemove event while over a drag and drop object.
3669
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3670
*/
3671
/**
3672
* @event b4DragDropEvent 
3673
* @description Fires before the dragDropEvent
3674
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3675
*/
3676
/**
3677
* @event dragDropEvent
3678
* @description Fires when the dragged objects is dropped on another.
3679
* @type YAHOO.util.CustomEvent See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
3680
*/
3681

    
3682
});
3683
/**
3684
 * A DragDrop implementation that does not move, but can be a drop 
3685
 * target.  You would get the same result by simply omitting implementation 
3686
 * for the event callbacks, but this way we reduce the processing cost of the 
3687
 * event listener and the callbacks.
3688
 * @class DDTarget
3689
 * @extends YAHOO.util.DragDrop 
3690
 * @constructor
3691
 * @param {String} id the id of the element that is a drop target
3692
 * @param {String} sGroup the group of related DragDrop objects
3693
 * @param {object} config an object containing configurable attributes
3694
 *                 Valid properties for DDTarget in addition to those in 
3695
 *                 DragDrop: 
3696
 *                    none
3697
 */
3698
YAHOO.util.DDTarget = function(id, sGroup, config) {
3699
    if (id) {
3700
        this.initTarget(id, sGroup, config);
3701
    }
3702
};
3703

    
3704
// YAHOO.util.DDTarget.prototype = new YAHOO.util.DragDrop();
3705
YAHOO.extend(YAHOO.util.DDTarget, YAHOO.util.DragDrop, {
3706
    toString: function() {
3707
        return ("DDTarget " + this.id);
3708
    }
3709
});
3710
YAHOO.register("dragdrop", YAHOO.util.DragDropMgr, {version: "2.8.0r4", build: "2449"});
(2-2/5)