Project

General

Profile

1
/*
2
Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.net/yui/license.txt
5
version: 2.4.1
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

    
35
    return {
36

    
37
        /**
38
         * Two dimensional Array of registered DragDrop objects.  The first 
39
         * dimension is the DragDrop item group, the second the DragDrop 
40
         * object.
41
         * @property ids
42
         * @type {string: string}
43
         * @private
44
         * @static
45
         */
46
        ids: {},
47

    
48
        /**
49
         * Array of element ids defined as drag handles.  Used to determine 
50
         * if the element that generated the mousedown event is actually the 
51
         * handle and not the html element itself.
52
         * @property handleIds
53
         * @type {string: string}
54
         * @private
55
         * @static
56
         */
57
        handleIds: {},
58

    
59
        /**
60
         * the DragDrop object that is currently being dragged
61
         * @property dragCurrent
62
         * @type DragDrop
63
         * @private
64
         * @static
65
         **/
66
        dragCurrent: null,
67

    
68
        /**
69
         * the DragDrop object(s) that are being hovered over
70
         * @property dragOvers
71
         * @type Array
72
         * @private
73
         * @static
74
         */
75
        dragOvers: {},
76

    
77
        /**
78
         * the X distance between the cursor and the object being dragged
79
         * @property deltaX
80
         * @type int
81
         * @private
82
         * @static
83
         */
84
        deltaX: 0,
85

    
86
        /**
87
         * the Y distance between the cursor and the object being dragged
88
         * @property deltaY
89
         * @type int
90
         * @private
91
         * @static
92
         */
93
        deltaY: 0,
94

    
95
        /**
96
         * Flag to determine if we should prevent the default behavior of the
97
         * events we define. By default this is true, but this can be set to 
98
         * false if you need the default behavior (not recommended)
99
         * @property preventDefault
100
         * @type boolean
101
         * @static
102
         */
103
        preventDefault: true,
104

    
105
        /**
106
         * Flag to determine if we should stop the propagation of the events 
107
         * we generate. This is true by default but you may want to set it to
108
         * false if the html element contains other features that require the
109
         * mouse click.
110
         * @property stopPropagation
111
         * @type boolean
112
         * @static
113
         */
114
        stopPropagation: true,
115

    
116
        /**
117
         * Internal flag that is set to true when drag and drop has been
118
         * initialized
119
         * @property initialized
120
         * @private
121
         * @static
122
         */
123
        initialized: false,
124

    
125
        /**
126
         * All drag and drop can be disabled.
127
         * @property locked
128
         * @private
129
         * @static
130
         */
131
        locked: false,
132

    
133
        /**
134
         * Provides additional information about the the current set of
135
         * interactions.  Can be accessed from the event handlers. It
136
         * contains the following properties:
137
         *
138
         *       out:       onDragOut interactions
139
         *       enter:     onDragEnter interactions
140
         *       over:      onDragOver interactions
141
         *       drop:      onDragDrop interactions
142
         *       point:     The location of the cursor
143
         *       draggedRegion: The location of dragged element at the time
144
         *                      of the interaction
145
         *       sourceRegion: The location of the source elemtn at the time
146
         *                     of the interaction
147
         *       validDrop: boolean
148
         * @property interactionInfo
149
         * @type object
150
         * @static
151
         */
152
        interactionInfo: null,
153

    
154
        /**
155
         * Called the first time an element is registered.
156
         * @method init
157
         * @private
158
         * @static
159
         */
160
        init: function() {
161
            this.initialized = true;
162
        },
163

    
164
        /**
165
         * In point mode, drag and drop interaction is defined by the 
166
         * location of the cursor during the drag/drop
167
         * @property POINT
168
         * @type int
169
         * @static
170
         * @final
171
         */
172
        POINT: 0,
173

    
174
        /**
175
         * In intersect mode, drag and drop interaction is defined by the 
176
         * cursor position or the amount of overlap of two or more drag and 
177
         * drop objects.
178
         * @property INTERSECT
179
         * @type int
180
         * @static
181
         * @final
182
         */
183
        INTERSECT: 1,
184

    
185
        /**
186
         * In intersect mode, drag and drop interaction is defined only by the 
187
         * overlap of two or more drag and drop objects.
188
         * @property STRICT_INTERSECT
189
         * @type int
190
         * @static
191
         * @final
192
         */
193
        STRICT_INTERSECT: 2,
194

    
195
        /**
196
         * The current drag and drop mode.  Default: POINT
197
         * @property mode
198
         * @type int
199
         * @static
200
         */
201
        mode: 0,
202

    
203
        /**
204
         * Runs method on all drag and drop objects
205
         * @method _execOnAll
206
         * @private
207
         * @static
208
         */
209
        _execOnAll: function(sMethod, args) {
210
            for (var i in this.ids) {
211
                for (var j in this.ids[i]) {
212
                    var oDD = this.ids[i][j];
213
                    if (! this.isTypeOfDD(oDD)) {
214
                        continue;
215
                    }
216
                    oDD[sMethod].apply(oDD, args);
217
                }
218
            }
219
        },
220

    
221
        /**
222
         * Drag and drop initialization.  Sets up the global event handlers
223
         * @method _onLoad
224
         * @private
225
         * @static
226
         */
227
        _onLoad: function() {
228

    
229
            this.init();
230

    
231

    
232
            Event.on(document, "mouseup",   this.handleMouseUp, this, true);
233
            Event.on(document, "mousemove", this.handleMouseMove, this, true);
234
            Event.on(window,   "unload",    this._onUnload, this, true);
235
            Event.on(window,   "resize",    this._onResize, this, true);
236
            // Event.on(window,   "mouseout",    this._test);
237

    
238
        },
239

    
240
        /**
241
         * Reset constraints on all drag and drop objs
242
         * @method _onResize
243
         * @private
244
         * @static
245
         */
246
        _onResize: function(e) {
247
            this._execOnAll("resetConstraints", []);
248
        },
249

    
250
        /**
251
         * Lock all drag and drop functionality
252
         * @method lock
253
         * @static
254
         */
255
        lock: function() { this.locked = true; },
256

    
257
        /**
258
         * Unlock all drag and drop functionality
259
         * @method unlock
260
         * @static
261
         */
262
        unlock: function() { this.locked = false; },
263

    
264
        /**
265
         * Is drag and drop locked?
266
         * @method isLocked
267
         * @return {boolean} True if drag and drop is locked, false otherwise.
268
         * @static
269
         */
270
        isLocked: function() { return this.locked; },
271

    
272
        /**
273
         * Location cache that is set for all drag drop objects when a drag is
274
         * initiated, cleared when the drag is finished.
275
         * @property locationCache
276
         * @private
277
         * @static
278
         */
279
        locationCache: {},
280

    
281
        /**
282
         * Set useCache to false if you want to force object the lookup of each
283
         * drag and drop linked element constantly during a drag.
284
         * @property useCache
285
         * @type boolean
286
         * @static
287
         */
288
        useCache: true,
289

    
290
        /**
291
         * The number of pixels that the mouse needs to move after the 
292
         * mousedown before the drag is initiated.  Default=3;
293
         * @property clickPixelThresh
294
         * @type int
295
         * @static
296
         */
297
        clickPixelThresh: 3,
298

    
299
        /**
300
         * The number of milliseconds after the mousedown event to initiate the
301
         * drag if we don't get a mouseup event. Default=1000
302
         * @property clickTimeThresh
303
         * @type int
304
         * @static
305
         */
306
        clickTimeThresh: 1000,
307

    
308
        /**
309
         * Flag that indicates that either the drag pixel threshold or the 
310
         * mousdown time threshold has been met
311
         * @property dragThreshMet
312
         * @type boolean
313
         * @private
314
         * @static
315
         */
316
        dragThreshMet: false,
317

    
318
        /**
319
         * Timeout used for the click time threshold
320
         * @property clickTimeout
321
         * @type Object
322
         * @private
323
         * @static
324
         */
325
        clickTimeout: null,
326

    
327
        /**
328
         * The X position of the mousedown event stored for later use when a 
329
         * drag threshold is met.
330
         * @property startX
331
         * @type int
332
         * @private
333
         * @static
334
         */
335
        startX: 0,
336

    
337
        /**
338
         * The Y position of the mousedown event stored for later use when a 
339
         * drag threshold is met.
340
         * @property startY
341
         * @type int
342
         * @private
343
         * @static
344
         */
345
        startY: 0,
346

    
347
        /**
348
         * Flag to determine if the drag event was fired from the click timeout and
349
         * not the mouse move threshold.
350
         * @property fromTimeout
351
         * @type boolean
352
         * @private
353
         * @static
354
         */
355
        fromTimeout: false,
356

    
357
        /**
358
         * Each DragDrop instance must be registered with the DragDropMgr.  
359
         * This is executed in DragDrop.init()
360
         * @method regDragDrop
361
         * @param {DragDrop} oDD the DragDrop object to register
362
         * @param {String} sGroup the name of the group this element belongs to
363
         * @static
364
         */
365
        regDragDrop: function(oDD, sGroup) {
366
            if (!this.initialized) { this.init(); }
367
            
368
            if (!this.ids[sGroup]) {
369
                this.ids[sGroup] = {};
370
            }
371
            this.ids[sGroup][oDD.id] = oDD;
372
        },
373

    
374
        /**
375
         * Removes the supplied dd instance from the supplied group. Executed
376
         * by DragDrop.removeFromGroup, so don't call this function directly.
377
         * @method removeDDFromGroup
378
         * @private
379
         * @static
380
         */
381
        removeDDFromGroup: function(oDD, sGroup) {
382
            if (!this.ids[sGroup]) {
383
                this.ids[sGroup] = {};
384
            }
385

    
386
            var obj = this.ids[sGroup];
387
            if (obj && obj[oDD.id]) {
388
                delete obj[oDD.id];
389
            }
390
        },
391

    
392
        /**
393
         * Unregisters a drag and drop item.  This is executed in 
394
         * DragDrop.unreg, use that method instead of calling this directly.
395
         * @method _remove
396
         * @private
397
         * @static
398
         */
399
        _remove: function(oDD) {
400
            for (var g in oDD.groups) {
401
                if (g && this.ids[g][oDD.id]) {
402
                    delete this.ids[g][oDD.id];
403
                }
404
            }
405
            delete this.handleIds[oDD.id];
406
        },
407

    
408
        /**
409
         * Each DragDrop handle element must be registered.  This is done
410
         * automatically when executing DragDrop.setHandleElId()
411
         * @method regHandle
412
         * @param {String} sDDId the DragDrop id this element is a handle for
413
         * @param {String} sHandleId the id of the element that is the drag 
414
         * handle
415
         * @static
416
         */
417
        regHandle: function(sDDId, sHandleId) {
418
            if (!this.handleIds[sDDId]) {
419
                this.handleIds[sDDId] = {};
420
            }
421
            this.handleIds[sDDId][sHandleId] = sHandleId;
422
        },
423

    
424
        /**
425
         * Utility function to determine if a given element has been 
426
         * registered as a drag drop item.
427
         * @method isDragDrop
428
         * @param {String} id the element id to check
429
         * @return {boolean} true if this element is a DragDrop item, 
430
         * false otherwise
431
         * @static
432
         */
433
        isDragDrop: function(id) {
434
            return ( this.getDDById(id) ) ? true : false;
435
        },
436

    
437
        /**
438
         * Returns the drag and drop instances that are in all groups the
439
         * passed in instance belongs to.
440
         * @method getRelated
441
         * @param {DragDrop} p_oDD the obj to get related data for
442
         * @param {boolean} bTargetsOnly if true, only return targetable objs
443
         * @return {DragDrop[]} the related instances
444
         * @static
445
         */
446
        getRelated: function(p_oDD, bTargetsOnly) {
447
            var oDDs = [];
448
            for (var i in p_oDD.groups) {
449
                for (var j in this.ids[i]) {
450
                    var dd = this.ids[i][j];
451
                    if (! this.isTypeOfDD(dd)) {
452
                        continue;
453
                    }
454
                    if (!bTargetsOnly || dd.isTarget) {
455
                        oDDs[oDDs.length] = dd;
456
                    }
457
                }
458
            }
459

    
460
            return oDDs;
461
        },
462

    
463
        /**
464
         * Returns true if the specified dd target is a legal target for 
465
         * the specifice drag obj
466
         * @method isLegalTarget
467
         * @param {DragDrop} the drag obj
468
         * @param {DragDrop} the target
469
         * @return {boolean} true if the target is a legal target for the 
470
         * dd obj
471
         * @static
472
         */
473
        isLegalTarget: function (oDD, oTargetDD) {
474
            var targets = this.getRelated(oDD, true);
475
            for (var i=0, len=targets.length;i<len;++i) {
476
                if (targets[i].id == oTargetDD.id) {
477
                    return true;
478
                }
479
            }
480

    
481
            return false;
482
        },
483

    
484
        /**
485
         * My goal is to be able to transparently determine if an object is
486
         * typeof DragDrop, and the exact subclass of DragDrop.  typeof 
487
         * returns "object", oDD.constructor.toString() always returns
488
         * "DragDrop" and not the name of the subclass.  So for now it just
489
         * evaluates a well-known variable in DragDrop.
490
         * @method isTypeOfDD
491
         * @param {Object} the object to evaluate
492
         * @return {boolean} true if typeof oDD = DragDrop
493
         * @static
494
         */
