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