]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/dd/DragDrop.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / dd / DragDrop.js
CommitLineData
6527f429
DM
1/*\r
2 * This is a derivative of the similarly named class in the YUI Library.\r
3 * The original license:\r
4 * Copyright (c) 2006, Yahoo! Inc. All rights reserved.\r
5 * Code licensed under the BSD License:\r
6 * http://developer.yahoo.net/yui/license.txt\r
7 */\r
8\r
9\r
10/**\r
11 * Defines the interface and base operation of items that that can be\r
12 * dragged or can be drop targets. It was designed to be extended, overriding\r
13 * the event handlers for startDrag, onDrag, onDragOver and onDragOut.\r
14 * Up to three html elements can be associated with a DragDrop instance:\r
15 *\r
16 * - linked element: the element that is passed into the constructor.\r
17 * This is the element which defines the boundaries for interaction with\r
18 * other DragDrop objects.\r
19 *\r
20 * - handle element(s): The drag operation only occurs if the element that\r
21 * was clicked matches a handle element. By default this is the linked\r
22 * element, but there are times that you will want only a portion of the\r
23 * linked element to initiate the drag operation, and the setHandleElId()\r
24 * method provides a way to define this.\r
25 *\r
26 * - drag element: this represents the element that would be moved along\r
27 * with the cursor during a drag operation. By default, this is the linked\r
28 * element itself as in {@link Ext.dd.DD}. setDragElId() lets you define\r
29 * a separate element that would be moved, as in {@link Ext.dd.DDProxy}.\r
30 *\r
31 * This class should not be instantiated until the onload event to ensure that\r
32 * the associated elements are available.\r
33 * The following would define a DragDrop obj that would interact with any\r
34 * other DragDrop obj in the "group1" group:\r
35 *\r
36 * dd = new Ext.dd.DragDrop("div1", "group1");\r
37 *\r
38 * Since none of the event handlers have been implemented, nothing would\r
39 * actually happen if you were to run the code above. Normally you would\r
40 * override this class or one of the default implementations, but you can\r
41 * also override the methods you want on an instance of the class...\r
42 *\r
43 * dd.onDragDrop = function(e, id) {\r
44 * alert("dd was dropped on " + id);\r
45 * }\r
46 *\r
47 */\r
48Ext.define('Ext.dd.DragDrop', {\r
49 requires: ['Ext.dd.DragDropManager'],\r
50\r
51 /**\r
52 * Creates new DragDrop.\r
53 * @param {String} id of the element that is linked to this instance\r
54 * @param {String} sGroup the group of related DragDrop objects\r
55 * @param {Object} config an object containing configurable attributes.\r
56 * Valid properties for DragDrop:\r
57 *\r
58 * - padding\r
59 * - isTarget\r
60 * - maintainOffset\r
61 * - primaryButtonOnly\r
62 */\r
63 constructor: function(id, sGroup, config) {\r
64 if(id) {\r
65 this.init(id, sGroup, config);\r
66 }\r
67 },\r
68\r
69 /**\r
70 * @property {Boolean} ignoreSelf\r
71 * Set to false to enable a DragDrop object to fire drag events while dragging\r
72 * over its own Element. Defaults to true - DragDrop objects do not by default\r
73 * fire drag events to themselves.\r
74 */\r
75\r
76 /**\r
77 * @property {String} id\r
78 * The id of the element associated with this object. This is what we\r
79 * refer to as the "linked element" because the size and position of\r
80 * this element is used to determine when the drag and drop objects have\r
81 * interacted.\r
82 */\r
83 id: null,\r
84\r
85 /**\r
86 * @property {Object} config\r
87 * Configuration attributes passed into the constructor\r
88 */\r
89 config: null,\r
90\r
91 /**\r
92 * @property {String} dragElId\r
93 * The id of the element that will be dragged. By default this is same\r
94 * as the linked element, but could be changed to another element. Ex:\r
95 * Ext.dd.DDProxy\r
96 * @private\r
97 */\r
98 dragElId: null,\r
99\r
100 /**\r
101 * @property {String} handleElId\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 * @private\r
107 */\r
108 handleElId: null,\r
109\r
110 /**\r
111 * @property {Object} invalidHandleTypes\r
112 * An object who's property names identify HTML tags to be considered invalid as drag handles.\r
113 * A non-null property value identifies the tag as invalid. Defaults to the\r
114 * following value which prevents drag operations from being initiated by `<a>` elements:\r
115 *\r
116 * {\r
117 * A: "A"\r
118 * }\r
119 */\r
120 invalidHandleTypes: null,\r
121\r
122 /**\r
123 * @property {Object} invalidHandleIds\r
124 * An object who's property names identify the IDs of elements to be considered invalid as drag handles.\r
125 * A non-null property value identifies the ID as invalid. For example, to prevent\r
126 * dragging from being initiated on element ID "foo", use:\r
127 *\r
128 * {\r
129 * foo: true\r
130 * }\r
131 */\r
132 invalidHandleIds: null,\r
133\r
134 /**\r
135 * @property {String[]} invalidHandleClasses\r
136 * An Array of CSS class names for elements to be considered in valid as drag handles.\r
137 */\r
138 invalidHandleClasses: null,\r
139\r
140 /**\r
141 * @property {Number} startPageX\r
142 * The linked element's absolute X position at the time the drag was\r
143 * started\r
144 * @private\r
145 */\r
146 startPageX: 0,\r
147\r
148 /**\r
149 * @property {Number} startPageY\r
150 * The linked element's absolute X position at the time the drag was\r
151 * started\r
152 * @private\r
153 */\r
154 startPageY: 0,\r
155\r
156 /**\r
157 * @property {Object} groups\r
158 * The group defines a logical collection of DragDrop objects that are\r
159 * related. Instances only get events when interacting with other\r
160 * DragDrop object in the same group. This lets us define multiple\r
161 * groups using a single DragDrop subclass if we want.\r
162 *\r
163 * An object in the format {'group1':true, 'group2':true}\r
164 */\r
165 groups: null,\r
166\r
167 /**\r
168 * @property {Boolean} locked\r
169 * Individual drag/drop instances can be locked. This will prevent\r
170 * onmousedown start drag.\r
171 * @private\r
172 */\r
173 locked: false,\r
174\r
175 /**\r
176 * Locks this instance\r
177 */\r
178 lock: function() {\r
179 this.locked = true;\r
180 },\r
181\r
182 /**\r
183 * @property {Boolean} moveOnly\r
184 * When set to true, other DD objects in cooperating DDGroups do not receive\r
185 * notification events when this DD object is dragged over them.\r
186 */\r
187 moveOnly: false,\r
188\r
189 /**\r
190 * Unlocks this instace\r
191 */\r
192 unlock: function() {\r
193 this.locked = false;\r
194 },\r
195\r
196 /**\r
197 * @property {Boolean} isTarget\r
198 * By default, all instances can be a drop target. This can be disabled by\r
199 * setting isTarget to false.\r
200 */\r
201 isTarget: true,\r
202\r
203 /**\r
204 * @property {Number[]} padding\r
205 * The padding configured for this drag and drop object for calculating\r
206 * the drop zone intersection with this object.\r
207 * An array containing the 4 padding values: [top, right, bottom, left]\r
208 */\r
209 padding: null,\r
210\r
211 /**\r
212 * @property _domRef\r
213 * Cached reference to the linked element\r
214 * @private\r
215 */\r
216 _domRef: null,\r
217\r
218 /**\r
219 * @property __ygDragDrop\r
220 * Internal typeof flag\r
221 * @private\r
222 */\r
223 __ygDragDrop: true,\r
224\r
225 /**\r
226 * @property {Boolean} constrainX\r
227 * Set to true when horizontal contraints are applied\r
228 * @private\r
229 */\r
230 constrainX: false,\r
231\r
232 /**\r
233 * @property {Boolean} constrainY\r
234 * Set to true when vertical contraints are applied\r
235 * @private\r
236 */\r
237 constrainY: false,\r
238\r
239 /**\r
240 * @property {Number} minX\r
241 * The left constraint\r
242 * @private\r
243 */\r
244 minX: 0,\r
245\r
246 /**\r
247 * @property {Number} maxX\r
248 * The right constraint\r
249 * @private\r
250 */\r
251 maxX: 0,\r
252\r
253 /**\r
254 * @property {Number} minY\r
255 * The up constraint\r
256 * @private\r
257 */\r
258 minY: 0,\r
259\r
260 /**\r
261 * @property {Number} maxY\r
262 * The down constraint\r
263 * @private\r
264 */\r
265 maxY: 0,\r
266\r
267 /**\r
268 * @property {Boolean} maintainOffset\r
269 * Maintain offsets when we resetconstraints. Set to true when you want\r
270 * the position of the element relative to its parent to stay the same\r
271 * when the page changes\r
272 */\r
273 maintainOffset: false,\r
274\r
275 /**\r
276 * @property {Number[]} xTicks\r
277 * Array of pixel locations the element will snap to if we specified a\r
278 * horizontal graduation/interval. This array is generated automatically\r
279 * when you define a tick interval.\r
280 */\r
281 xTicks: null,\r
282\r
283 /**\r
284 * @property {Number[]} yTicks\r
285 * Array of pixel locations the element will snap to if we specified a\r
286 * vertical graduation/interval. This array is generated automatically\r
287 * when you define a tick interval.\r
288 */\r
289 yTicks: null,\r
290\r
291 /**\r
292 * @property {Boolean} primaryButtonOnly\r
293 * By default the drag and drop instance will only respond to the primary\r
294 * button click (left button for a right-handed mouse). Set to true to\r
295 * allow drag and drop to start with any mouse click that is propogated\r
296 * by the browser\r
297 */\r
298 primaryButtonOnly: true,\r
299\r
300 /**\r
301 * @property {Boolean} available\r
302 * The available property is false until the linked dom element is accessible.\r
303 */\r
304 available: false,\r
305\r
306 /**\r
307 * @property {Boolean} hasOuterHandles\r
308 * By default, drags can only be initiated if the mousedown occurs in the\r
309 * region the linked element is. This is done in part to work around a\r
310 * bug in some browsers that mis-report the mousedown if the previous\r
311 * mouseup happened outside of the window. This property is set to true\r
312 * if outer handles are defined. Defaults to false.\r
313 */\r
314 hasOuterHandles: false,\r
315\r
316 triggerEvent: 'mousedown',\r
317\r
318 /**\r
319 * Code that executes immediately before the startDrag event\r
320 * @private\r
321 */\r
322 b4StartDrag: function(x, y) { },\r
323\r
324 /**\r
325 * Abstract method called after a drag/drop object is clicked\r
326 * and the drag or mousedown time thresholds have beeen met.\r
327 * @param {Number} x X click location\r
328 * @param {Number} y Y click location\r
329 */\r
330 startDrag: function(x, y) { /* override this */ },\r
331\r
332 /**\r
333 * Code that executes immediately before the onDrag event\r
334 * @private\r
335 */\r
336 b4Drag: function(e) { },\r
337\r
338 /**\r
339 * Abstract method called during the onMouseMove event while dragging an\r
340 * object.\r
341 * @param {Event} e the mousemove event\r
342 */\r
343 onDrag: function(e) { /* override this */ },\r
344\r
345 /**\r
346 * Abstract method called when this element fist begins hovering over\r
347 * another DragDrop obj\r
348 * @param {Event} e the mousemove event\r
349 * @param {String/Ext.dd.DragDrop[]} id In POINT mode, the element\r
350 * id this is hovering over. In INTERSECT mode, an array of one or more\r
351 * dragdrop items being hovered over.\r
352 */\r
353 onDragEnter: function(e, id) { /* override this */ },\r
354\r
355 /**\r
356 * Code that executes immediately before the onDragOver event\r
357 * @private\r
358 */\r
359 b4DragOver: function(e) { },\r
360\r
361 /**\r
362 * Abstract method called when this element is hovering over another\r
363 * DragDrop obj\r
364 * @param {Event} e the mousemove event\r
365 * @param {String/Ext.dd.DragDrop[]} id In POINT mode, the element\r
366 * id this is hovering over. In INTERSECT mode, an array of dd items\r
367 * being hovered over.\r
368 */\r
369 onDragOver: function(e, id) { /* override this */ },\r
370\r
371 /**\r
372 * Code that executes immediately before the onDragOut event\r
373 * @private\r
374 */\r
375 b4DragOut: function(e) { },\r
376\r
377 /**\r
378 * Abstract method called when we are no longer hovering over an element\r
379 * @param {Event} e the mousemove event\r
380 * @param {String/Ext.dd.DragDrop[]} id In POINT mode, the element\r
381 * id this was hovering over. In INTERSECT mode, an array of dd items\r
382 * that the mouse is no longer over.\r
383 */\r
384 onDragOut: function(e, id) { /* override this */ },\r
385\r
386 /**\r
387 * Code that executes immediately before the onDragDrop event\r
388 * @private\r
389 */\r
390 b4DragDrop: function(e) { },\r
391\r
392 /**\r
393 * Abstract method called when this item is dropped on another DragDrop\r
394 * obj\r
395 * @param {Event} e the mouseup event\r
396 * @param {String/Ext.dd.DragDrop[]} id In POINT mode, the element\r
397 * id this was dropped on. In INTERSECT mode, an array of dd items this\r
398 * was dropped on.\r
399 */\r
400 onDragDrop: function(e, id) { /* override this */ },\r
401\r
402 /**\r
403 * Abstract method called when this item is dropped on an area with no\r
404 * drop target\r
405 * @param {Event} e the mouseup event\r
406 */\r
407 onInvalidDrop: function(e) { /* override this */ },\r
408\r
409 /**\r
410 * Code that executes immediately before the endDrag event\r
411 * @private\r
412 */\r
413 b4EndDrag: function(e) { },\r
414\r
415 /**\r
416 * Called when we are done dragging the object\r
417 * @param {Event} e the mouseup event\r
418 */\r
419 endDrag: function(e) { /* override this */ },\r
420\r
421 /**\r
422 * Code executed immediately before the onMouseDown event\r
423 * @param {Event} e the mousedown event\r
424 * @private\r
425 */\r
426 b4MouseDown: function(e) { },\r
427\r
428 /**\r
429 * Called when a drag/drop obj gets a mousedown\r
430 * @param {Event} e the mousedown event\r
431 */\r
432 onMouseDown: function(e) { /* override this */ },\r
433\r
434 /**\r
435 * Called when a drag/drop obj gets a mouseup\r
436 * @param {Event} e the mouseup event\r
437 */\r
438 onMouseUp: function(e) { /* override this */ },\r
439\r
440 /**\r
441 * Override the onAvailable method to do what is needed after the initial\r
442 * position was determined.\r
443 */\r
444 onAvailable: function () {\r
445 },\r
446\r
447 /**\r
448 * @property {Object} defaultPadding\r
449 * Provides default constraint padding to "constrainTo" elements.\r
450 */\r
451 defaultPadding: {\r
452 left: 0,\r
453 right: 0,\r
454 top: 0,\r
455 bottom: 0\r
456 },\r
457\r
458 /**\r
459 * Initializes the drag drop object's constraints to restrict movement to a certain element.\r
460 *\r
461 * Usage:\r
462 *\r
463 * var dd = new Ext.dd.DDProxy("dragDiv1", "proxytest",\r
464 * { dragElId: "existingProxyDiv" });\r
465 * dd.startDrag = function(){\r
466 * this.constrainTo("parent-id");\r
467 * };\r
468 *\r
469 * Or you can initalize it using the {@link Ext.dom.Element} object:\r
470 *\r
471 * Ext.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {\r
472 * startDrag : function(){\r
473 * this.constrainTo("parent-id");\r
474 * }\r
475 * });\r
476 *\r
477 * @param {String/HTMLElement/Ext.dom.Element} constrainTo The element or element ID to constrain to.\r
478 * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,\r
479 * and can be either a number for symmetrical padding (4 would be equal to `{left:4, right:4, top:4, bottom:4}`) or\r
480 * an object containing the sides to pad. For example: `{right:10, bottom:10}`\r
481 * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)\r
482 */\r
483 constrainTo : function(constrainTo, pad, inContent){\r
484 if (Ext.isNumber(pad)) {\r
485 pad = {left: pad, right:pad, top:pad, bottom:pad};\r
486 }\r
487 pad = pad || this.defaultPadding;\r
488 var ddBox = Ext.get(this.getEl()).getBox(),\r
489 constrainEl = Ext.get(constrainTo),\r
490 s = constrainEl.getScroll(),\r
491 c,\r
492 constrainDom = constrainEl.dom,\r
493 xy,\r
494 topSpace,\r
495 leftSpace;\r
496\r
497 if (constrainDom === document.body) {\r
498 c = {\r
499 x: s.left,\r
500 y: s.top,\r
501 width: Ext.Element.getViewportWidth(),\r
502 height: Ext.Element.getViewportHeight()\r
503 };\r
504 } else {\r
505 xy = constrainEl.getXY();\r
506 c = {\r
507 x : xy[0],\r
508 y: xy[1],\r
509 width: constrainDom.clientWidth,\r
510 height: constrainDom.clientHeight\r
511 };\r
512 }\r
513\r
514 topSpace = ddBox.y - c.y;\r
515 leftSpace = ddBox.x - c.x;\r
516\r
517 this.resetConstraints();\r
518 this.setXConstraint(leftSpace - (pad.left||0), // left\r
519 c.width - leftSpace - ddBox.width - (pad.right||0), //right\r
520 this.xTickSize\r
521 );\r
522 this.setYConstraint(topSpace - (pad.top||0), //top\r
523 c.height - topSpace - ddBox.height - (pad.bottom||0), //bottom\r
524 this.yTickSize\r
525 );\r
526 },\r
527\r
528 /**\r
529 * Returns a reference to the linked element\r
530 * @return {HTMLElement} the html element\r
531 */\r
532 getEl: function() {\r
533 if (!this._domRef) {\r
534 this._domRef = Ext.getDom(this.id);\r
535 }\r
536\r
537 return this._domRef;\r
538 },\r
539\r
540 /**\r
541 * Returns a reference to the actual element to drag. By default this is\r
542 * the same as the html element, but it can be assigned to another\r
543 * element. An example of this can be found in Ext.dd.DDProxy\r
544 * @return {HTMLElement} the html element\r
545 */\r
546 getDragEl: function() {\r
547 return Ext.getDom(this.dragElId);\r
548 },\r
549\r
550 /**\r
551 * Sets up the DragDrop object. Must be called in the constructor of any\r
552 * Ext.dd.DragDrop subclass\r
553 * @param {String} id the id of the linked element\r
554 * @param {String} sGroup the group of related items\r
555 * @param {Object} config configuration attributes\r
556 */\r
557 init: function(id, sGroup, config) {\r
558 var me = this;\r
559\r
560 me.el = me.el || Ext.get(id); // subclass may have already set "el"\r
561\r
562 me.initTarget(id, sGroup, config);\r
563 Ext.get(me.id).on(me.triggerEvent, me.handleMouseDown, me);\r
564 },\r
565\r
566 /**\r
567 * Initializes Targeting functionality only... the object does not\r
568 * get a mousedown handler.\r
569 * @param {String} id the id of the linked element\r
570 * @param {String} sGroup the group of related items\r
571 * @param {Object} config configuration attributes\r
572 */\r
573 initTarget: function(id, sGroup, config) {\r
574 // configuration attributes\r
575 this.config = config || {};\r
576\r
577 // create a local reference to the drag and drop manager\r
578 this.DDMInstance = Ext.dd.DragDropManager;\r
579 // initialize the groups array\r
580 this.groups = {};\r
581\r
582 // assume that we have an element reference instead of an id if the\r
583 // parameter is not a string\r
584 if (typeof id !== "string") {\r
585 id = Ext.id(id);\r
586 }\r
587\r
588 // set the id\r
589 this.id = id;\r
590\r
591 // add to an interaction group\r
592 this.addToGroup((sGroup) ? sGroup : "default");\r
593\r
594 // We don't want to register this as the handle with the manager\r
595 // so we just set the id rather than calling the setter.\r
596 this.handleElId = id;\r
597\r
598 // the linked element is the element that gets dragged by default\r
599 this.setDragElId(id);\r
600\r
601 // by default, clicked anchors will not start drag operations.\r
602 this.invalidHandleTypes = { A: "A" };\r
603 this.invalidHandleIds = {};\r
604 this.invalidHandleClasses = [];\r
605\r
606 this.applyConfig();\r
607\r
608 this.handleOnAvailable();\r
609 },\r
610\r
611 /**\r
612 * Applies the configuration parameters that were passed into the constructor.\r
613 * This is supposed to happen at each level through the inheritance chain. So\r
614 * a DDProxy implentation will execute apply config on DDProxy, DD, and\r
615 * DragDrop in order to get all of the parameters that are available in\r
616 * each object.\r
617 */\r
618 applyConfig: function() {\r
619\r
620 // configurable properties:\r
621 // padding, isTarget, maintainOffset, primaryButtonOnly\r
622 this.padding = this.config.padding || [0, 0, 0, 0];\r
623 this.isTarget = (this.config.isTarget !== false);\r
624 this.maintainOffset = (this.config.maintainOffset);\r
625 this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);\r
626 },\r
627 \r
628 /**\r
629 * Executed when the linked element is available\r
630 * @private\r
631 */\r
632 handleOnAvailable: function() {\r
633 this.available = true;\r
634 this.resetConstraints();\r
635 this.onAvailable();\r
636 },\r
637 \r
638 /**\r
639 * Configures the padding for the target zone in px. Effectively expands\r
640 * (or reduces) the virtual object size for targeting calculations.\r
641 * Supports css-style shorthand; if only one parameter is passed, all sides\r
642 * will have that padding, and if only two are passed, the top and bottom\r
643 * will have the first param, the left and right the second.\r
644 * @param {Number} iTop Top pad\r
645 * @param {Number} iRight Right pad\r
646 * @param {Number} iBot Bot pad\r
647 * @param {Number} iLeft Left pad\r
648 */\r
649 setPadding: function(iTop, iRight, iBot, iLeft) {\r
650 // this.padding = [iLeft, iRight, iTop, iBot];\r
651 if (!iRight && 0 !== iRight) {\r
652 this.padding = [iTop, iTop, iTop, iTop];\r
653 } else if (!iBot && 0 !== iBot) {\r
654 this.padding = [iTop, iRight, iTop, iRight];\r
655 } else {\r
656 this.padding = [iTop, iRight, iBot, iLeft];\r
657 }\r
658 },\r
659\r
660 /**\r
661 * Stores the initial placement of the linked element.\r
662 * @param {Number} diffX the X offset, default 0\r
663 * @param {Number} diffY the Y offset, default 0\r
664 */\r
665 setInitPosition: function(diffX, diffY) {\r
666 var el = this.getEl(),\r
667 dx, dy, p;\r
668\r
669 if (!this.DDMInstance.verifyEl(el)) {\r
670 return;\r
671 }\r
672\r
673 dx = diffX || 0;\r
674 dy = diffY || 0;\r
675\r
676 p = Ext.fly(el).getXY();\r
677\r
678 this.initPageX = p[0] - dx;\r
679 this.initPageY = p[1] - dy;\r
680\r
681 this.lastPageX = p[0];\r
682 this.lastPageY = p[1];\r
683\r
684 this.setStartPosition(p);\r
685 },\r
686\r
687 /**\r
688 * Sets the start position of the element. This is set when the obj\r
689 * is initialized, the reset when a drag is started.\r
690 * @param pos current position (from previous lookup)\r
691 * @private\r
692 */\r
693 setStartPosition: function(pos) {\r
694 var p = pos || Ext.fly(this.getEl()).getXY();\r
695 this.deltaSetXY = null;\r
696\r
697 this.startPageX = p[0];\r
698 this.startPageY = p[1];\r
699 },\r
700\r
701 /**\r
702 * Adds this instance to a group of related drag/drop objects. All\r
703 * instances belong to at least one group, and can belong to as many\r
704 * groups as needed.\r
705 * @param {String} sGroup the name of the group\r
706 */\r
707 addToGroup: function(sGroup) {\r
708 this.groups[sGroup] = true;\r
709 this.DDMInstance.regDragDrop(this, sGroup);\r
710 },\r
711\r
712 /**\r
713 * Removes this instance from the supplied interaction group\r
714 * @param {String} sGroup The group to drop\r
715 */\r
716 removeFromGroup: function(sGroup) {\r
717 if (this.groups[sGroup]) {\r
718 delete this.groups[sGroup];\r
719 }\r
720\r
721 this.DDMInstance.removeDDFromGroup(this, sGroup);\r
722 },\r
723\r
724 /**\r
725 * Allows you to specify that an element other than the linked element\r
726 * will be moved with the cursor during a drag\r
727 * @param {String} id the id of the element that will be used to initiate the drag\r
728 */\r
729 setDragElId: function(id) {\r
730 this.dragElId = id;\r
731 },\r
732\r
733 /**\r
734 * Allows you to specify a child of the linked element that should be\r
735 * used to initiate the drag operation. An example of this would be if\r
736 * you have a content div with text and links. Clicking anywhere in the\r
737 * content area would normally start the drag operation. Use this method\r
738 * to specify that an element inside of the content div is the element\r
739 * that starts the drag operation.\r
740 * @param {String} id the id of the element that will be used to\r
741 * initiate the drag.\r
742 */\r
743 setHandleElId: function(id) {\r
744 if (typeof id !== "string") {\r
745 id = Ext.id(id);\r
746 }\r
747 this.handleElId = id;\r
748 this.DDMInstance.regHandle(this.id, id);\r
749 },\r
750\r
751 /**\r
752 * Allows you to set an element outside of the linked element as a drag\r
753 * handle\r
754 * @param {String} id the id of the element that will be used to initiate the drag\r
755 */\r
756 setOuterHandleElId: function(id) {\r
757 if (typeof id !== "string") {\r
758 id = Ext.id(id);\r
759 }\r
760 Ext.get(id).on(this.triggerEvent, this.handleMouseDown, this);\r
761 this.setHandleElId(id);\r
762\r
763 this.hasOuterHandles = true;\r
764 },\r
765\r
766 /**\r
767 * Removes all drag and drop hooks for this element\r
768 */\r
769 unreg: function() {\r
770 var me = this,\r
771 el;\r
772 \r
773 if (me._domRef) {\r
774 el = Ext.fly(me.id);\r
775 if (el) {\r
776 el.un(me.triggerEvent, me.handleMouseDown, me);\r
777 }\r
778 }\r
779 me._domRef = null;\r
780 me.DDMInstance._remove(me, me.autoGroup);\r
781 },\r
782\r
783 destroy: function() {\r
784 this.unreg();\r
785 this.callParent();\r
786 },\r
787\r
788 /**\r
789 * Returns true if this instance is locked, or the drag drop mgr is locked\r
790 * (meaning that all drag/drop is disabled on the page.)\r
791 * @return {Boolean} true if this obj or all drag/drop is locked, else\r
792 * false\r
793 */\r
794 isLocked: function() {\r
795 return (this.DDMInstance.isLocked() || this.locked);\r
796 },\r
797\r
798 /**\r
799 * Called when this object is clicked\r
800 * @param {Event} e\r
801 * @param {Ext.dd.DragDrop} oDD the clicked dd object (this dd obj)\r
802 * @private\r
803 */\r
804 handleMouseDown: function(e, oDD){\r
805 var me = this;\r
806\r
807 if ((me.primaryButtonOnly && e.button) || me.isLocked()) {\r
808 return;\r
809 }\r
810\r
811 me.DDMInstance.refreshCache(me.groups);\r
812\r
813 if (me.hasOuterHandles || me.DDMInstance.isOverTarget(e.getPoint(), me)) {\r
814 if (me.clickValidator(e)) {\r
815 // set the initial element position\r
816 me.setStartPosition();\r
817 me.b4MouseDown(e);\r
818 me.onMouseDown(e);\r
819\r
820 me.DDMInstance.handleMouseDown(e, me);\r
821\r
822 me.DDMInstance.stopEvent(e);\r
823 }\r
824 }\r
825 },\r
826\r
827 clickValidator: function(e) {\r
828 var target = e.getTarget();\r
829 return ( this.isValidHandleChild(target) &&\r
830 (this.id === this.handleElId ||\r
831 this.DDMInstance.handleWasClicked(target, this.id)) );\r
832 },\r
833\r
834 /**\r
835 * Allows you to specify a tag name that should not start a drag operation\r
836 * when clicked. This is designed to facilitate embedding links within a\r
837 * drag handle that do something other than start the drag.\r
838 * @method addInvalidHandleType\r
839 * @param {String} tagName the type of element to exclude\r
840 */\r
841 addInvalidHandleType: function(tagName) {\r
842 var type = tagName.toUpperCase();\r
843 this.invalidHandleTypes[type] = type;\r
844 },\r
845\r
846 /**\r
847 * Lets you to specify an element id for a child of a drag handle\r
848 * that should not initiate a drag\r
849 * @method addInvalidHandleId\r
850 * @param {String} id the element id of the element you wish to ignore\r
851 */\r
852 addInvalidHandleId: function(id) {\r
853 if (typeof id !== "string") {\r
854 id = Ext.id(id);\r
855 }\r
856 this.invalidHandleIds[id] = id;\r
857 },\r
858\r
859 /**\r
860 * Lets you specify a css class of elements that will not initiate a drag\r
861 * @param {String} cssClass the class of the elements you wish to ignore\r
862 */\r
863 addInvalidHandleClass: function(cssClass) {\r
864 this.invalidHandleClasses.push(cssClass);\r
865 },\r
866\r
867 /**\r
868 * Unsets an excluded tag name set by addInvalidHandleType\r
869 * @param {String} tagName the type of element to unexclude\r
870 */\r
871 removeInvalidHandleType: function(tagName) {\r
872 var type = tagName.toUpperCase();\r
873 // this.invalidHandleTypes[type] = null;\r
874 delete this.invalidHandleTypes[type];\r
875 },\r
876\r
877 /**\r
878 * Unsets an invalid handle id\r
879 * @param {String} id the id of the element to re-enable\r
880 */\r
881 removeInvalidHandleId: function(id) {\r
882 if (typeof id !== "string") {\r
883 id = Ext.id(id);\r
884 }\r
885 delete this.invalidHandleIds[id];\r
886 },\r
887\r
888 /**\r
889 * Unsets an invalid css class\r
890 * @param {String} cssClass the class of the element(s) you wish to\r
891 * re-enable\r
892 */\r
893 removeInvalidHandleClass: function(cssClass) {\r
894 var invalidHandleClasses = this.invalidHandleClasses,\r
895 len = invalidHandleClasses.length,\r
896 i;\r
897\r
898 for (i = 0; i < len; ++i) {\r
899 if (invalidHandleClasses[i] === cssClass) {\r
900 delete invalidHandleClasses[i];\r
901 }\r
902 }\r
903 },\r
904\r
905 /**\r
906 * Checks the tag exclusion list to see if this click should be ignored\r
907 * @param {HTMLElement} node the HTMLElement to evaluate\r
908 * @return {Boolean} true if this is a valid tag type, false if not\r
909 */\r
910 isValidHandleChild: function(node) {\r
911\r
912 var valid = true,\r
913 nodeName,\r
914 i, len;\r
915 // var n = (node.nodeName == "#text") ? node.parentNode : node;\r
916 try {\r
917 nodeName = node.nodeName.toUpperCase();\r
918 } catch(e) {\r
919 nodeName = node.nodeName;\r
920 }\r
921 valid = valid && !this.invalidHandleTypes[nodeName];\r
922 valid = valid && !this.invalidHandleIds[node.id];\r
923\r
924 for (i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {\r
925 valid = !Ext.fly(node).hasCls(this.invalidHandleClasses[i]);\r
926 }\r
927\r
928\r
929 return valid;\r
930\r
931 },\r
932\r
933 /**\r
934 * Creates the array of horizontal tick marks if an interval was specified\r
935 * in setXConstraint().\r
936 * @private\r
937 */\r
938 setXTicks: function(iStartX, iTickSize) {\r
939 this.xTicks = [];\r
940 this.xTickSize = iTickSize;\r
941\r
942 var tickMap = {},\r
943 i;\r
944\r
945 for (i = this.initPageX; i >= this.minX; i = i - iTickSize) {\r
946 if (!tickMap[i]) {\r
947 this.xTicks[this.xTicks.length] = i;\r
948 tickMap[i] = true;\r
949 }\r
950 }\r
951\r
952 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {\r
953 if (!tickMap[i]) {\r
954 this.xTicks[this.xTicks.length] = i;\r
955 tickMap[i] = true;\r
956 }\r
957 }\r
958\r
959 Ext.Array.sort(this.xTicks, this.DDMInstance.numericSort);\r
960 },\r
961\r
962 /**\r
963 * Creates the array of vertical tick marks if an interval was specified in\r
964 * setYConstraint().\r
965 * @private\r
966 */\r
967 setYTicks: function(iStartY, iTickSize) {\r
968 this.yTicks = [];\r
969 this.yTickSize = iTickSize;\r
970\r
971 var tickMap = {},\r
972 i;\r
973\r
974 for (i = this.initPageY; i >= this.minY; i = i - iTickSize) {\r
975 if (!tickMap[i]) {\r
976 this.yTicks[this.yTicks.length] = i;\r
977 tickMap[i] = true;\r
978 }\r
979 }\r
980\r
981 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {\r
982 if (!tickMap[i]) {\r
983 this.yTicks[this.yTicks.length] = i;\r
984 tickMap[i] = true;\r
985 }\r
986 }\r
987\r
988 Ext.Array.sort(this.yTicks, this.DDMInstance.numericSort);\r
989 },\r
990\r
991 /**\r
992 * By default, the element can be dragged any place on the screen. Use\r
993 * this method to limit the horizontal travel of the element. Pass in\r
994 * 0,0 for the parameters if you want to lock the drag to the y axis.\r
995 * @param {Number} iLeft the number of pixels the element can move to the left\r
996 * @param {Number} iRight the number of pixels the element can move to the\r
997 * right\r
998 * @param {Number} iTickSize (optional) parameter for specifying that the\r
999 * element should move iTickSize pixels at a time.\r
1000 */\r
1001 setXConstraint: function(iLeft, iRight, iTickSize) {\r
1002 this.leftConstraint = iLeft;\r
1003 this.rightConstraint = iRight;\r
1004\r
1005 this.minX = this.initPageX - iLeft;\r
1006 this.maxX = this.initPageX + iRight;\r
1007 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }\r
1008\r
1009 this.constrainX = true;\r
1010 },\r
1011\r
1012 /**\r
1013 * Clears any constraints applied to this instance. Also clears ticks\r
1014 * since they can't exist independent of a constraint at this time.\r
1015 */\r
1016 clearConstraints: function() {\r
1017 this.constrainX = false;\r
1018 this.constrainY = false;\r
1019 this.clearTicks();\r
1020 },\r
1021\r
1022 /**\r
1023 * Clears any tick interval defined for this instance\r
1024 */\r
1025 clearTicks: function() {\r
1026 this.xTicks = null;\r
1027 this.yTicks = null;\r
1028 this.xTickSize = 0;\r
1029 this.yTickSize = 0;\r
1030 },\r
1031\r
1032 /**\r
1033 * By default, the element can be dragged any place on the screen. Set\r
1034 * this to limit the vertical travel of the element. Pass in 0,0 for the\r
1035 * parameters if you want to lock the drag to the x axis.\r
1036 * @param {Number} iUp the number of pixels the element can move up\r
1037 * @param {Number} iDown the number of pixels the element can move down\r
1038 * @param {Number} iTickSize (optional) parameter for specifying that the\r
1039 * element should move iTickSize pixels at a time.\r
1040 */\r
1041 setYConstraint: function(iUp, iDown, iTickSize) {\r
1042 this.topConstraint = iUp;\r
1043 this.bottomConstraint = iDown;\r
1044\r
1045 this.minY = this.initPageY - iUp;\r
1046 this.maxY = this.initPageY + iDown;\r
1047 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }\r
1048\r
1049 this.constrainY = true;\r
1050\r
1051 },\r
1052\r
1053 /**\r
1054 * Must be called if you manually reposition a dd element.\r
1055 * @param {Boolean} maintainOffset\r
1056 */\r
1057 resetConstraints: function() {\r
1058 // Maintain offsets if necessary\r
1059 if (this.initPageX || this.initPageX === 0) {\r
1060 // figure out how much this thing has moved\r
1061 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0,\r
1062 dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;\r
1063\r
1064 this.setInitPosition(dx, dy);\r
1065\r
1066 // This is the first time we have detected the element's position\r
1067 } else {\r
1068 this.setInitPosition();\r
1069 }\r
1070\r
1071 if (this.constrainX) {\r
1072 this.setXConstraint( this.leftConstraint,\r
1073 this.rightConstraint,\r
1074 this.xTickSize );\r
1075 }\r
1076\r
1077 if (this.constrainY) {\r
1078 this.setYConstraint( this.topConstraint,\r
1079 this.bottomConstraint,\r
1080 this.yTickSize );\r
1081 }\r
1082 },\r
1083\r
1084 /**\r
1085 * Normally the drag element is moved pixel by pixel, but we can specify\r
1086 * that it move a number of pixels at a time. This method resolves the\r
1087 * location when we have it set up like this.\r
1088 * @param {Number} val where we want to place the object\r
1089 * @param {Number[]} tickArray sorted array of valid points\r
1090 * @return {Number} the closest tick\r
1091 * @private\r
1092 */\r
1093 getTick: function(val, tickArray) {\r
1094 if (!tickArray) {\r
1095 // If tick interval is not defined, it is effectively 1 pixel,\r
1096 // so we return the value passed to us.\r
1097 return val;\r
1098 } else if (tickArray[0] >= val) {\r
1099 // The value is lower than the first tick, so we return the first\r
1100 // tick.\r
1101 return tickArray[0];\r
1102 } else {\r
1103 var i, len, next, diff1, diff2;\r
1104 for (i=0, len=tickArray.length; i<len; ++i) {\r
1105 next = i + 1;\r
1106 if (tickArray[next] && tickArray[next] >= val) {\r
1107 diff1 = val - tickArray[i];\r
1108 diff2 = tickArray[next] - val;\r
1109 return (diff2 > diff1) ? tickArray[i] : tickArray[next];\r
1110 }\r
1111 }\r
1112\r
1113 // The value is larger than the last tick, so we return the last\r
1114 // tick.\r
1115 return tickArray[tickArray.length - 1];\r
1116 }\r
1117 },\r
1118\r
1119 /**\r
1120 * toString method\r
1121 * @return {String} string representation of the dd obj\r
1122 */\r
1123 toString: function() {\r
1124 return ("DragDrop " + this.id);\r
1125 }\r
1126\r
1127});\r