495
        isTypeOfDD: function (oDD) {
496
            return (oDD && oDD.__ygDragDrop);
497
        },
498

    
499
        /**
500
         * Utility function to determine if a given element has been 
501
         * registered as a drag drop handle for the given Drag Drop object.
502
         * @method isHandle
503
         * @param {String} id the element id to check
504
         * @return {boolean} true if this element is a DragDrop handle, false 
505
         * otherwise
506
         * @static
507
         */
508
        isHandle: function(sDDId, sHandleId) {
509
            return ( this.handleIds[sDDId] && 
510
                            this.handleIds[sDDId][sHandleId] );
511
        },
512

    
513
        /**
514
         * Returns the DragDrop instance for a given id
515
         * @method getDDById
516
         * @param {String} id the id of the DragDrop object
517
         * @return {DragDrop} the drag drop object, null if it is not found
518
         * @static
519
         */
520
        getDDById: function(id) {
521
            for (var i in this.ids) {
522
                if (this.ids[i][id]) {
523
                    return this.ids[i][id];
524
                }
525
            }
526
            return null;
527
        },
528

    
529
        /**
530
         * Fired after a registered DragDrop object gets the mousedown event.
531
         * Sets up the events required to track the object being dragged
532
         * @method handleMouseDown
533
         * @param {Event} e the event
534
         * @param oDD the DragDrop object being dragged
535
         * @private
536
         * @static
537
         */
538
        handleMouseDown: function(e, oDD) {
539

    
540
            this.currentTarget = YAHOO.util.Event.getTarget(e);
541

    
542
            this.dragCurrent = oDD;
543

    
544
            var el = oDD.getEl();
545

    
546
            // track start position
547
            this.startX = YAHOO.util.Event.getPageX(e);
548
            this.startY = YAHOO.util.Event.getPageY(e);
549

    
550
            this.deltaX = this.startX - el.offsetLeft;
551
            this.deltaY = this.startY - el.offsetTop;
552

    
553
            this.dragThreshMet = false;
554

    
555
            this.clickTimeout = setTimeout( 
556
                    function() { 
557
                        var DDM = YAHOO.util.DDM;
558
                        DDM.startDrag(DDM.startX, DDM.startY);
559
                        DDM.fromTimeout = true;
560
                    }, 
561
                    this.clickTimeThresh );
562
        },
563

    
564
        /**
565
         * Fired when either the drag pixel threshol or the mousedown hold 
566
         * time threshold has been met.
567
         * @method startDrag
568
         * @param x {int} the X position of the original mousedown
569
         * @param y {int} the Y position of the original mousedown
570
         * @static
571
         */
572
        startDrag: function(x, y) {
573
            clearTimeout(this.clickTimeout);
574
            var dc = this.dragCurrent;
575
            if (dc) {
576
                dc.b4StartDrag(x, y);
577
            }
578
            if (dc) {
579
                dc.startDrag(x, y);
580
            }
581
            this.dragThreshMet = true;
582
        },
583

    
584
        /**
585
         * Internal function to handle the mouseup event.  Will be invoked 
586
         * from the context of the document.
587
         * @method handleMouseUp
588
         * @param {Event} e the event
589
         * @private
590
         * @static
591
         */
592
        handleMouseUp: function(e) {
593
            if (this.dragCurrent) {
594
                clearTimeout(this.clickTimeout);
595

    
596
                if (this.dragThreshMet) {
597
                    if (this.fromTimeout) {
598
                        this.handleMouseMove(e);
599
                    }
600
                    this.fromTimeout = false;
601
                    this.fireEvents(e, true);
602
                } else {
603
                }
604

    
605
                this.stopDrag(e);
606

    
607
                this.stopEvent(e);
608
            }
609
        },
610

    
611
        /**
612
         * Utility to stop event propagation and event default, if these 
613
         * features are turned on.
614
         * @method stopEvent
615
         * @param {Event} e the event as returned by this.getEvent()
616
         * @static
617
         */
618
        stopEvent: function(e) {
619
            if (this.stopPropagation) {
620
                YAHOO.util.Event.stopPropagation(e);
621
            }
622

    
623
            if (this.preventDefault) {
624
                YAHOO.util.Event.preventDefault(e);
625
            }
626
        },
627

    
628
        /** 
629
         * Ends the current drag, cleans up the state, and fires the endDrag
630
         * and mouseUp events.  Called internally when a mouseup is detected
631
         * during the drag.  Can be fired manually during the drag by passing
632
         * either another event (such as the mousemove event received in onDrag)
633
         * or a fake event with pageX and pageY defined (so that endDrag and
634
         * onMouseUp have usable position data.).  Alternatively, pass true
635
         * for the silent parameter so that the endDrag and onMouseUp events
636
         * are skipped (so no event data is needed.)
637
         *
638
         * @method stopDrag
639
         * @param {Event} e the mouseup event, another event (or a fake event) 
640
         *                  with pageX and pageY defined, or nothing if the 
641
         *                  silent parameter is true
642
         * @param {boolean} silent skips the enddrag and mouseup events if true
643
         * @static
644
         */
645
        stopDrag: function(e, silent) {
646

    
647
            // Fire the drag end event for the item that was dragged
648
            if (this.dragCurrent && !silent) {
649
                if (this.dragThreshMet) {
650
                    this.dragCurrent.b4EndDrag(e);
651
                    this.dragCurrent.endDrag(e);
652
                }
653

    
654
                this.dragCurrent.onMouseUp(e);
655
            }
656

    
657
            this.dragCurrent = null;
658
            this.dragOvers = {};
659
        },
660

    
661
        /** 
662
         * Internal function to handle the mousemove event.  Will be invoked 
663
         * from the context of the html element.
664
         *
665
         * @TODO figure out what we can do about mouse events lost when the 
666
         * user drags objects beyond the window boundary.  Currently we can 
667
         * detect this in internet explorer by verifying that the mouse is 
668
         * down during the mousemove event.  Firefox doesn't give us the 
669
         * button state on the mousemove event.
670
         * @method handleMouseMove
671
         * @param {Event} e the event
672
         * @private
673
         * @static
674
         */
675
        handleMouseMove: function(e) {
676
            
677
            var dc = this.dragCurrent;
678
            if (dc) {
679

    
680
                // var button = e.which || e.button;
681

    
682
                // check for IE mouseup outside of page boundary
683
                if (YAHOO.util.Event.isIE && !e.button) {
684
                    this.stopEvent(e);
685
                    return this.handleMouseUp(e);
686
                }
687

    
688
                if (!this.dragThreshMet) {
689
                    var diffX = Math.abs(this.startX - YAHOO.util.Event.getPageX(e));
690
                    var diffY = Math.abs(this.startY - YAHOO.util.Event.getPageY(e));
691
                    if (diffX > this.clickPixelThresh || 
692
                                diffY > this.clickPixelThresh) {
693
                        this.startDrag(this.startX, this.startY);
694
                    }
695
                }
696

    
697
                if (this.dragThreshMet) {
698
                    dc.b4Drag(e);
699
                    if (dc) {
700
                        dc.onDrag(e);
701
                    }
702
                    if (dc) {
703
                        this.fireEvents(e, false);
704
                    }
705
                }
706

    
707
                this.stopEvent(e);
708
            }
709
        },
710
        
711
        /**
712
         * Iterates over all of the DragDrop elements to find ones we are 
713
         * hovering over or dropping on
714
         * @method fireEvents
715
         * @param {Event} e the event
716
         * @param {boolean} isDrop is this a drop op or a mouseover op?
717
         * @private
718
         * @static
719
         */
720
        fireEvents: function(e, isDrop) {
721
            var dc = this.dragCurrent;
722

    
723
            // If the user did the mouse up outside of the window, we could 
724
            // get here even though we have ended the drag.
725
            // If the config option dragOnly is true, bail out and don't fire the events
726
            if (!dc || dc.isLocked() || dc.dragOnly) {
727
                return;
728
            }
729

    
730
            var x = YAHOO.util.Event.getPageX(e),
731
                y = YAHOO.util.Event.getPageY(e),
732
                pt = new YAHOO.util.Point(x,y),
733
                pos = dc.getTargetCoord(pt.x, pt.y),
734
                el = dc.getDragEl(),
735
                curRegion = new YAHOO.util.Region( pos.y, 
736
                                               pos.x + el.offsetWidth,
737
                                               pos.y + el.offsetHeight, 
738
                                               pos.x ),
739
            
740
                oldOvers = [], // cache the previous dragOver array
741
                outEvts   = [],
742
                overEvts  = [],
743
                dropEvts  = [],
744
                enterEvts = [],
745
                inGroupsObj  = {},
746
                inGroups  = [];
747

    
748

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

    
753
                var ddo = this.dragOvers[i];
754

    
755
                if (! this.isTypeOfDD(ddo)) {
756
                    continue;
757
                }
758
                if (! this.isOverTarget(pt, ddo, this.mode, curRegion)) {
759
                    outEvts.push( ddo );
760
                }
761

    
762
                oldOvers[i] = true;
763
                delete this.dragOvers[i];
764
            }
765

    
766
            for (var sGroup in dc.groups) {
767
                
768
                if ("string" != typeof sGroup) {
769
                    continue;
770
                }
771

    
772
                for (i in this.ids[sGroup]) {
773
                    var oDD = this.ids[sGroup][i];
774
                    if (! this.isTypeOfDD(oDD)) {
775
                        continue;
776
                    }
777

    
778
                    if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
779
                        if (this.isOverTarget(pt, oDD, this.mode, curRegion)) {
780
                            inGroupsObj[sGroup] = true;
781
                            // look for drop interactions
782
                            if (isDrop) {
783
                                dropEvts.push( oDD );
784
                            // look for drag enter and drag over interactions
785
                            } else {
786

    
787
                                // initial drag over: dragEnter fires
788
                                if (!oldOvers[oDD.id]) {
789
                                    enterEvts.push( oDD );
790
                                // subsequent drag overs: dragOver fires
791
                                } else {
792
                                    overEvts.push( oDD );
793
                                }
794

    
795
                                this.dragOvers[oDD.id] = oDD;
796
                            }
797
                        }
798
                    }
799
                }
800
            }
801

    
802
            this.interactionInfo = {
803
                out:       outEvts,
804
                enter:     enterEvts,
805
                over:      overEvts,
806
                drop:      dropEvts,
807
                point:     pt,
808
                draggedRegion:    curRegion,
809
                sourceRegion: this.locationCache[dc.id],
810
                validDrop: isDrop
811
            };
812

    
813
            
814
            for (var inG in inGroupsObj) {
815
                inGroups.push(inG);
816
            }
817

    
818
            // notify about a drop that did not find a target
819
            if (isDrop && !dropEvts.length) {
820
                this.interactionInfo.validDrop = false;
821
                dc.onInvalidDrop(e);
822
            }
823

    
824

    
825
            if (this.mode) {
826
                if (outEvts.length) {
827
                    dc.b4DragOut(e, outEvts);
828
                    if (dc) {
829
                        dc.onDragOut(e, outEvts);
830
                    }
831
                }
832

    
833
                if (enterEvts.length) {
834
                    if (dc) {
835
                        dc.onDragEnter(e, enterEvts, inGroups);
836
                    }
837
                }
838

    
839
                if (overEvts.length) {
840
                    if (dc) {
841
                        dc.b4DragOver(e, overEvts, inGroups);
842
                    }
843

    
844
                    if (dc) {
845
                        dc.onDragOver(e, overEvts, inGroups);
846
                    }
847
                }
848

    
849
                if (dropEvts.length) {
850
                    if (dc) {
851
                        dc.b4DragDrop(e, dropEvts, inGroups);
852
                    }
853
                    if (dc) {
854
                        dc.onDragDrop(e, dropEvts, inGroups);
855
                    }
856
                }
857

    
858
            } else {
859
                // fire dragout events
860
                var len = 0;
861
                for (i=0, len=outEvts.length; i<len; ++i) {
862
                    if (dc) {
863
                        dc.b4DragOut(e, outEvts[i].id, inGroups[0]);
864
                    }
865
                    if (dc) {
866
                        dc.onDragOut(e, outEvts[i].id, inGroups[0]);
867
                    }
868
                }
869
                 
870
                // fire enter events
871
                for (i=0,len=enterEvts.length; i<len; ++i) {
872
                    // dc.b4DragEnter(e, oDD.id);
873

    
874
                    if (dc) {
875
                        dc.onDragEnter(e, enterEvts[i].id, inGroups[0]);
876
                    }
877
                }
878
         
879
                // fire over events
880
                for (i=0,len=overEvts.length; i<len; ++i) {
881
                    if (dc) {
882
                        dc.b4DragOver(e, overEvts[i].id, inGroups[0]);
883
                    }
884
                    if (dc) {
885
                        dc.onDragOver(e, overEvts[i].id, inGroups[0]);
886
                    }
887
                }
888

    
889
                // fire drop events
890
                for (i=0, len=dropEvts.length; i<len; ++i) {
891
                    if (dc) {
892
                        dc.b4DragDrop(e, dropEvts[i].id, inGroups[0]);
893
                    }
894
                    if (dc) {
895
                        dc.onDragDrop(e, dropEvts[i].id, inGroups[0]);
896
                    }
897
                }
898

    
899
            }
