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