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