900
        },
901

    
902
        /**
903
         * Helper function for getting the best match from the list of drag 
904
         * and drop objects returned by the drag and drop events when we are 
905
         * in INTERSECT mode.  It returns either the first object that the 
906
         * cursor is over, or the object that has the greatest overlap with 
907
         * the dragged element.
908
         * @method getBestMatch
909
         * @param  {DragDrop[]} dds The array of drag and drop objects 
910
         * targeted
911
         * @return {DragDrop}       The best single match
912
         * @static
913
         */
914
        getBestMatch: function(dds) {
915
            var winner = null;
916

    
917
            var len = dds.length;
918

    
919
            if (len == 1) {
920
                winner = dds[0];
921
            } else {
922
                // Loop through the targeted items
923
                for (var i=0; i<len; ++i) {
924
                    var dd = dds[i];
925
                    // If the cursor is over the object, it wins.  If the 
926
                    // cursor is over multiple matches, the first one we come
927
                    // to wins.
928
                    if (this.mode == this.INTERSECT && dd.cursorIsOver) {
929
                        winner = dd;
930
                        break;
931
                    // Otherwise the object with the most overlap wins
932
                    } else {
933
                        if (!winner || !winner.overlap || (dd.overlap &&
934
                            winner.overlap.getArea() < dd.overlap.getArea())) {
935
                            winner = dd;
936
                        }
937
                    }
938
                }
939
            }
940

    
941
            return winner;
942
        },
943

    
944
        /**
945
         * Refreshes the cache of the top-left and bottom-right points of the 
946
         * drag and drop objects in the specified group(s).  This is in the
947
         * format that is stored in the drag and drop instance, so typical 
948
         * usage is:
949
         * <code>
950
         * YAHOO.util.DragDropMgr.refreshCache(ddinstance.groups);
951
         * </code>
952
         * Alternatively:
953
         * <code>
954
         * YAHOO.util.DragDropMgr.refreshCache({group1:true, group2:true});
955
         * </code>
956
         * @TODO this really should be an indexed array.  Alternatively this
957
         * method could accept both.
958
         * @method refreshCache
959
         * @param {Object} groups an associative array of groups to refresh
960
         * @static
961
         */
962
        refreshCache: function(groups) {
963

    
964
            // refresh everything if group array is not provided
965
            var g = groups || this.ids;
966

    
967
            for (var sGroup in g) {
968
                if ("string" != typeof sGroup) {
969
                    continue;
970
                }
971
                for (var i in this.ids[sGroup]) {
972
                    var oDD = this.ids[sGroup][i];
973

    
974
                    if (this.isTypeOfDD(oDD)) {
975
                        var loc = this.getLocation(oDD);
976
                        if (loc) {
977
                            this.locationCache[oDD.id] = loc;
978
                        } else {
979
                            delete this.locationCache[oDD.id];
980
                        }
981
                    }
982
                }
983
            }
984
        },
985

    
986
        /**
987
         * This checks to make sure an element exists and is in the DOM.  The
988
         * main purpose is to handle cases where innerHTML is used to remove
989
         * drag and drop objects from the DOM.  IE provides an 'unspecified
990
         * error' when trying to access the offsetParent of such an element
991
         * @method verifyEl
992
         * @param {HTMLElement} el the element to check
993
         * @return {boolean} true if the element looks usable
994
         * @static
995
         */
996
        verifyEl: function(el) {
997
            try {
998
                if (el) {
999
                    var parent = el.offsetParent;
1000
                    if (parent) {
1001
                        return true;
1002
                    }
1003
                }
1004
            } catch(e) {
1005
            }
1006

    
1007
            return false;
1008
        },
1009
        
1010
        /**
1011
         * Returns a Region object containing the drag and drop element's position
1012
         * and size, including the padding configured for it
1013
         * @method getLocation
1014
         * @param {DragDrop} oDD the drag and drop object to get the 
1015
         *                       location for
1016
         * @return {YAHOO.util.Region} a Region object representing the total area
1017
         *                             the element occupies, including any padding
1018
         *                             the instance is configured for.
1019
         * @static
1020
         */
1021
        getLocation: function(oDD) {
1022
            if (! this.isTypeOfDD(oDD)) {
1023
                return null;
1024
            }
1025

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

    
1028
            try {
1029
                pos= YAHOO.util.Dom.getXY(el);
1030
            } catch (e) { }
1031

    
1032
            if (!pos) {
1033
                return null;
1034
            }
1035

    
1036
            x1 = pos[0];
1037
            x2 = x1 + el.offsetWidth;
1038
            y1 = pos[1];
1039
            y2 = y1 + el.offsetHeight;
1040

    
1041
            t = y1 - oDD.padding[0];
1042
            r = x2 + oDD.padding[1];
1043
            b = y2 + oDD.padding[2];
1044
            l = x1 - oDD.padding[3];
1045

    
1046
            return new YAHOO.util.Region( t, r, b, l );
1047
        },
1048

    
1049
        /**
1050
         * Checks the cursor location to see if it over the target
1051
         * @method isOverTarget
1052
         * @param {YAHOO.util.Point} pt The point to evaluate
1053
         * @param {DragDrop} oTarget the DragDrop object we are inspecting
1054
         * @param {boolean} intersect true if we are in intersect mode
1055
         * @param {YAHOO.util.Region} pre-cached location of the dragged element
1056
         * @return {boolean} true if the mouse is over the target
1057
         * @private
1058
         * @static
1059
         */
1060
        isOverTarget: function(pt, oTarget, intersect, curRegion) {
1061
            // use cache if available
1062
            var loc = this.locationCache[oTarget.id];
1063
            if (!loc || !this.useCache) {
1064
                loc = this.getLocation(oTarget);
1065
                this.locationCache[oTarget.id] = loc;
1066

    
1067
            }
1068

    
1069
            if (!loc) {
1070
                return false;
1071
            }
1072

    
1073
            oTarget.cursorIsOver = loc.contains( pt );
1074

    
1075
            // DragDrop is using this as a sanity check for the initial mousedown
1076
            // in this case we are done.  In POINT mode, if the drag obj has no
1077
            // contraints, we are done. Otherwise we need to evaluate the 
1078
            // region the target as occupies to determine if the dragged element
1079
            // overlaps with it.
1080
            
1081
            var dc = this.dragCurrent;
1082
            if (!dc || (!intersect && !dc.constrainX && !dc.constrainY)) {
1083

    
1084
                //if (oTarget.cursorIsOver) {
1085
                //}
1086
                return oTarget.cursorIsOver;
1087
            }
1088

    
1089
            oTarget.overlap = null;
1090

    
1091
            // Get the current location of the drag element, this is the
1092
            // location of the mouse event less the delta that represents
1093
            // where the original mousedown happened on the element.  We
1094
            // need to consider constraints and ticks as well.
1095

    
1096
            if (!curRegion) {
1097
                var pos = dc.getTargetCoord(pt.x, pt.y);
1098
                var el = dc.getDragEl();
1099
                curRegion = new YAHOO.util.Region( pos.y, 
1100
                                                   pos.x + el.offsetWidth,
1101
                                                   pos.y + el.offsetHeight, 
1102
                                                   pos.x );
1103
            }
1104

    
1105
            var overlap = curRegion.intersect(loc);
1106

    
1107
            if (overlap) {
1108
                oTarget.overlap = overlap;
1109
                return (intersect) ? true : oTarget.cursorIsOver;
1110
            } else {
1111
                return false;
1112
            }
1113
        },
1114

    
1115
        /**
1116
         * unload event handler
1117
         * @method _onUnload
1118
         * @private
1119
         * @static
1120
         */
1121
        _onUnload: function(e, me) {
1122
            this.unregAll();
1123
        },
1124

    
1125
        /**
1126
         * Cleans up the drag and drop events and objects.
1127
         * @method unregAll
1128
         * @private
1129
         * @static
1130
         */
1131
        unregAll: function() {
1132

    
1133
            if (this.dragCurrent) {
1134
                this.stopDrag();
1135
                this.dragCurrent = null;
1136
            }
1137

    
1138
            this._execOnAll("unreg", []);
1139

    
1140
            //for (var i in this.elementCache) {
1141
                //delete this.elementCache[i];
1142
            //}
1143
            //this.elementCache = {};
1144

    
1145
            this.ids = {};
1146
        },
1147

    
1148
        /**
1149
         * A cache of DOM elements
1150
         * @property elementCache
1151
         * @private
1152
         * @static
1153
         * @deprecated elements are not cached now
1154
         */
1155
        elementCache: {},
1156
        
1157
        /**
1158
         * Get the wrapper for the DOM element specified
1159
         * @method getElWrapper
1160
         * @param {String} id the id of the element to get
1161
         * @return {YAHOO.util.DDM.ElementWrapper} the wrapped element
1162
         * @private
1163
         * @deprecated This wrapper isn't that useful
1164
         * @static
1165
         */
1166
        getElWrapper: function(id) {
1167
            var oWrapper = this.elementCache[id];
1168
            if (!oWrapper || !oWrapper.el) {
1169
                oWrapper = this.elementCache[id] = 
1170
                    new this.ElementWrapper(YAHOO.util.Dom.get(id));
1171
            }
1172
            return oWrapper;
1173
        },
1174

    
1175
        /**
1176
         * Returns the actual DOM element
1177
         * @method getElement
1178
         * @param {String} id the id of the elment to get
1179
         * @return {Object} The element
1180
         * @deprecated use YAHOO.util.Dom.get instead
1181
         * @static
1182
         */
1183
        getElement: function(id) {
1184
            return YAHOO.util.Dom.get(id);
1185
        },
1186
        
1187
        /**
1188
         * Returns the style property for the DOM element (i.e., 
1189
         * document.getElById(id).style)
1190
         * @method getCss
1191
         * @param {String} id the id of the elment to get
1192
         * @return {Object} The style property of the element
1193
         * @deprecated use YAHOO.util.Dom instead
1194
         * @static
1195
         */
1196
        getCss: function(id) {
1197
            var el = YAHOO.util.Dom.get(id);
1198
            return (el) ? el.style : null;
1199
        },
1200

    
1201
        /**
1202
         * Inner class for cached elements
1203
         * @class DragDropMgr.ElementWrapper
1204
         * @for DragDropMgr
1205
         * @private
1206
         * @deprecated
1207
         */
1208
        ElementWrapper: function(el) {
1209
                /**
1210
                 * The element
1211
                 * @property el
1212
                 */
1213
                this.el = el || null;
1214
                /**
1215
                 * The element id
1216
                 * @property id
1217
                 */
1218
                this.id = this.el && el.id;
1219
                /**
1220
                 * A reference to the style property
1221
                 * @property css
1222
                 */
1223
                this.css = this.el && el.style;
1224
            },
1225

    
1226
        /**
1227
         * Returns the X position of an html element
1228
         * @method getPosX
1229
         * @param el the element for which to get the position
1230
         * @return {int} the X coordinate
1231
         * @for DragDropMgr
1232
         * @deprecated use YAHOO.util.Dom.getX instead
1233
         * @static
1234
         */
1235
        getPosX: function(el) {
1236
            return YAHOO.util.Dom.getX(el);
1237
        },
1238

    
1239
        /**
1240
         * Returns the Y position of an html element
1241
         * @method getPosY
1242
         * @param el the element for which to get the position
1243
         * @return {int} the Y coordinate
1244
         * @deprecated use YAHOO.util.Dom.getY instead
1245
         * @static
1246
         */
1247
        getPosY: function(el) {
1248
            return YAHOO.util.Dom.getY(el); 
1249
        },
1250

    
1251
        /**
1252
         * Swap two nodes.  In IE, we use the native method, for others we 
1253
         * emulate the IE behavior
1254
         * @method swapNode
1255
         * @param n1 the first node to swap
1256
         * @param n2 the other node to swap
1257
         * @static
1258
         */
1259
        swapNode: function(n1, n2) {
1260
            if (n1.swapNode) {
1261
                n1.swapNode(n2);
1262
            } else {
1263
                var p = n2.parentNode;
1264
                var s = n2.nextSibling;
1265

    
1266
                if (s == n1) {
1267
                    p.insertBefore(n1, n2);
1268
                } else if (n2 == n1.nextSibling) {
1269
                    p.insertBefore(n2, n1);
1270
                } else {
1271
                    n1.parentNode.replaceChild(n2, n1);
1272
                    p.insertBefore(n1, s);
1273
                }
1274
            }
1275
        },
1276

    
1277
        /**
1278
         * Returns the current scroll position
1279
         * @method getScroll
1280
         * @private
1281
         * @static
1282
         */
1283
        getScroll: function () {
1284
            var t, l, dde=document.documentElement, db=document.body;
1285
            if (dde && (dde.scrollTop || dde.scrollLeft)) {
1286
                t = dde.scrollTop;
1287
                l = dde.scrollLeft;
1288
            } else if (db) {
1289
                t = db.scrollTop;
1290
                l = db.scrollLeft;
1291
            } else {
1292
            }
1293
            return { top: t, left: l };
1294
        },
1295

    
1296
        /**
1297
         * Returns the specified element style property
1298
         * @method getStyle
1299
         * @param {HTMLElement} el          the element
1300
         * @param {string}      styleProp   the style property
1301
         * @return {string} The value of the style property
1302
         * @deprecated use YAHOO.util.Dom.getStyle
1303
         * @static
1304
         */
1305
        getStyle: function(el, styleProp) {
1306
            return YAHOO.util.Dom.getStyle(el, styleProp);
1307
        },
1308

    
1309
        /**
1310
         * Gets the scrollTop
1311
         * @method getScrollTop
1312
         * @return {int} the document's scrollTop
1313
         * @static
1314
         */
1315
        getScrollTop: function () { return this.getScroll().top; },
1316

    
1317
        /**
1318
         * Gets the scrollLeft
1319
         * @method getScrollLeft
1320
         * @return {int} the document's scrollTop
1321
         * @static
1322
         */
1323
        getScrollLeft: function () { return this.getScroll().left; },
1324

    
1325
        /**
1326
         * Sets the x/y position of an element to the location of the
1327
         * target element.
1328
         * @method moveToEl
1329
         * @param {HTMLElement} moveEl      The element to move
1330
         * @param {HTMLElement} targetEl    The position reference element
1331
         * @static
1332
         */
1333
        moveToEl: function (moveEl, targetEl) {
1334
            var aCoord = YAHOO.util.Dom.getXY(targetEl);
1335
            YAHOO.util.Dom.setXY(moveEl, aCoord);
1336
        },
1337

    
1338
        /**
1339
         * Gets the client height
1340
         * @method getClientHeight
1341
         * @return {int} client height in px
1342
         * @deprecated use YAHOO.util.Dom.getViewportHeight instead
1343
         * @static
1344
         */
1345
        getClientHeight: function() {
1346
            return YAHOO.util.Dom.getViewportHeight();
1347
        },
1348

    
1349
        /**
1350
         * Gets the client width
1351
         * @method getClientWidth
1352
         * @return {int} client width in px
1353
         * @deprecated use YAHOO.util.Dom.getViewportWidth instead
1354
         * @static
1355
         */
1356
        getClientWidth: function() {
1357
            return YAHOO.util.Dom.getViewportWidth();
1358
        },
1359

    
1360
        /**
1361
         * Numeric array sort function
1362
         * @method numericSort
1363
         * @static
1364
         */
1365
        numericSort: function(a, b) { return (a - b); },
1366

    
1367
        /**
1368
         * Internal counter
1369
         * @property _timeoutCount
1370
         * @private
1371
         * @static
1372
         */
1373
        _timeoutCount: 0,
1374

    
1375
        /**
1376
         * Trying to make the load order less important.  Without this we get
1377
         * an error if this file is loaded before the Event Utility.
1378
         * @method _addListeners
1379
         * @private
1380
         * @static
1381
         */
1382
        _addListeners: function() {
1383
            var DDM = YAHOO.util.DDM;
1384
            if ( YAHOO.util.Event && document ) {
1385
                DDM._onLoad();
1386
            } else {
1387
                if (DDM._timeoutCount > 2000) {
1388
                } else {
1389
                    setTimeout(DDM._addListeners, 10);
1390
                    if (document && document.body) {
1391
                        DDM._timeoutCount += 1;
1392
                    }
1393
                }
1394
            }
1395
        },
1396

    
1397
        /**
1398
         * Recursively searches the immediate parent and all child nodes for 
1399
         * the handle element in order to determine wheter or not it was 
1400
         * clicked.
1401
         * @method handleWasClicked
1402
         * @param node the html element to inspect
1403
         * @static
1404
         */
1405
        handleWasClicked: function(node, id) {
1406
            if (this.isHandle(id, node.id)) {
1407
                return true;
1408
            } else {
1409
                // check to see if this is a text node child of the one we want
1410
                var p = node.parentNode;
1411

    
1412
                while (p) {
1413
                    if (this.isHandle(id, p.id)) {
1414
                        return true;
1415
                    } else {
1416
                        p = p.parentNode;
1417
                    }
1418
                }
1419
            }
1420

    
1421
            return false;
1422
        }
1423

    
1424
    };
1425

    
1426
}();
1427

    
1428
// shorter alias, save a few bytes
1429
YAHOO.util.DDM = YAHOO.util.DragDropMgr;
1430
YAHOO.util.DDM._addListeners();
1431

    
1432
}
1433

    
1434
(function() {
1435

    
1436
var Event=YAHOO.util.Event; 
1437
var Dom=YAHOO.util.Dom;
1438

    
1439
/**
1440
 * Defines the interface and base operation of items that that can be 
1441
 * dragged or can be drop targets.  It was designed to be extended, overriding
1442
 * the event handlers for startDrag, onDrag, onDragOver, onDragOut.
1443
 * Up to three html elements can be associated with a DragDrop instance:
1444
 * <ul>
1445
 * <li>linked element: the element that is passed into the constructor.
1446
 * This is the element which defines the boundaries for interaction with 
1447
 * other DragDrop objects.</li>
1448
 * <li>handle element(s): The drag operation only occurs if the element that 
1449
 * was clicked matches a handle element.  By default this is the linked 
1450
 * element, but there are times that you will want only a portion of the 
1451
 * linked element to initiate the drag operation, and the setHandleElId() 
1452
 * method provides a way to define this.</li>
1453
 * <li>drag element: this represents an the element that would be moved along
1454
 * with the cursor during a drag operation.  By default, this is the linked
1455
 * element itself as in {@link YAHOO.util.DD}.  setDragElId() lets you define
1456
 * a separate element that would be moved, as in {@link YAHOO.util.DDProxy}
1457
 * </li>
1458
 * </ul>
1459
 * This class should not be instantiated until the onload event to ensure that
1460
 * the associated elements are available.
1461
 * The following would define a DragDrop obj that would interact with any 
1462
 * other DragDrop obj in the "group1" group:
1463
 * <pre>
1464
 *  dd = new YAHOO.util.DragDrop("div1", "group1");
1465
 * </pre>
1466
 * Since none of the event handlers have been implemented, nothing would 
1467
 * actually happen if you were to run the code above.  Normally you would 
1468
 * override this class or one of the default implementations, but you can 
1469
 * also override the methods you want on an instance of the class...
1470
 * <pre>
1471
 *  dd.onDragDrop = function(e, id) {
1472
 *  &nbsp;&nbsp;alert("dd was dropped on " + id);
1473
 *  }
1474
 * </pre>
1475
 * @namespace YAHOO.util
1476
 * @class DragDrop
1477
 * @constructor
1478
 * @param {String} id of the element that is linked to this instance
1479
 * @param {String} sGroup the group of related DragDrop objects
1480
 * @param {object} config an object containing configurable attributes
1481
 *                Valid properties for DragDrop: 
1482
 *                    padding, isTarget, maintainOffset, primaryButtonOnly,
1483
 */
1484
YAHOO.util.DragDrop = function(id, sGroup, config) {
1485
    if (id) {
1486
        this.init(id, sGroup, config); 
1487
    }
1488
};
1489

    
1490
YAHOO.util.DragDrop.prototype = {
1491

    
1492
    /**
1493
     * The id of the element associated with this object.  This is what we 
1494
     * refer to as the "linked element" because the size and position of 
1495
     * this element is used to determine when the drag and drop objects have 
1496
     * interacted.
1497
     * @property id
1498
     * @type String
1499
     */
1500
    id: null,
1501

    
1502
    /**
1503
     * Configuration attributes passed into the constructor
1504
     * @property config
1505
     * @type object
1506
     */
1507
    config: null,
1508

    
1509
    /**
1510
     * The id of the element that will be dragged.  By default this is same 
1511
     * as the linked element , but could be changed to another element. Ex: 
1512
     * YAHOO.util.DDProxy
1513
     * @property dragElId
1514
     * @type String
1515
     * @private
1516
     */
1517
    dragElId: null, 
1518

    
1519
    /**
1520
     * the id of the element that initiates the drag operation.  By default 
1521
     * this is the linked element, but could be changed to be a child of this
1522
     * element.  This lets us do things like only starting the drag when the 
1523
     * header element within the linked html element is clicked.
1524
     * @property handleElId
1525
     * @type String
1526
     * @private
1527
     */
1528
    handleElId: null, 
1529

    
1530
    /**
1531
     * An associative array of HTML tags that will be ignored if clicked.
1532
     * @property invalidHandleTypes
1533
     * @type {string: string}
1534
     */
1535
    invalidHandleTypes: null, 
1536

    
1537
    /**
1538
     * An associative array of ids for elements that will be ignored if clicked
1539
     * @property invalidHandleIds
1540
     * @type {string: string}
1541
     */
1542
    invalidHandleIds: null, 
1543

    
1544
    /**
1545
     * An indexted array of css class names for elements that will be ignored
1546
     * if clicked.
1547
     * @property invalidHandleClasses
1548
     * @type string[]
1549
     */
1550
    invalidHandleClasses: null, 
1551

    
1552
    /**
1553
     * The linked element's absolute X position at the time the drag was 
1554
     * started
1555
     * @property startPageX
1556
     * @type int
1557
     * @private
1558
     */
1559
    startPageX: 0,
1560

    
1561
    /**
1562
     * The linked element's absolute X position at the time the drag was 
1563
     * started
1564
     * @property startPageY
1565
     * @type int
1566
     * @private
1567
     */
1568
    startPageY: 0,
1569

    
1570
    /**
1571
     * The group defines a logical collection of DragDrop objects that are 
1572
     * related.  Instances only get events when interacting with other 
1573
     * DragDrop object in the same group.  This lets us define multiple 
1574
     * groups using a single DragDrop subclass if we want.
1575
     * @property groups
1576
     * @type {string: string}
1577
     */
1578
    groups: null,
1579

    
1580
    /**
1581
     * Individual drag/drop instances can be locked.  This will prevent 
1582
     * onmousedown start drag.
1583
     * @property locked
1584
     * @type boolean
1585
     * @private
1586
     */
1587
    locked: false,
1588

    
1589
    /**
1590
     * Lock this instance
1591
     * @method lock
1592
     */
1593
    lock: function() { this.locked = true; },
1594

    
1595
    /**
1596
     * Unlock this instace
1597
     * @method unlock
1598
     */
1599
    unlock: function() { this.locked = false; },
1600

    
1601
    /**
1602
     * By default, all instances can be a drop target.  This can be disabled by
1603
     * setting isTarget to false.
1604
     * @property isTarget
1605
     * @type boolean
1606
     */
1607
    isTarget: true,
1608

    
1609
    /**
1610
     * The padding configured for this drag and drop object for calculating
1611
     * the drop zone intersection with this object.
1612
     * @property padding
1613
     * @type int[]
1614
     */
1615
    padding: null,
1616
    /**
1617
     * If this flag is true, do not fire drop events. The element is a drag only element (for movement not dropping)
1618
     * @property dragOnly
1619
     * @type Boolean
1620
     */
1621
    dragOnly: false,
1622

    
1623
    /**
1624
     * Cached reference to the linked element
1625
     * @property _domRef
1626
     * @private
1627
     */
1628
    _domRef: null,
1629

    
1630
    /**
1631
     * Internal typeof flag
1632
     * @property __ygDragDrop
1633
     * @private
1634
     */
1635
    __ygDragDrop: true,
1636

    
1637
    /**
1638
     * Set to true when horizontal contraints are applied
1639
     * @property constrainX
1640
     * @type boolean
1641
     * @private
1642
     */
1643
    constrainX: false,
1644

    
1645
    /**
1646
     * Set to true when vertical contraints are applied
1647
     * @property constrainY
1648
     * @type boolean
1649
     * @private
1650
     */
1651
    constrainY: false,
1652

    
1653
    /**
1654
     * The left constraint
1655
     * @property minX
1656
     * @type int
1657
     * @private
1658
     */
1659
    minX: 0,
1660

    
1661
    /**
1662
     * The right constraint
1663
     * @property maxX
1664
     * @type int
1665
     * @private
1666
     */
1667
    maxX: 0,
1668

    
1669
    /**
1670
     * The up constraint 
1671
     * @property minY
1672
     * @type int
1673
     * @type int
1674
     * @private
1675
     */
1676
    minY: 0,
1677

    
1678
    /**
1679
     * The down constraint 
1680
     * @property maxY
1681
     * @type int
1682
     * @private
1683
     */
1684
    maxY: 0,
1685

    
1686
    /**
1687
     * The difference between the click position and the source element's location
1688
     * @property deltaX
1689
     * @type int
1690
     * @private
1691
     */
1692
    deltaX: 0,
1693

    
1694
    /**
1695
     * The difference between the click position and the source element's location
1696
     * @property deltaY
1697
     * @type int
1698
     * @private
1699
     */
1700
    deltaY: 0,
1701

    
1702
    /**
1703
     * Maintain offsets when we resetconstraints.  Set to true when you want
1704
     * the position of the element relative to its parent to stay the same
1705
     * when the page changes
1706
     *
1707
     * @property maintainOffset
1708
     * @type boolean
1709
     */
1710
    maintainOffset: false,
1711

    
1712
    /**
1713
     * Array of pixel locations the element will snap to if we specified a 
1714
     * horizontal graduation/interval.  This array is generated automatically
1715
     * when you define a tick interval.
1716
     * @property xTicks
1717
     * @type int[]
1718
     */
1719
    xTicks: null,
1720

    
1721
    /**
1722
     * Array of pixel locations the element will snap to if we specified a 
1723
     * vertical graduation/interval.  This array is generated automatically 
1724
     * when you define a tick interval.
1725
     * @property yTicks
1726
     * @type int[]
1727
     */
1728
    yTicks: null,
1729

    
1730
    /**
1731
     * By default the drag and drop instance will only respond to the primary
1732
     * button click (left button for a right-handed mouse).  Set to true to
1733
     * allow drag and drop to start with any mouse click that is propogated
1734
     * by the browser
1735
     * @property primaryButtonOnly
1736
     * @type boolean
1737
     */
1738
    primaryButtonOnly: true,
1739

    
1740
    /**
1741
     * The availabe property is false until the linked dom element is accessible.
1742
     * @property available
1743
     * @type boolean
1744
     */
1745
    available: false,
1746

    
1747
    /**
1748
     * By default, drags can only be initiated if the mousedown occurs in the
1749
     * region the linked element is.  This is done in part to work around a
1750
     * bug in some browsers that mis-report the mousedown if the previous
1751
     * mouseup happened outside of the window.  This property is set to true
1752
     * if outer handles are defined.
1753
     *
1754
     * @property hasOuterHandles
1755
     * @type boolean
1756
     * @default false
1757
     */
1758
    hasOuterHandles: false,
1759

    
1760
    /**
1761
     * Property that is assigned to a drag and drop object when testing to
1762
     * see if it is being targeted by another dd object.  This property
1763
     * can be used in intersect mode to help determine the focus of
1764
     * the mouse interaction.  DDM.getBestMatch uses this property first to
1765
     * determine the closest match in INTERSECT mode when multiple targets
1766
     * are part of the same interaction.
1767
     * @property cursorIsOver
1768
     * @type boolean
1769
     */
1770
    cursorIsOver: false,
1771

    
1772
    /**
1773
     * Property that is assigned to a drag and drop object when testing to
1774
     * see if it is being targeted by another dd object.  This is a region
1775
     * that represents the area the draggable element overlaps this target.
1776
     * DDM.getBestMatch uses this property to compare the size of the overlap
1777
     * to that of other targets in order to determine the closest match in
1778
     * INTERSECT mode when multiple targets are part of the same interaction.
1779
     * @property overlap 
1780
     * @type YAHOO.util.Region
1781
     */
1782
    overlap: null,
1783

    
1784
    /**
1785
     * Code that executes immediately before the startDrag event
1786
     * @method b4StartDrag
1787
     * @private
1788
     */
1789
    b4StartDrag: function(x, y) { },
1790

    
1791
    /**
1792
     * Abstract method called after a drag/drop object is clicked
1793
     * and the drag or mousedown time thresholds have beeen met.
1794
     * @method startDrag
1795
     * @param {int} X click location
1796
     * @param {int} Y click location
1797
     */
1798
    startDrag: function(x, y) { /* override this */ },
1799

    
1800
    /**
1801
     * Code that executes immediately before the onDrag event
1802
     * @method b4Drag
1803
     * @private
1804
     */
1805
    b4Drag: function(e) { },
1806

    
1807
    /**
1808
     * Abstract method called during the onMouseMove event while dragging an 
1809
     * object.
1810
     * @method onDrag
1811
     * @param {Event} e the mousemove event
1812
     */
1813
    onDrag: function(e) { /* override this */ },
1814

    
1815
    /**
1816
     * Abstract method called when this element fist begins hovering over 
1817
     * another DragDrop obj
1818
     * @method onDragEnter
1819
     * @param {Event} e the mousemove event
1820
     * @param {String|DragDrop[]} id In POINT mode, the element
1821
     * id this is hovering over.  In INTERSECT mode, an array of one or more 
1822
     * dragdrop items being hovered over.
1823
     */
1824
    onDragEnter: function(e, id) { /* override this */ },
1825

    
1826
    /**
1827
     * Code that executes immediately before the onDragOver event
1828
     * @method b4DragOver
1829
     * @private
1830
     */
1831
    b4DragOver: function(e) { },
1832

    
1833
    /**
1834
     * Abstract method called when this element is hovering over another 
1835
     * DragDrop obj
1836
     * @method onDragOver
1837
     * @param {Event} e the mousemove event
1838
     * @param {String|DragDrop[]} id In POINT mode, the element
1839
     * id this is hovering over.  In INTERSECT mode, an array of dd items 
1840
     * being hovered over.
1841
     */
1842
    onDragOver: function(e, id) { /* override this */ },
1843

    
1844
    /**
1845
     * Code that executes immediately before the onDragOut event
1846
     * @method b4DragOut
1847
     * @private
1848
     */
1849
    b4DragOut: function(e) { },
1850

    
1851
    /**
1852
     * Abstract method called when we are no longer hovering over an element
1853
     * @method onDragOut
1854
     * @param {Event} e the mousemove event
1855
     * @param {String|DragDrop[]} id In POINT mode, the element
1856
     * id this was hovering over.  In INTERSECT mode, an array of dd items 
1857
     * that the mouse is no longer over.
1858
     */
1859
    onDragOut: function(e, id) { /* override this */ },
1860

    
1861
    /**
1862
     * Code that executes immediately before the onDragDrop event
1863
     * @method b4DragDrop
1864
     * @private
1865
     */
1866
    b4DragDrop: function(e) { },
1867

    
1868
    /**
1869
     * Abstract method called when this item is dropped on another DragDrop 
1870
     * obj
1871
     * @method onDragDrop
1872
     * @param {Event} e the mouseup event
1873
     * @param {String|DragDrop[]} id In POINT mode, the element
1874
     * id this was dropped on.  In INTERSECT mode, an array of dd items this 
1875
     * was dropped on.
1876
     */
1877
    onDragDrop: function(e, id) { /* override this */ },
1878

    
1879
    /**
1880
     * Abstract method called when this item is dropped on an area with no
1881
     * drop target
1882
     * @method onInvalidDrop
1883
     * @param {Event} e the mouseup event
1884
     */
1885
    onInvalidDrop: function(e) { /* override this */ },
1886

    
1887
    /**
1888
     * Code that executes immediately before the endDrag event
1889
     * @method b4EndDrag
1890
     * @private
1891
     */
1892
    b4EndDrag: function(e) { },
1893

    
1894
    /**
1895
     * Fired when we are done dragging the object
1896
     * @method endDrag
1897
     * @param {Event} e the mouseup event
1898
     */
1899
    endDrag: function(e) { /* override this */ },
1900

    
1901
    /**
1902
     * Code executed immediately before the onMouseDown event
1903
     * @method b4MouseDown
1904
     * @param {Event} e the mousedown event
1905
     * @private
1906
     */
1907
    b4MouseDown: function(e) {  },
1908

    
1909
    /**
1910
     * Event handler that fires when a drag/drop obj gets a mousedown
1911
     * @method onMouseDown
1912
     * @param {Event} e the mousedown event
1913
     */
1914
    onMouseDown: function(e) { /* override this */ },
1915

    
1916
    /**
1917
     * Event handler that fires when a drag/drop obj gets a mouseup
1918
     * @method onMouseUp
1919
     * @param {Event} e the mouseup event
1920
     */
1921
    onMouseUp: function(e) { /* override this */ },
1922
   
1923
    /**
1924
     * Override the onAvailable method to do what is needed after the initial
1925
     * position was determined.
1926
     * @method onAvailable
1927
     */
1928
    onAvailable: function () { 
1929
    },
1930

    
1931
    /**
1932
     * Returns a reference to the linked element
1933
     * @method getEl
1934
     * @return {HTMLElement} the html element 
1935
     */
1936
    getEl: function() { 
1937
        if (!this._domRef) {
1938
            this._domRef = Dom.get(this.id); 
1939
        }
1940

    
1941
        return this._domRef;
1942
    },
1943

    
1944
    /**
1945
     * Returns a reference to the actual element to drag.  By default this is
1946
     * the same as the html element, but it can be assigned to another 
1947
     * element. An example of this can be found in YAHOO.util.DDProxy
1948
     * @method getDragEl
1949
     * @return {HTMLElement} the html element 
1950
     */
1951
    getDragEl: function() {
1952
        return Dom.get(this.dragElId);
1953
    },
1954

    
1955
    /**
1956
     * Sets up the DragDrop object.  Must be called in the constructor of any
1957
     * YAHOO.util.DragDrop subclass
1958
     * @method init
1959
     * @param id the id of the linked element
1960
     * @param {String} sGroup the group of related items
1961
     * @param {object} config configuration attributes
1962
     */
1963
    init: function(id, sGroup, config) {
1964
        this.initTarget(id, sGroup, config);
1965
        Event.on(this._domRef || this.id, "mousedown", 
1966
                        this.handleMouseDown, this, true);
1967
        // Event.on(this.id, "selectstart", Event.preventDefault);
1968
    },
1969

    
1970
    /**
1971
     * Initializes Targeting functionality only... the object does not
1972
     * get a mousedown handler.
1973
     * @method initTarget
1974
     * @param id the id of the linked element
1975
     * @param {String} sGroup the group of related items
1976
     * @param {object} config configuration attributes
1977
     */
1978
    initTarget: function(id, sGroup, config) {
1979

    
1980
        // configuration attributes 
1981
        this.config = config || {};
1982

    
1983
        // create a local reference to the drag and drop manager
1984
        this.DDM = YAHOO.util.DDM;
1985

    
1986
        // initialize the groups object
1987
        this.groups = {};
1988

    
1989
        // assume that we have an element reference instead of an id if the
1990
        // parameter is not a string
1991
        if (typeof id !== "string") {
1992
            this._domRef = id;
1993
            id = Dom.generateId(id);
1994
        }
1995

    
1996
        // set the id
1997
        this.id = id;
1998

    
1999
        // add to an interaction group
2000
        this.addToGroup((sGroup) ? sGroup : "default");
2001

    
2002
        // We don't want to register this as the handle with the manager
2003
        // so we just set the id rather than calling the setter.
2004
        this.handleElId = id;
2005

    
2006
        Event.onAvailable(id, this.handleOnAvailable, this, true);
2007

    
2008

    
2009
        // the linked element is the element that gets dragged by default
2010
        this.setDragElId(id); 
2011

    
2012
        // by default, clicked anchors will not start drag operations. 
2013
        // @TODO what else should be here?  Probably form fields.
2014
        this.invalidHandleTypes = { A: "A" };
2015
        this.invalidHandleIds = {};
2016
        this.invalidHandleClasses = [];
2017

    
2018
        this.applyConfig();
2019
    },
2020

    
2021
    /**
2022
     * Applies the configuration parameters that were passed into the constructor.
2023
     * This is supposed to happen at each level through the inheritance chain.  So
2024
     * a DDProxy implentation will execute apply config on DDProxy, DD, and 
2025
     * DragDrop in order to get all of the parameters that are available in
2026
     * each object.
2027
     * @method applyConfig
2028
     */
2029
    applyConfig: function() {
2030

    
2031
        // configurable properties: 
2032
        //    padding, isTarget, maintainOffset, primaryButtonOnly
2033
        this.padding           = this.config.padding || [0, 0, 0, 0];
2034
        this.isTarget          = (this.config.isTarget !== false);
2035
        this.maintainOffset    = (this.config.maintainOffset);
2036
        this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
2037
        this.dragOnly = ((this.config.dragOnly === true) ? true : false);
2038
    },
2039

    
2040
    /**
2041
     * Executed when the linked element is available
2042
     * @method handleOnAvailable
2043
     * @private
2044
     */
2045
    handleOnAvailable: function() {
2046
        this.available = true;
2047
        this.resetConstraints();
2048
        this.onAvailable();
2049
    },
2050

    
2051
     /**
2052
     * Configures the padding for the target zone in px.  Effectively expands
2053
     * (or reduces) the virtual object size for targeting calculations.  
2054
     * Supports css-style shorthand; if only one parameter is passed, all sides
2055
     * will have that padding, and if only two are passed, the top and bottom
2056
     * will have the first param, the left and right the second.
2057
     * @method setPadding
2058
     * @param {int} iTop    Top pad
2059
     * @param {int} iRight  Right pad
2060
     * @param {int} iBot    Bot pad
2061
     * @param {int} iLeft   Left pad
2062
     */
2063
    setPadding: function(iTop, iRight, iBot, iLeft) {
2064
        // this.padding = [iLeft, iRight, iTop, iBot];
2065
        if (!iRight && 0 !== iRight) {
2066
            this.padding = [iTop, iTop, iTop, iTop];
2067
        } else if (!iBot && 0 !== iBot) {
2068
            this.padding = [iTop, iRight, iTop, iRight];
2069
        } else {
2070
            this.padding = [iTop, iRight, iBot, iLeft];
2071
        }
2072
    },
2073

    
2074
    /**
2075
     * Stores the initial placement of the linked element.
2076
     * @method setInitialPosition
2077
     * @param {int} diffX   the X offset, default 0
2078
     * @param {int} diffY   the Y offset, default 0
2079
     * @private
2080
     */
2081
    setInitPosition: function(diffX, diffY) {
2082
        var el = this.getEl();
2083

    
2084
        if (!this.DDM.verifyEl(el)) {
2085
            return;
2086
        }
2087

    
2088
        var dx = diffX || 0;
2089
        var dy = diffY || 0;
2090

    
2091
        var p = Dom.getXY( el );
2092

    
2093
        this.initPageX = p[0] - dx;
2094
        this.initPageY = p[1] - dy;
2095

    
2096
        this.lastPageX = p[0];
2097
        this.lastPageY = p[1];
2098

    
2099

    
2100

    
2101
        this.setStartPosition(p);
2102
    },
2103

    
2104
    /**
2105
     * Sets the start position of the element.  This is set when the obj
2106
     * is initialized, the reset when a drag is started.
2107
     * @method setStartPosition
2108
     * @param pos current position (from previous lookup)
2109
     * @private
2110
     */
2111
    setStartPosition: function(pos) {
2112
        var p = pos || Dom.getXY(this.getEl());
2113

    
2114
        this.deltaSetXY = null;
2115

    
2116
        this.startPageX = p[0];
2117
        this.startPageY = p[1];
2118
    },
2119

    
2120
    /**
2121
     * Add this instance to a group of related drag/drop objects.  All 
2122
     * instances belong to at least one group, and can belong to as many 
2123
     * groups as needed.
2124
     * @method addToGroup
2125
     * @param sGroup {string} the name of the group
2126
     */
2127
    addToGroup: function(sGroup) {
2128
        this.groups[sGroup] = true;
2129
        this.DDM.regDragDrop(this, sGroup);
2130
    },
2131

    
2132
    /**
2133
     * Remove's this instance from the supplied interaction group
2134
     * @method removeFromGroup
2135
     * @param {string}  sGroup  The group to drop
2136
     */
2137
    removeFromGroup: function(sGroup) {
2138
        if (this.groups[sGroup]) {
2139
            delete this.groups[sGroup];
2140
        }
2141

    
2142
        this.DDM.removeDDFromGroup(this, sGroup);
2143
    },
2144

    
2145
    /**
2146
     * Allows you to specify that an element other than the linked element 
2147
     * will be moved with the cursor during a drag
2148
     * @method setDragElId
2149
     * @param id {string} the id of the element that will be used to initiate the drag
2150
     */
2151
    setDragElId: function(id) {
2152
        this.dragElId = id;
2153
    },
2154

    
2155
    /**
2156
     * Allows you to specify a child of the linked element that should be 
2157
     * used to initiate the drag operation.  An example of this would be if 
2158
     * you have a content div with text and links.  Clicking anywhere in the 
2159
     * content area would normally start the drag operation.  Use this method
2160
     * to specify that an element inside of the content div is the element 
2161
     * that starts the drag operation.
2162
     * @method setHandleElId
2163
     * @param id {string} the id of the element that will be used to 
2164
     * initiate the drag.
2165
     */
2166
    setHandleElId: function(id) {
2167
        if (typeof id !== "string") {
2168
            id = Dom.generateId(id);
2169
        }
2170
        this.handleElId = id;
2171
        this.DDM.regHandle(this.id, id);
2172
    },
2173

    
2174
    /**
2175
     * Allows you to set an element outside of the linked element as a drag 
2176
     * handle
2177
     * @method setOuterHandleElId
2178
     * @param id the id of the element that will be used to initiate the drag
2179
     */
2180
    setOuterHandleElId: function(id) {
2181
        if (typeof id !== "string") {
2182
            id = Dom.generateId(id);
2183
        }
2184
        Event.on(id, "mousedown", 
2185
                this.handleMouseDown, this, true);
2186
        this.setHandleElId(id);
2187

    
2188
        this.hasOuterHandles = true;
2189
    },
2190

    
2191
    /**
2192
     * Remove all drag and drop hooks for this element
2193
     * @method unreg
2194
     */
2195
    unreg: function() {
2196
        Event.removeListener(this.id, "mousedown", 
2197
                this.handleMouseDown);
2198
        this._domRef = null;
2199
        this.DDM._remove(this);
2200
    },
2201

    
2202
    /**
2203
     * Returns true if this instance is locked, or the drag drop mgr is locked
2204
     * (meaning that all drag/drop is disabled on the page.)
2205
     * @method isLocked
2206
     * @return {boolean} true if this obj or all drag/drop is locked, else 
2207
     * false
2208
     */
2209
    isLocked: function() {
2210
        return (this.DDM.isLocked() || this.locked);
2211
    },
2212

    
2213
    /**
2214
     * Fired when this object is clicked
2215
     * @method handleMouseDown
2216
     * @param {Event} e 
2217
     * @param {YAHOO.util.DragDrop} oDD the clicked dd object (this dd obj)
2218
     * @private
2219
     */
2220
    handleMouseDown: function(e, oDD) {
2221

    
2222
        var button = e.which || e.button;
2223

    
2224
        if (this.primaryButtonOnly && button > 1) {
2225
            return;
2226
        }
2227

    
2228
        if (this.isLocked()) {
2229
            return;
2230
        }
2231

    
2232

    
2233

    
2234
        // firing the mousedown events prior to calculating positions
2235
        var b4Return = this.b4MouseDown(e);
2236
        var mDownReturn = this.onMouseDown(e);
2237

    
2238
        if ((b4Return === false) || (mDownReturn === false)) {
2239
            return;
2240
        }
2241

    
2242
        this.DDM.refreshCache(this.groups);
2243
        // var self = this;
2244
        // setTimeout( function() { self.DDM.refreshCache(self.groups); }, 0);
2245

    
2246
        // Only process the event if we really clicked within the linked 
2247
        // element.  The reason we make this check is that in the case that 
2248
        // another element was moved between the clicked element and the 
2249
        // cursor in the time between the mousedown and mouseup events. When 
2250
        // this happens, the element gets the next mousedown event 
2251
        // regardless of where on the screen it happened.  
2252
        var pt = new YAHOO.util.Point(Event.getPageX(e), Event.getPageY(e));
2253
        if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
2254
        } else {
2255
            if (this.clickValidator(e)) {
2256

    
2257

    
2258
                // set the initial element position
2259
                this.setStartPosition();
2260

    
2261
                // start tracking mousemove distance and mousedown time to
2262
                // determine when to start the actual drag
2263
                this.DDM.handleMouseDown(e, this);
2264

    
2265
                // this mousedown is mine
2266
                this.DDM.stopEvent(e);
2267
            } else {
2268

    
2269

    
2270
            }
2271
        }
2272
    },
2273

    
2274
    /**
2275
     * @method clickValidator
2276
     * @description Method validates that the clicked element
2277
     * was indeed the handle or a valid child of the handle
2278
     * @param {Event} e 
2279
     * @private
2280
     */
2281
    clickValidator: function(e) {
2282
        var target = Event.getTarget(e);
2283
        return ( this.isValidHandleChild(target) &&
2284
                    (this.id == this.handleElId || 
2285
                        this.DDM.handleWasClicked(target, this.id)) );
2286
    },
2287

    
2288
    /**
2289
     * Finds the location the element should be placed if we want to move
2290
     * it to where the mouse location less the click offset would place us.
2291
     * @method getTargetCoord
2292
     * @param {int} iPageX the X coordinate of the click
2293
     * @param {int} iPageY the Y coordinate of the click
2294
     * @return an object that contains the coordinates (Object.x and Object.y)
2295
     * @private
2296
     */
2297
    getTargetCoord: function(iPageX, iPageY) {
2298

    
2299

    
2300
        var x = iPageX - this.deltaX;
2301
        var y = iPageY - this.deltaY;
2302

    
2303
        if (this.constrainX) {
2304
            if (x < this.minX) { x = this.minX; }
2305
            if (x > this.maxX) { x = this.maxX; }
2306
        }
2307

    
2308
        if (this.constrainY) {
2309
            if (y < this.minY) { y = this.minY; }
2310
            if (y > this.maxY) { y = this.maxY; }
2311
        }
2312

    
2313
        x = this.getTick(x, this.xTicks);
2314
        y = this.getTick(y, this.yTicks);
2315

    
2316

    
2317
        return {x:x, y:y};
2318
    },
2319

    
2320
    /**
2321
     * Allows you to specify a tag name that should not start a drag operation
2322
     * when clicked.  This is designed to facilitate embedding links within a
2323
     * drag handle that do something other than start the drag.
2324
     * @method addInvalidHandleType
2325
     * @param {string} tagName the type of element to exclude
2326
     */
2327
    addInvalidHandleType: function(tagName) {
2328
        var type = tagName.toUpperCase();
2329
        this.invalidHandleTypes[type] = type;
2330
    },
2331

    
2332
    /**
2333
     * Lets you to specify an element id for a child of a drag handle
2334
     * that should not initiate a drag
2335
     * @method addInvalidHandleId
2336
     * @param {string} id the element id of the element you wish to ignore
2337
     */
2338
    addInvalidHandleId: function(id) {
2339
        if (typeof id !== "string") {
2340
            id = Dom.generateId(id);
2341
        }
2342
        this.invalidHandleIds[id] = id;
2343
    },
2344

    
2345

    
2346
    /**
2347
     * Lets you specify a css class of elements that will not initiate a drag
2348
     * @method addInvalidHandleClass
2349
     * @param {string} cssClass the class of the elements you wish to ignore
2350
     */
2351
    addInvalidHandleClass: function(cssClass) {
2352
        this.invalidHandleClasses.push(cssClass);
2353
    },
2354

    
2355
    /**
2356
     * Unsets an excluded tag name set by addInvalidHandleType
2357
     * @method removeInvalidHandleType
2358
     * @param {string} tagName the type of element to unexclude
2359
     */
2360
    removeInvalidHandleType: function(tagName) {
2361
        var type = tagName.toUpperCase();
2362
        // this.invalidHandleTypes[type] = null;
2363
        delete this.invalidHandleTypes[type];
2364
    },
2365
    
2366
    /**
2367
     * Unsets an invalid handle id
2368
     * @method removeInvalidHandleId
2369
     * @param {string} id the id of the element to re-enable
2370
     */
2371
    removeInvalidHandleId: function(id) {
2372
        if (typeof id !== "string") {
2373
            id = Dom.generateId(id);
2374
        }
2375
        delete this.invalidHandleIds[id];
2376
    },
2377

    
2378
    /**
2379
     * Unsets an invalid css class
2380
     * @method removeInvalidHandleClass
2381
     * @param {string} cssClass the class of the element(s) you wish to 
2382
     * re-enable
2383
     */
2384
    removeInvalidHandleClass: function(cssClass) {
2385
        for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
2386
            if (this.invalidHandleClasses[i] == cssClass) {
2387
                delete this.invalidHandleClasses[i];
2388
            }
2389
        }
2390
    },
2391

    
2392
    /**
2393
     * Checks the tag exclusion list to see if this click should be ignored
2394
     * @method isValidHandleChild
2395
     * @param {HTMLElement} node the HTMLElement to evaluate
2396
     * @return {boolean} true if this is a valid tag type, false if not
2397
     */
2398
    isValidHandleChild: function(node) {
2399

    
2400
        var valid = true;
2401
        // var n = (node.nodeName == "#text") ? node.parentNode : node;
2402
        var nodeName;
2403
        try {
2404
            nodeName = node.nodeName.toUpperCase();
2405
        } catch(e) {
2406
            nodeName = node.nodeName;
2407
        }
2408
        valid = valid && !this.invalidHandleTypes[nodeName];
2409
        valid = valid && !this.invalidHandleIds[node.id];
2410

    
2411
        for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
2412
            valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
2413
        }
2414

    
2415

    
2416
        return valid;
2417

    
2418
    },
2419

    
2420
    /**
2421
     * Create the array of horizontal tick marks if an interval was specified
2422
     * in setXConstraint().
2423
     * @method setXTicks
2424
     * @private
2425
     */
2426
    setXTicks: function(iStartX, iTickSize) {
2427
        this.xTicks = [];
2428
        this.xTickSize = iTickSize;
2429
        
2430
        var tickMap = {};
2431

    
2432
        for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
2433
            if (!tickMap[i]) {
2434
                this.xTicks[this.xTicks.length] = i;
2435
                tickMap[i] = true;
2436
            }
2437
        }
2438

    
2439
        for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
2440
            if (!tickMap[i]) {
2441
                this.xTicks[this.xTicks.length] = i;
2442
                tickMap[i] = true;
2443
            }
2444
        }
2445

    
2446
        this.xTicks.sort(this.DDM.numericSort) ;
2447
    },
2448

    
2449
    /**
2450
     * Create the array of vertical tick marks if an interval was specified in 
2451
     * setYConstraint().
2452
     * @method setYTicks
2453
     * @private
2454
     */
2455
    setYTicks: function(iStartY, iTickSize) {
2456
        this.yTicks = [];
2457
        this.yTickSize = iTickSize;
2458

    
2459
        var tickMap = {};
2460

    
2461
        for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
2462
            if (!tickMap[i]) {
2463
                this.yTicks[this.yTicks.length] = i;
2464
                tickMap[i] = true;
2465
            }
2466
        }
2467

    
2468
        for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
2469
            if (!tickMap[i]) {
2470
                this.yTicks[this.yTicks.length] = i;
2471
                tickMap[i] = true;
2472
            }
2473
        }
2474

    
2475
        this.yTicks.sort(this.DDM.numericSort) ;
2476
    },
2477

    
2478
    /**
2479
     * By default, the element can be dragged any place on the screen.  Use 
2480
     * this method to limit the horizontal travel of the element.  Pass in 
2481
     * 0,0 for the parameters if you want to lock the drag to the y axis.
2482
     * @method setXConstraint
2483
     * @param {int} iLeft the number of pixels the element can move to the left
2484
     * @param {int} iRight the number of pixels the element can move to the 
2485
     * right
2486
     * @param {int} iTickSize optional parameter for specifying that the 
2487
     * element
2488
     * should move iTickSize pixels at a time.
2489
     */
2490
    setXConstraint: function(iLeft, iRight, iTickSize) {
2491
        this.leftConstraint = parseInt(iLeft, 10);
2492
        this.rightConstraint = parseInt(iRight, 10);
2493

    
2494
        this.minX = this.initPageX - this.leftConstraint;
2495
        this.maxX = this.initPageX + this.rightConstraint;
2496
        if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
2497

    
2498
        this.constrainX = true;
2499
    },
2500

    
2501
    /**
2502
     * Clears any constraints applied to this instance.  Also clears ticks
2503
     * since they can't exist independent of a constraint at this time.
2504
     * @method clearConstraints
2505
     */
2506
    clearConstraints: function() {
2507
        this.constrainX = false;
2508
        this.constrainY = false;
2509
        this.clearTicks();
2510
    },
2511

    
2512
    /**
2513
     * Clears any tick interval defined for this instance
2514
     * @method clearTicks
2515
     */
2516
    clearTicks: function() {
2517
        this.xTicks = null;
2518
        this.yTicks = null;
2519
        this.xTickSize = 0;
2520
        this.yTickSize = 0;
2521
    },
2522

    
2523
    /**
2524
     * By default, the element can be dragged any place on the screen.  Set 
2525
     * this to limit the vertical travel of the element.  Pass in 0,0 for the
2526
     * parameters if you want to lock the drag to the x axis.
2527
     * @method setYConstraint
2528
     * @param {int} iUp the number of pixels the element can move up
2529
     * @param {int} iDown the number of pixels the element can move down
2530
     * @param {int} iTickSize optional parameter for specifying that the 
2531
     * element should move iTickSize pixels at a time.
2532
     */
2533
    setYConstraint: function(iUp, iDown, iTickSize) {
2534
        this.topConstraint = parseInt(iUp, 10);
2535
        this.bottomConstraint = parseInt(iDown, 10);
2536

    
2537
        this.minY = this.initPageY - this.topConstraint;
2538
        this.maxY = this.initPageY + this.bottomConstraint;
2539
        if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
2540

    
2541
        this.constrainY = true;
2542
        
2543
    },
2544

    
2545
    /**
2546
     * resetConstraints must be called if you manually reposition a dd element.
2547
     * @method resetConstraints
2548
     */
2549
    resetConstraints: function() {
2550

    
2551

    
2552
        // Maintain offsets if necessary
2553
        if (this.initPageX || this.initPageX === 0) {
2554
            // figure out how much this thing has moved
2555
            var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
2556
            var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
2557

    
2558
            this.setInitPosition(dx, dy);
2559

    
2560
        // This is the first time we have detected the element's position
2561
        } else {
2562
            this.setInitPosition();
2563
        }
2564

    
2565
        if (this.constrainX) {
2566
            this.setXConstraint( this.leftConstraint, 
2567
                                 this.rightConstraint, 
2568
                                 this.xTickSize        );
2569
        }
2570

    
2571
        if (this.constrainY) {
2572
            this.setYConstraint( this.topConstraint, 
2573
                                 this.bottomConstraint, 
2574
                                 this.yTickSize         );
2575
        }
2576
    },
2577

    
2578
    /**
2579
     * Normally the drag element is moved pixel by pixel, but we can specify 
2580
     * that it move a number of pixels at a time.  This method resolves the 
2581
     * location when we have it set up like this.
2582
     * @method getTick
2583
     * @param {int} val where we want to place the object
2584
     * @param {int[]} tickArray sorted array of valid points
2585
     * @return {int} the closest tick
2586
     * @private
2587
     */
2588
    getTick: function(val, tickArray) {
2589

    
2590
        if (!tickArray) {
2591
            // If tick interval is not defined, it is effectively 1 pixel, 
2592
            // so we return the value passed to us.
2593
            return val; 
2594
        } else if (tickArray[0] >= val) {
2595
            // The value is lower than the first tick, so we return the first
2596
            // tick.
2597
            return tickArray[0];
2598
        } else {
2599
            for (var i=0, len=tickArray.length; i<len; ++i) {
2600
                var next = i + 1;
2601
                if (tickArray[next] && tickArray[next] >= val) {
2602
                    var diff1 = val - tickArray[i];
2603
                    var diff2 = tickArray[next] - val;
2604
                    return (diff2 > diff1) ? tickArray[i] : tickArray[next];
2605
                }
2606
            }
2607

    
2608
            // The value is larger than the last tick, so we return the last
2609
            // tick.
2610
            return tickArray[tickArray.length - 1];
2611
        }
2612
    },
2613

    
2614
    /**
2615
     * toString method
2616
     * @method toString
2617
     * @return {string} string representation of the dd obj
2618
     */
2619
    toString: function() {
2620
        return ("DragDrop " + this.id);
2621
    }
2622

    
2623
};
2624

    
2625
})();
2626
/**
2627
 * A DragDrop implementation where the linked element follows the 
2628
 * mouse cursor during a drag.
2629
 * @class DD
2630
 * @extends YAHOO.util.DragDrop
2631
 * @constructor
2632
 * @param {String} id the id of the linked element 
2633
 * @param {String} sGroup the group of related DragDrop items
2634
 * @param {object} config an object containing configurable attributes
2635
 *                Valid properties for DD: 
2636
 *                    scroll
2637
 */
2638
YAHOO.util.DD = function(id, sGroup, config) {
2639
    if (id) {
2640
        this.init(id, sGroup, config);
2641
    }
2642
};
2643

    
2644
YAHOO.extend(YAHOO.util.DD, YAHOO.util.DragDrop, {
2645

    
2646
    /**
2647
     * When set to true, the utility automatically tries to scroll the browser
2648
     * window wehn a drag and drop element is dragged near the viewport boundary.
2649
     * Defaults to true.
2650
     * @property scroll
2651
     * @type boolean
2652
     */
2653
    scroll: true, 
2654

    
2655
    /**
2656
     * Sets the pointer offset to the distance between the linked element's top 
2657
     * left corner and the location the element was clicked
2658
     * @method autoOffset
2659
     * @param {int} iPageX the X coordinate of the click
2660
     * @param {int} iPageY the Y coordinate of the click
2661
     */
2662
    autoOffset: function(iPageX, iPageY) {
2663
        var x = iPageX - this.startPageX;
2664
        var y = iPageY - this.startPageY;
2665
        this.setDelta(x, y);
2666
    },
2667

    
2668
    /** 
2669
     * Sets the pointer offset.  You can call this directly to force the 
2670
     * offset to be in a particular location (e.g., pass in 0,0 to set it 
2671
     * to the center of the object, as done in YAHOO.widget.Slider)
2672
     * @method setDelta
2673
     * @param {int} iDeltaX the distance from the left
2674
     * @param {int} iDeltaY the distance from the top
2675
     */
2676
    setDelta: function(iDeltaX, iDeltaY) {
2677
        this.deltaX = iDeltaX;
2678
        this.deltaY = iDeltaY;
2679
    },
2680

    
2681
    /**
2682
     * Sets the drag element to the location of the mousedown or click event, 
2683
     * maintaining the cursor location relative to the location on the element 
2684
     * that was clicked.  Override this if you want to place the element in a 
2685
     * location other than where the cursor is.
2686
     * @method setDragElPos
2687
     * @param {int} iPageX the X coordinate of the mousedown or drag event
2688
     * @param {int} iPageY the Y coordinate of the mousedown or drag event
2689
     */
2690
    setDragElPos: function(iPageX, iPageY) {
2691
        // the first time we do this, we are going to check to make sure
2692
        // the element has css positioning
2693

    
2694
        var el = this.getDragEl();
2695
        this.alignElWithMouse(el, iPageX, iPageY);
2696
    },
2697

    
2698
    /**
2699
     * Sets the element to the location of the mousedown or click event, 
2700
     * maintaining the cursor location relative to the location on the element 
2701
     * that was clicked.  Override this if you want to place the element in a 
2702
     * location other than where the cursor is.
2703
     * @method alignElWithMouse
2704
     * @param {HTMLElement} el the element to move
2705
     * @param {int} iPageX the X coordinate of the mousedown or drag event
2706
     * @param {int} iPageY the Y coordinate of the mousedown or drag event
2707
     */
2708
    alignElWithMouse: function(el, iPageX, iPageY) {
2709
        var oCoord = this.getTargetCoord(iPageX, iPageY);
2710

    
2711
        if (!this.deltaSetXY) {
2712
            var aCoord = [oCoord.x, oCoord.y];
2713
            YAHOO.util.Dom.setXY(el, aCoord);
2714
            var newLeft = parseInt( YAHOO.util.Dom.getStyle(el, "left"), 10 );
2715
            var newTop  = parseInt( YAHOO.util.Dom.getStyle(el, "top" ), 10 );
2716

    
2717
            this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2718
        } else {
2719
            YAHOO.util.Dom.setStyle(el, "left", (oCoord.x + this.deltaSetXY[0]) + "px");
2720
            YAHOO.util.Dom.setStyle(el, "top",  (oCoord.y + this.deltaSetXY[1]) + "px");
2721
            //el.style.left = (oCoord.x + this.deltaSetXY[0]) + "px";
2722
            //el.style.top = (oCoord.y + this.deltaSetXY[1]) + "px";
2723
        }
2724
        
2725
        this.cachePosition(oCoord.x, oCoord.y);
2726
        //DAV
2727
        var self = this;
2728
        setTimeout(function() {
2729
            self.autoScroll.call(self, oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2730
        }, 0);
2731
    },
2732

    
2733
    /**
2734
     * Saves the most recent position so that we can reset the constraints and
2735
     * tick marks on-demand.  We need to know this so that we can calculate the
2736
     * number of pixels the element is offset from its original position.
2737
     * @method cachePosition
2738
     * @param iPageX the current x position (optional, this just makes it so we
2739
     * don't have to look it up again)
2740
     * @param iPageY the current y position (optional, this just makes it so we
2741
     * don't have to look it up again)
2742
     */
2743
    cachePosition: function(iPageX, iPageY) {
2744
        if (iPageX) {
2745
            this.lastPageX = iPageX;
2746
            this.lastPageY = iPageY;
2747
        } else {
2748
            var aCoord = YAHOO.util.Dom.getXY(this.getEl());
2749
            this.lastPageX = aCoord[0];
2750
            this.lastPageY = aCoord[1];
2751
        }
2752
    },
2753

    
2754
    /**
2755
     * Auto-scroll the window if the dragged object has been moved beyond the 
2756
     * visible window boundary.
2757
     * @method autoScroll
2758
     * @param {int} x the drag element's x position
2759
     * @param {int} y the drag element's y position
2760
     * @param {int} h the height of the drag element
2761
     * @param {int} w the width of the drag element
2762
     * @private
2763
     */
2764
    autoScroll: function(x, y, h, w) {
2765

    
2766
        if (this.scroll) {
2767
            // The client height
2768
            var clientH = this.DDM.getClientHeight();
2769

    
2770
            // The client width
2771
            var clientW = this.DDM.getClientWidth();
2772

    
2773
            // The amt scrolled down
2774
            var st = this.DDM.getScrollTop();
2775

    
2776
            // The amt scrolled right
2777
            var sl = this.DDM.getScrollLeft();
2778

    
2779
            // Location of the bottom of the element
2780
            var bot = h + y;
2781

    
2782
            // Location of the right of the element
2783
            var right = w + x;
2784

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

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

    
2793

    
2794
            // How close to the edge the cursor must be before we scroll
2795
            // var thresh = (document.all) ? 100 : 40;
2796
            var thresh = 40;
2797

    
2798
            // How many pixels to scroll per autoscroll op.  This helps to reduce 
2799
            // clunky scrolling. IE is more sensitive about this ... it needs this 
2800
            // value to be higher.
2801
            var scrAmt = (document.all) ? 80 : 30;
2802

    
2803
            // Scroll down if we are near the bottom of the visible page and the 
2804
            // obj extends below the crease
2805
            if ( bot > clientH && toBot < thresh ) { 
2806
                window.scrollTo(sl, st + scrAmt); 
2807
            }
2808

    
2809
            // Scroll up if the window is scrolled down and the top of the object
2810
            // goes above the top border
2811
            if ( y < st && st > 0 && y - st < thresh ) { 
2812
                window.scrollTo(sl, st - scrAmt); 
2813
            }
2814

    
2815
            // Scroll right if the obj is beyond the right border and the cursor is
2816
            // near the border.
2817
            if ( right > clientW && toRight < thresh ) { 
2818
                window.scrollTo(sl + scrAmt, st); 
2819
            }
2820

    
2821
            // Scroll left if the window has been scrolled to the right and the obj
2822
            // extends past the left border
2823
            if ( x < sl && sl > 0 && x - sl < thresh ) { 
2824
                window.scrollTo(sl - scrAmt, st);
2825
            }
2826
        }
2827
    },
2828

    
2829
    /*
2830
     * Sets up config options specific to this class. Overrides
2831
     * YAHOO.util.DragDrop, but all versions of this method through the 
2832
     * inheritance chain are called
2833
     */
2834
    applyConfig: function() {
2835
        YAHOO.util.DD.superclass.applyConfig.call(this);
2836
        this.scroll = (this.config.scroll !== false);
2837
    },
2838

    
2839
    /*
2840
     * Event that fires prior to the onMouseDown event.  Overrides 
2841
     * YAHOO.util.DragDrop.
2842
     */
2843
    b4MouseDown: function(e) {
2844
        this.setStartPosition();
2845
        // this.resetConstraints();
2846
        this.autoOffset(YAHOO.util.Event.getPageX(e), 
2847
                            YAHOO.util.Event.getPageY(e));
2848
    },
2849

    
2850
    /*
2851
     * Event that fires prior to the onDrag event.  Overrides 
2852
     * YAHOO.util.DragDrop.
2853
     */
2854
    b4Drag: function(e) {
2855
        this.setDragElPos(YAHOO.util.Event.getPageX(e), 
2856
                            YAHOO.util.Event.getPageY(e));
2857
    },
2858

    
2859
    toString: function() {
2860
        return ("DD " + this.id);
2861
    }
2862

    
2863
    //////////////////////////////////////////////////////////////////////////
2864
    // Debugging ygDragDrop events that can be overridden
2865
    //////////////////////////////////////////////////////////////////////////
2866
    /*
2867
    startDrag: function(x, y) {
2868
    },
2869

    
2870
    onDrag: function(e) {
2871
    },
2872

    
2873
    onDragEnter: function(e, id) {
2874
    },
2875

    
2876
    onDragOver: function(e, id) {
2877
    },
2878

    
2879
    onDragOut: function(e, id) {
2880
    },
2881

    
2882
    onDragDrop: function(e, id) {
2883
    },
2884

    
2885
    endDrag: function(e) {
2886
    }
2887

    
2888
    */
2889

    
2890
});
2891
/**
2892
 * A DragDrop implementation that inserts an empty, bordered div into
2893
 * the document that follows the cursor during drag operations.  At the time of
2894
 * the click, the frame div is resized to the dimensions of the linked html
2895
 * element, and moved to the exact location of the linked element.
2896
 *
2897
 * References to the "frame" element refer to the single proxy element that
2898
 * was created to be dragged in place of all DDProxy elements on the
2899
 * page.
2900
 *
2901
 * @class DDProxy
2902
 * @extends YAHOO.util.DD
2903
 * @constructor
2904
 * @param {String} id the id of the linked html element
2905
 * @param {String} sGroup the group of related DragDrop objects
2906
 * @param {object} config an object containing configurable attributes
2907
 *                Valid properties for DDProxy in addition to those in DragDrop: 
2908
 *                   resizeFrame, centerFrame, dragElId
2909
 */
2910
YAHOO.util.DDProxy = function(id, sGroup, config) {
2911
    if (id) {
2912
        this.init(id, sGroup, config);
2913
        this.initFrame(); 
2914
    }
2915
};
2916

    
2917
/**
2918
 * The default drag frame div id
2919
 * @property YAHOO.util.DDProxy.dragElId
2920
 * @type String
2921
 * @static
2922
 */
2923
YAHOO.util.DDProxy.dragElId = "ygddfdiv";
2924

    
2925
YAHOO.extend(YAHOO.util.DDProxy, YAHOO.util.DD, {
2926

    
2927
    /**
2928
     * By default we resize the drag frame to be the same size as the element
2929
     * we want to drag (this is to get the frame effect).  We can turn it off
2930
     * if we want a different behavior.
2931
     * @property resizeFrame
2932
     * @type boolean
2933
     */
2934
    resizeFrame: true,
2935

    
2936
    /**
2937
     * By default the frame is positioned exactly where the drag element is, so
2938
     * we use the cursor offset provided by YAHOO.util.DD.  Another option that works only if
2939
     * you do not have constraints on the obj is to have the drag frame centered
2940
     * around the cursor.  Set centerFrame to true for this effect.
2941
     * @property centerFrame
2942
     * @type boolean
2943
     */
2944
    centerFrame: false,
2945

    
2946
    /**
2947
     * Creates the proxy element if it does not yet exist
2948
     * @method createFrame
2949
     */
2950
    createFrame: function() {
2951
        var self=this, body=document.body;
2952

    
2953
        if (!body || !body.firstChild) {
2954
            setTimeout( function() { self.createFrame(); }, 50 );
2955
            return;
2956
        }
2957

    
2958
        var div=this.getDragEl(), Dom=YAHOO.util.Dom;
2959

    
2960
        if (!div) {
2961
            div    = document.createElement("div");
2962
            div.id = this.dragElId;
2963
            var s  = div.style;
2964

    
2965
            s.position   = "absolute";
2966
            s.visibility = "hidden";
2967
            s.cursor     = "move";
2968
            s.border     = "2px solid #aaa";
2969
            s.zIndex     = 999;
2970
            s.height     = "25px";
2971
            s.width      = "25px";
2972

    
2973
            var _data = document.createElement('div');
2974
            Dom.setStyle(_data, 'height', '100%');
2975
            Dom.setStyle(_data, 'width', '100%');
2976
            /**
2977
            * If the proxy element has no background-color, then it is considered to the "transparent" by Internet Explorer.
2978
            * Since it is "transparent" then the events pass through it to the iframe below.
2979
            * So creating a "fake" div inside the proxy element and giving it a background-color, then setting it to an
2980
            * opacity of 0, it appears to not be there, however IE still thinks that it is so the events never pass through.
2981
            */
2982
            Dom.setStyle(_data, 'background-color', '#ccc');
2983
            Dom.setStyle(_data, 'opacity', '0');
2984
            div.appendChild(_data);
2985

    
2986
            // appendChild can blow up IE if invoked prior to the window load event
2987
            // while rendering a table.  It is possible there are other scenarios 
2988
            // that would cause this to happen as well.
2989
            body.insertBefore(div, body.firstChild);
2990
        }
2991
    },
2992

    
2993
    /**
2994
     * Initialization for the drag frame element.  Must be called in the
2995
     * constructor of all subclasses
2996
     * @method initFrame
2997
     */
2998
    initFrame: function() {
2999
        this.createFrame();
3000
    },
3001

    
3002
    applyConfig: function() {
3003
        YAHOO.util.DDProxy.superclass.applyConfig.call(this);
3004

    
3005
        this.resizeFrame = (this.config.resizeFrame !== false);
3006
        this.centerFrame = (this.config.centerFrame);
3007
        this.setDragElId(this.config.dragElId || YAHOO.util.DDProxy.dragElId);
3008
    },
3009

    
3010
    /**
3011
     * Resizes the drag frame to the dimensions of the clicked object, positions 
3012
     * it over the object, and finally displays it
3013
     * @method showFrame
3014
     * @param {int} iPageX X click position
3015
     * @param {int} iPageY Y click position
3016
     * @private
3017
     */
3018
    showFrame: function(iPageX, iPageY) {
3019
        var el = this.getEl();
3020
        var dragEl = this.getDragEl();
3021
        var s = dragEl.style;
3022

    
3023
        this._resizeProxy();
3024

    
3025
        if (this.centerFrame) {
3026
            this.setDelta( Math.round(parseInt(s.width,  10)/2), 
3027
                           Math.round(parseInt(s.height, 10)/2) );
3028
        }
3029

    
3030
        this.setDragElPos(iPageX, iPageY);
3031

    
3032
        YAHOO.util.Dom.setStyle(dragEl, "visibility", "visible"); 
3033
    },
3034

    
3035
    /**
3036
     * The proxy is automatically resized to the dimensions of the linked
3037
     * element when a drag is initiated, unless resizeFrame is set to false
3038
     * @method _resizeProxy
3039
     * @private
3040
     */
3041
    _resizeProxy: function() {
3042
        if (this.resizeFrame) {
3043
            var DOM    = YAHOO.util.Dom;
3044
            var el     = this.getEl();
3045
            var dragEl = this.getDragEl();
3046

    
3047
            var bt = parseInt( DOM.getStyle(dragEl, "borderTopWidth"    ), 10);
3048
            var br = parseInt( DOM.getStyle(dragEl, "borderRightWidth"  ), 10);
3049
            var bb = parseInt( DOM.getStyle(dragEl, "borderBottomWidth" ), 10);
3050
            var bl = parseInt( DOM.getStyle(dragEl, "borderLeftWidth"   ), 10);
3051

    
3052
            if (isNaN(bt)) { bt = 0; }
3053
            if (isNaN(br)) { br = 0; }
3054
            if (isNaN(bb)) { bb = 0; }
3055
            if (isNaN(bl)) { bl = 0; }
3056

    
3057

    
3058
            var newWidth  = Math.max(0, el.offsetWidth  - br - bl);                                                                                           
3059
            var newHeight = Math.max(0, el.offsetHeight - bt - bb);
3060

    
3061

    
3062
            DOM.setStyle( dragEl, "width",  newWidth  + "px" );
3063
            DOM.setStyle( dragEl, "height", newHeight + "px" );
3064
        }
3065
    },
3066

    
3067
    // overrides YAHOO.util.DragDrop
3068
    b4MouseDown: function(e) {
3069
        this.setStartPosition();
3070
        var x = YAHOO.util.Event.getPageX(e);
3071
        var y = YAHOO.util.Event.getPageY(e);
3072
        this.autoOffset(x, y);
3073

    
3074
        // This causes the autoscroll code to kick off, which means autoscroll can
3075
        // happen prior to the check for a valid drag handle.
3076
        // this.setDragElPos(x, y);
3077
    },
3078

    
3079
    // overrides YAHOO.util.DragDrop
3080
    b4StartDrag: function(x, y) {
3081
        // show the drag frame
3082
        this.showFrame(x, y);
3083
    },
3084

    
3085
    // overrides YAHOO.util.DragDrop
3086
    b4EndDrag: function(e) {
3087
        YAHOO.util.Dom.setStyle(this.getDragEl(), "visibility", "hidden"); 
3088
    },
3089

    
3090
    // overrides YAHOO.util.DragDrop
3091
    // By default we try to move the element to the last location of the frame.  
3092
    // This is so that the default behavior mirrors that of YAHOO.util.DD.  
3093
    endDrag: function(e) {
3094
        var DOM = YAHOO.util.Dom;
3095
        var lel = this.getEl();
3096
        var del = this.getDragEl();
3097

    
3098
        // Show the drag frame briefly so we can get its position
3099
        // del.style.visibility = "";
3100
        DOM.setStyle(del, "visibility", ""); 
3101

    
3102
        // Hide the linked element before the move to get around a Safari 
3103
        // rendering bug.
3104
        //lel.style.visibility = "hidden";
3105
        DOM.setStyle(lel, "visibility", "hidden"); 
3106
        YAHOO.util.DDM.moveToEl(lel, del);
3107
        //del.style.visibility = "hidden";
3108
        DOM.setStyle(del, "visibility", "hidden"); 
3109
        //lel.style.visibility = "";
3110
        DOM.setStyle(lel, "visibility", ""); 
3111
    },
3112

    
3113
    toString: function() {
3114
        return ("DDProxy " + this.id);
3115
    }
3116

    
3117
});
3118
/**
3119
 * A DragDrop implementation that does not move, but can be a drop 
3120
 * target.  You would get the same result by simply omitting implementation 
3121
 * for the event callbacks, but this way we reduce the processing cost of the 
3122
 * event listener and the callbacks.
3123
 * @class DDTarget
3124
 * @extends YAHOO.util.DragDrop 
3125
 * @constructor
3126
 * @param {String} id the id of the element that is a drop target
3127
 * @param {String} sGroup the group of related DragDrop objects
3128
 * @param {object} config an object containing configurable attributes
3129
 *                 Valid properties for DDTarget in addition to those in 
3130
 *                 DragDrop: 
3131
 *                    none
3132
 */
3133
YAHOO.util.DDTarget = function(id, sGroup, config) {
3134
    if (id) {
3135
        this.initTarget(id, sGroup, config);
3136
    }
3137
};
3138

    
3139
// YAHOO.util.DDTarget.prototype = new YAHOO.util.DragDrop();
3140
YAHOO.extend(YAHOO.util.DDTarget, YAHOO.util.DragDrop, {
3141
    toString: function() {
3142
        return ("DDTarget " + this.id);
3143
    }
3144
});
3145
YAHOO.register("dragdrop", YAHOO.util.DragDropMgr, {version: "2.4.1", build: "742"});
(4-4/4)