]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/dd/dd-drag.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / dd / dd-drag.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 3.3.0
6 build: 3167
7 */
8 YUI.add('dd-drag', function(Y) {
9
10
11     /**
12      * Provides the ability to drag a Node.
13      * @module dd
14      * @submodule dd-drag
15      */     
16     /**
17      * Provides the ability to drag a Node.
18      * @class Drag
19      * @extends Base
20      * @constructor
21      * @namespace DD
22      */
23
24     var DDM = Y.DD.DDM,
25         NODE = 'node',
26         DRAGGING = 'dragging',
27         DRAG_NODE = 'dragNode',
28         OFFSET_HEIGHT = 'offsetHeight',
29         OFFSET_WIDTH = 'offsetWidth',        
30         /**
31         * @event drag:mouseup
32         * @description Handles the mouseup DOM event, does nothing internally just fires.
33         * @bubbles DDM
34         * @type {Event.Custom}
35         */
36         /**
37         * @event drag:mouseDown
38         * @description Handles the mousedown DOM event, checks to see if you have a valid handle then starts the drag timers.
39         * @preventable _defMouseDownFn
40         * @param {Event.Facade} event An Event Facade object with the following specific property added:
41         * <dl><dt>ev</dt><dd>The original mousedown event.</dd></dl>
42         * @bubbles DDM
43         * @type {Event.Custom}
44         */
45         EV_MOUSE_DOWN = 'drag:mouseDown',
46         /**
47         * @event drag:afterMouseDown
48         * @description Fires after the mousedown event has been cleared.
49         * @param {Event.Facade} event An Event Facade object with the following specific property added:
50         * <dl><dt>ev</dt><dd>The original mousedown event.</dd></dl>
51         * @bubbles DDM
52         * @type {Event.Custom}
53         */
54         EV_AFTER_MOUSE_DOWN = 'drag:afterMouseDown',
55         /**
56         * @event drag:removeHandle
57         * @description Fires after a handle is removed.
58         * @param {Event.Facade} event An Event Facade object with the following specific property added:
59         * <dl><dt>handle</dt><dd>The handle that was removed.</dd></dl>
60         * @bubbles DDM
61         * @type {Event.Custom}
62         */
63         EV_REMOVE_HANDLE = 'drag:removeHandle',
64         /**
65         * @event drag:addHandle
66         * @description Fires after a handle is added.
67         * @param {Event.Facade} event An Event Facade object with the following specific property added:
68         * <dl><dt>handle</dt><dd>The handle that was added.</dd></dl>
69         * @bubbles DDM
70         * @type {Event.Custom}
71         */
72         EV_ADD_HANDLE = 'drag:addHandle',
73         /**
74         * @event drag:removeInvalid
75         * @description Fires after an invalid selector is removed.
76         * @param {Event.Facade} event An Event Facade object with the following specific property added:
77         * <dl><dt>handle</dt><dd>The handle that was removed.</dd></dl>
78         * @bubbles DDM
79         * @type {Event.Custom}
80         */
81         EV_REMOVE_INVALID = 'drag:removeInvalid',
82         /**
83         * @event drag:addInvalid
84         * @description Fires after an invalid selector is added.
85         * @param {Event.Facade} event An Event Facade object with the following specific property added:
86         * <dl><dt>handle</dt><dd>The handle that was added.</dd></dl>
87         * @bubbles DDM
88         * @type {Event.Custom}
89         */
90         EV_ADD_INVALID = 'drag:addInvalid',
91         /**
92         * @event drag:start
93         * @description Fires at the start of a drag operation.
94         * @param {Event.Facade} event An Event Facade object with the following specific property added:
95         * <dl>
96         * <dt>pageX</dt><dd>The original node position X.</dd>
97         * <dt>pageY</dt><dd>The original node position Y.</dd>
98         * <dt>startTime</dt><dd>The startTime of the event. getTime on the current Date object.</dd>
99         * </dl>
100         * @bubbles DDM
101         * @type {Event.Custom}
102         */
103         EV_START = 'drag:start',
104         /**
105         * @event drag:end
106         * @description Fires at the end of a drag operation.
107         * @param {Event.Facade} event An Event Facade object with the following specific property added:
108         * <dl>
109         * <dt>pageX</dt><dd>The current node position X.</dd>
110         * <dt>pageY</dt><dd>The current node position Y.</dd>
111         * <dt>startTime</dt><dd>The startTime of the event, from the start event.</dd>
112         * <dt>endTime</dt><dd>The endTime of the event. getTime on the current Date object.</dd>
113         * </dl>
114         * @bubbles DDM
115         * @type {Event.Custom}
116         */
117         EV_END = 'drag:end',
118         /**
119         * @event drag:drag
120         * @description Fires every mousemove during a drag operation.
121         * @param {Event.Facade} event An Event Facade object with the following specific property added:
122         * <dl>
123         * <dt>pageX</dt><dd>The current node position X.</dd>
124         * <dt>pageY</dt><dd>The current node position Y.</dd>
125         * <dt>scroll</dt><dd>Should a scroll action occur.</dd>
126         * <dt>info</dt><dd>Object hash containing calculated XY arrays: start, xy, delta, offset</dd>
127         * </dl>
128         * @bubbles DDM
129         * @type {Event.Custom}
130         */
131         EV_DRAG = 'drag:drag',
132         /**
133         * @event drag:align
134         * @preventable _defAlignFn
135         * @description Fires when this node is aligned.
136         * @param {Event.Facade} event An Event Facade object with the following specific property added:
137         * <dl>
138         * <dt>pageX</dt><dd>The current node position X.</dd>
139         * <dt>pageY</dt><dd>The current node position Y.</dd>
140         * </dl>
141         * @bubbles DDM
142         * @type {Event.Custom}
143         */
144         EV_ALIGN = 'drag:align',
145         /**
146         * @event drag:over
147         * @description Fires when this node is over a Drop Target. (Fired from dd-drop)
148         * @param {Event.Facade} event An Event Facade object with the following specific property added:
149         * <dl>
150         * <dt>drop</dt><dd>The drop object at the time of the event.</dd>
151         * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
152         * </dl>
153         * @bubbles DDM
154         * @type {Event.Custom}
155         */
156         /**
157         * @event drag:enter
158         * @description Fires when this node enters a Drop Target. (Fired from dd-drop)
159         * @param {Event.Facade} event An Event Facade object with the following specific property added:
160         * <dl>
161         * <dt>drop</dt><dd>The drop object at the time of the event.</dd>
162         * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
163         * </dl>
164         * @bubbles DDM
165         * @type {Event.Custom}
166         */
167         /**
168         * @event drag:exit
169         * @description Fires when this node exits a Drop Target. (Fired from dd-drop)
170         * @param {Event.Facade} event An Event Facade object with the following specific property added:
171         * <dl>
172         * <dt>drop</dt><dd>The drop object at the time of the event.</dd>
173         * </dl>
174         * @bubbles DDM
175         * @type {Event.Custom}
176         */
177         /**
178         * @event drag:drophit
179         * @description Fires when this node is dropped on a valid Drop Target. (Fired from dd-ddm-drop)
180         * @param {Event.Facade} event An Event Facade object with the following specific property added:
181         * <dl>
182         * <dt>drop</dt><dd>The best guess on what was dropped on.</dd>
183         * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
184         * <dt>others</dt><dd>An array of all the other drop targets that was dropped on.</dd>
185         * </dl>
186         * @bubbles DDM
187         * @type {Event.Custom}
188         */
189         /**
190         * @event drag:dropmiss
191         * @description Fires when this node is dropped on an invalid Drop Target. (Fired from dd-ddm-drop)
192         * @param {Event.Facade} event An Event Facade object with the following specific property added:
193         * <dl>
194         * <dt>pageX</dt><dd>The current node position X.</dd>
195         * <dt>pageY</dt><dd>The current node position Y.</dd>
196         * </dl>
197         * @bubbles DDM
198         * @type {Event.Custom}
199         */
200     
201     Drag = function(o) {
202         this._lazyAddAttrs = false;
203         Drag.superclass.constructor.apply(this, arguments);
204
205         var valid = DDM._regDrag(this);
206         if (!valid) {
207             Y.error('Failed to register node, already in use: ' + o.node);
208         }
209     };
210
211     Drag.NAME = 'drag';
212     
213     /**
214     * This property defaults to "mousedown", but when drag-gestures is loaded, it is changed to "gesturemovestart"
215     * @static
216     * @property START_EVENT
217     */
218     Drag.START_EVENT = 'mousedown';
219
220     Drag.ATTRS = {
221         /**
222         * @attribute node
223         * @description Y.Node instance to use as the element to initiate a drag operation
224         * @type Node
225         */
226         node: {
227             setter: function(node) {
228                 var n = Y.one(node);
229                 if (!n) {
230                     Y.error('DD.Drag: Invalid Node Given: ' + node);
231                 }
232                 return n;
233             }
234         },
235         /**
236         * @attribute dragNode
237         * @description Y.Node instance to use as the draggable element, defaults to node
238         * @type Node
239         */
240         dragNode: {
241             setter: function(node) {
242                 var n = Y.one(node);
243                 if (!n) {
244                     Y.error('DD.Drag: Invalid dragNode Given: ' + node);
245                 }
246                 return n;
247             }
248         },
249         /**
250         * @attribute offsetNode
251         * @description Offset the drag element by the difference in cursor position: default true
252         * @type Boolean
253         */
254         offsetNode: {
255             value: true
256         },
257         /**
258         * @attribute startCentered
259         * @description Center the dragNode to the mouse position on drag:start: default false
260         * @type Boolean
261         */
262         startCentered: {
263             value: false
264         },
265         /**
266         * @attribute clickPixelThresh
267         * @description The number of pixels to move to start a drag operation, default is 3.
268         * @type Number
269         */
270         clickPixelThresh: {
271             value: DDM.get('clickPixelThresh')
272         },
273         /**
274         * @attribute clickTimeThresh
275         * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
276         * @type Number
277         */
278         clickTimeThresh: {
279             value: DDM.get('clickTimeThresh')
280         },
281         /**
282         * @attribute lock
283         * @description Set to lock this drag element so that it can't be dragged: default false.
284         * @type Boolean
285         */
286         lock: {
287             value: false,
288             setter: function(lock) {
289                 if (lock) {
290                     this.get(NODE).addClass(DDM.CSS_PREFIX + '-locked');
291                 } else {
292                     this.get(NODE).removeClass(DDM.CSS_PREFIX + '-locked');
293                 }
294                 return lock;
295             }
296         },
297         /**
298         * @attribute data
299         * @description A payload holder to store arbitrary data about this drag object, can be used to store any value.
300         * @type Mixed
301         */
302         data: {
303             value: false
304         },
305         /**
306         * @attribute move
307         * @description If this is false, the drag element will not move with the cursor: default true. Can be used to "resize" the element.
308         * @type Boolean
309         */
310         move: {
311             value: true
312         },
313         /**
314         * @attribute useShim
315         * @description Use the protective shim on all drag operations: default true. Only works with dd-ddm, not dd-ddm-base.
316         * @type Boolean
317         */
318         useShim: {
319             value: true
320         },
321         /**
322         * @attribute activeHandle
323         * @description This config option is set by Drag to inform you of which handle fired the drag event (in the case that there are several handles): default false.
324         * @type Node
325         */
326         activeHandle: {
327             value: false
328         },
329         /**
330         * @attribute primaryButtonOnly
331         * @description By default a drag operation will only begin if the mousedown occurred with the primary mouse button. Setting this to false will allow for all mousedown events to trigger a drag.
332         * @type Boolean
333         */
334         primaryButtonOnly: {
335             value: true
336         },
337         /**
338         * @attribute dragging
339         * @description This attribute is not meant to be used by the implementor, it is meant to be used as an Event tracker so you can listen for it to change.
340         * @type Boolean
341         */
342         dragging: {
343             value: false
344         },
345         parent: {
346             value: false
347         },
348         /**
349         * @attribute target
350         * @description This attribute only works if the dd-drop module has been loaded. It will make this node a drop target as well as draggable.
351         * @type Boolean
352         */
353         target: {
354             value: false,
355             setter: function(config) {
356                 this._handleTarget(config);
357                 return config;
358             }
359         },
360         /**
361         * @attribute dragMode
362         * @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of this Drag instance.
363         * @type String
364         */
365         dragMode: {
366             value: null,
367             setter: function(mode) {
368                 return DDM._setDragMode(mode);
369             }
370         },
371         /**
372         * @attribute groups
373         * @description Array of groups to add this drag into.
374         * @type Array
375         */
376         groups: {
377             value: ['default'],
378             getter: function() {
379                 if (!this._groups) {
380                     this._groups = {};
381                 }
382                 var ret = [];
383                 Y.each(this._groups, function(v, k) {
384                     ret[ret.length] = k;
385                 });
386                 return ret;
387             },
388             setter: function(g) {
389                 this._groups = {};
390                 Y.each(g, function(v, k) {
391                     this._groups[v] = true;
392                 }, this);
393                 return g;
394             }
395         },
396         /**
397         * @attribute handles
398         * @description Array of valid handles to add. Adding something here will set all handles, even if previously added with addHandle
399         * @type Array
400         */
401         handles: {
402             value: null,
403             setter: function(g) {
404                 if (g) {
405                     this._handles = {};
406                     Y.each(g, function(v, k) {
407                         var key = v;
408                         if (v instanceof Y.Node || v instanceof Y.NodeList) {
409                             key = v._yuid;
410                         }
411                         this._handles[key] = v;
412                     }, this);
413                 } else {
414                     this._handles = null;
415                 }
416                 return g;
417             }
418         },
419         /**
420         * @deprecated
421         * @attribute bubbles
422         * @description Controls the default bubble parent for this Drag instance. Default: Y.DD.DDM. Set to false to disable bubbling. Use bubbleTargets in config
423         * @type Object
424         */
425         bubbles: {
426             setter: function(t) {
427                 this.addTarget(t);
428                 return t;
429             }
430         },
431         /**
432         * @attribute haltDown
433         * @description Should the mousedown event be halted. Default: true
434         * @type Boolean
435         */
436         haltDown: {
437             value: true
438         }
439     };
440
441     Y.extend(Drag, Y.Base, {
442         /**
443         * @private
444         * @property _bubbleTargets
445         * @description The default bubbleTarget for this object. Default: Y.DD.DDM
446         */
447         _bubbleTargets: Y.DD.DDM,
448         /**
449         * @method addToGroup
450         * @description Add this Drag instance to a group, this should be used for on-the-fly group additions.
451         * @param {String} g The group to add this Drag Instance to.
452         * @return {Self}
453         * @chainable
454         */
455         addToGroup: function(g) {
456             this._groups[g] = true;
457             DDM._activateTargets();
458             return this;
459         },
460         /**
461         * @method removeFromGroup
462         * @description Remove this Drag instance from a group, this should be used for on-the-fly group removals.
463         * @param {String} g The group to remove this Drag Instance from.
464         * @return {Self}
465         * @chainable
466         */
467         removeFromGroup: function(g) {
468             delete this._groups[g];
469             DDM._activateTargets();
470             return this;
471         },
472         /**
473         * @property target
474         * @description This will be a reference to the Drop instance associated with this drag if the target: true config attribute is set..
475         * @type {Object}
476         */
477         target: null,
478         /**
479         * @private
480         * @method _handleTarget
481         * @description Attribute handler for the target config attribute.
482         * @param {Boolean/Object}
483         * @return {Boolean/Object}
484         */
485         _handleTarget: function(config) {
486             if (Y.DD.Drop) {
487                 if (config === false) {
488                     if (this.target) {
489                         DDM._unregTarget(this.target);
490                         this.target = null;
491                     }
492                     return false;
493                 } else {
494                     if (!Y.Lang.isObject(config)) {
495                         config = {};
496                     }
497                     config.bubbleTargets = ('bubbleTargets' in config) ? config.bubbleTargets : Y.Object.values(this._yuievt.targets);
498                     config.node = this.get(NODE);
499                     config.groups = config.groups || this.get('groups');
500                     this.target = new Y.DD.Drop(config);
501                 }
502             } else {
503                 return false;
504             }
505         },
506         /**
507         * @private
508         * @property _groups
509         * @description Storage Array for the groups this drag belongs to.
510         * @type {Array}
511         */
512         _groups: null,
513         /**
514         * @private
515         * @method _createEvents
516         * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
517         */
518         _createEvents: function() {
519             
520             this.publish(EV_MOUSE_DOWN, {
521                 defaultFn: this._defMouseDownFn,
522                 queuable: false,
523                 emitFacade: true,
524                 bubbles: true,
525                 prefix: 'drag'
526             });
527             
528             this.publish(EV_ALIGN, {
529                 defaultFn: this._defAlignFn,
530                 queuable: false,
531                 emitFacade: true,
532                 bubbles: true,
533                 prefix: 'drag'
534             });
535             
536             this.publish(EV_DRAG, {
537                 defaultFn: this._defDragFn,
538                 queuable: false,
539                 emitFacade: true,
540                 bubbles: true,
541                 prefix: 'drag'
542             });
543             
544             this.publish(EV_END, {
545                 defaultFn: this._defEndFn,
546                 preventedFn: this._prevEndFn,
547                 queuable: false,
548                 emitFacade: true,
549                 bubbles: true,
550                 prefix: 'drag'
551             });
552             
553             var ev = [
554                 EV_AFTER_MOUSE_DOWN,
555                 EV_REMOVE_HANDLE,
556                 EV_ADD_HANDLE,
557                 EV_REMOVE_INVALID,
558                 EV_ADD_INVALID,
559                 EV_START,
560                 'drag:drophit',
561                 'drag:dropmiss',
562                 'drag:over',
563                 'drag:enter',
564                 'drag:exit'
565             ];
566             
567             Y.each(ev, function(v, k) {
568                 this.publish(v, {
569                     type: v,
570                     emitFacade: true,
571                     bubbles: true,
572                     preventable: false,
573                     queuable: false,
574                     prefix: 'drag'
575                 });
576             }, this);
577         },
578         /**
579         * @private
580         * @property _ev_md
581         * @description A private reference to the mousedown DOM event
582         * @type {Event.Facade}
583         */
584         _ev_md: null,
585         /**
586         * @private
587         * @property _startTime
588         * @description The getTime of the mousedown event. Not used, just here in case someone wants/needs to use it.
589         * @type Date
590         */
591         _startTime: null,
592         /**
593         * @private
594         * @property _endTime
595         * @description The getTime of the mouseup event. Not used, just here in case someone wants/needs to use it.
596         * @type Date
597         */
598         _endTime: null,
599         /**
600         * @private
601         * @property _handles
602         * @description A private hash of the valid drag handles
603         * @type {Object}
604         */
605         _handles: null,
606         /**
607         * @private
608         * @property _invalids
609         * @description A private hash of the invalid selector strings
610         * @type {Object}
611         */
612         _invalids: null,
613         /**
614         * @private
615         * @property _invalidsDefault
616         * @description A private hash of the default invalid selector strings: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true}
617         * @type {Object}
618         */
619         _invalidsDefault: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true },
620         /**
621         * @private
622         * @property _dragThreshMet
623         * @description Private flag to see if the drag threshhold was met
624         * @type {Boolean}
625         */
626         _dragThreshMet: null,
627         /**
628         * @private
629         * @property _fromTimeout
630         * @description Flag to determine if the drag operation came from a timeout
631         * @type {Boolean}
632         */
633         _fromTimeout: null,
634         /**
635         * @private
636         * @property _clickTimeout
637         * @description Holder for the setTimeout call
638         * @type {Boolean}
639         */
640         _clickTimeout: null,
641         /**
642         * @property deltaXY
643         * @description The offset of the mouse position to the element's position
644         * @type {Array}
645         */
646         deltaXY: null,
647         /**
648         * @property startXY
649         * @description The initial mouse position
650         * @type {Array}
651         */
652         startXY: null,
653         /**
654         * @property nodeXY
655         * @description The initial element position
656         * @type {Array}
657         */
658         nodeXY: null,
659         /**
660         * @property lastXY
661         * @description The position of the element as it's moving (for offset calculations)
662         * @type {Array}
663         */
664         lastXY: null,
665         /**
666         * @property actXY
667         * @description The xy that the node will be set to. Changing this will alter the position as it's dragged.
668         * @type {Array}
669         */
670         actXY: null,
671         /**
672         * @property realXY
673         * @description The real xy position of the node.
674         * @type {Array}
675         */
676         realXY: null,
677         /**
678         * @property mouseXY
679         * @description The XY coords of the mousemove
680         * @type {Array}
681         */
682         mouseXY: null,
683         /**
684         * @property region
685         * @description A region object associated with this drag, used for checking regions while dragging.
686         * @type Object
687         */
688         region: null,       
689         /**
690         * @private
691         * @method _handleMouseUp
692         * @description Handler for the mouseup DOM event
693         * @param {Event.Facade}
694         */
695         _handleMouseUp: function(ev) {
696             this.fire('drag:mouseup');
697             this._fixIEMouseUp();
698             if (DDM.activeDrag) {
699                 DDM._end();
700             }
701         },
702         /** 
703         * @private
704         * @method _fixDragStart
705         * @description The function we use as the ondragstart handler when we start a drag in Internet Explorer. This keeps IE from blowing up on images as drag handles.
706         */
707         _fixDragStart: function(e) {
708             e.preventDefault();
709         },
710         /** 
711         * @private
712         * @method _ieSelectFix
713         * @description The function we use as the onselectstart handler when we start a drag in Internet Explorer
714         */
715         _ieSelectFix: function() {
716             return false;
717         },
718         /** 
719         * @private
720         * @property _ieSelectBack
721         * @description We will hold a copy of the current "onselectstart" method on this property, and reset it after we are done using it.
722         */
723         _ieSelectBack: null,
724         /**
725         * @private
726         * @method _fixIEMouseDown
727         * @description This method copies the onselectstart listner on the document to the _ieSelectFix property
728         */
729         _fixIEMouseDown: function(e) {
730             if (Y.UA.ie) {
731                 this._ieSelectBack = Y.config.doc.body.onselectstart;
732                 Y.config.doc.body.onselectstart = this._ieSelectFix;
733             }           
734         },
735         /**
736         * @private
737         * @method _fixIEMouseUp
738         * @description This method copies the _ieSelectFix property back to the onselectstart listner on the document.
739         */
740         _fixIEMouseUp: function() {
741             if (Y.UA.ie) {
742                 Y.config.doc.body.onselectstart = this._ieSelectBack;
743             }           
744         },
745         /**
746         * @private
747         * @method _handleMouseDownEvent
748         * @description Handler for the mousedown DOM event
749         * @param {Event.Facade}
750         */
751         _handleMouseDownEvent: function(ev) {
752             this.fire(EV_MOUSE_DOWN, { ev: ev });
753         },
754         /**
755         * @private
756         * @method _defMouseDownFn
757         * @description Handler for the mousedown DOM event
758         * @param {Event.Facade}
759         */
760         _defMouseDownFn: function(e) {
761             var ev = e.ev;
762
763             this._dragThreshMet = false;
764             this._ev_md = ev;
765             
766             if (this.get('primaryButtonOnly') && ev.button > 1) {
767                 return false;
768             }
769             if (this.validClick(ev)) {
770                 this._fixIEMouseDown(ev);
771                 if (this.get('haltDown')) {
772                     ev.halt();
773                 } else {
774                     ev.preventDefault();
775                 }
776                 
777                 this._setStartPosition([ev.pageX, ev.pageY]);
778
779                 DDM.activeDrag = this;
780                 
781                 this._clickTimeout = Y.later(this.get('clickTimeThresh'), this, this._timeoutCheck);
782             }
783             this.fire(EV_AFTER_MOUSE_DOWN, { ev: ev });
784         },
785         /**
786         * @method validClick
787         * @description Method first checks to see if we have handles, if so it validates the click against the handle. Then if it finds a valid handle, it checks it against the invalid handles list. Returns true if a good handle was used, false otherwise.
788         * @param {Event.Facade}
789         * @return {Boolean}
790         */
791         validClick: function(ev) {
792             var r = false, n = false,
793             tar = ev.target,
794             hTest = null,
795             els = null,
796             nlist = null,
797             set = false;
798             if (this._handles) {
799                 Y.each(this._handles, function(i, n) {
800                     if (i instanceof Y.Node || i instanceof Y.NodeList) {
801                         if (!r) {
802                             nlist = i;
803                             if (nlist instanceof Y.Node) {
804                                 nlist = new Y.NodeList(i._node);
805                             }
806                             nlist.each(function(nl) {
807                                 if (nl.contains(tar)) {
808                                     r = true;
809                                 }
810                             });
811                         }
812                     } else if (Y.Lang.isString(n)) {
813                         //Am I this or am I inside this
814                         if (tar.test(n + ', ' + n + ' *') && !hTest) {
815                             hTest = n;
816                             r = true;
817                         }
818                     }
819                 });
820             } else {
821                 n = this.get(NODE);
822                 if (n.contains(tar) || n.compareTo(tar)) {
823                     r = true;
824                 }
825             }
826             if (r) {
827                 if (this._invalids) {
828                     Y.each(this._invalids, function(i, n) {
829                         if (Y.Lang.isString(n)) {
830                             //Am I this or am I inside this
831                             if (tar.test(n + ', ' + n + ' *')) {
832                                 r = false;
833                             }
834                         }
835                     });
836                 }
837             }
838             if (r) {
839                 if (hTest) {
840                     els = ev.currentTarget.all(hTest);
841                     set = false;
842                     els.each(function(n, i) {
843                         if ((n.contains(tar) || n.compareTo(tar)) && !set) {
844                             set = true;
845                             this.set('activeHandle', n);
846                         }
847                     }, this);
848                 } else {
849                     this.set('activeHandle', this.get(NODE));
850                 }
851             }
852             return r;
853         },
854         /**
855         * @private
856         * @method _setStartPosition
857         * @description Sets the current position of the Element and calculates the offset
858         * @param {Array} xy The XY coords to set the position to.
859         */
860         _setStartPosition: function(xy) {
861             this.startXY = xy;
862             
863             this.nodeXY = this.lastXY = this.realXY = this.get(NODE).getXY();
864             
865             if (this.get('offsetNode')) {
866                 this.deltaXY = [(this.startXY[0] - this.nodeXY[0]), (this.startXY[1] - this.nodeXY[1])];
867             } else {
868                 this.deltaXY = [0, 0];
869             }
870         },
871         /**
872         * @private
873         * @method _timeoutCheck
874         * @description The method passed to setTimeout to determine if the clickTimeThreshold was met.
875         */
876         _timeoutCheck: function() {
877             if (!this.get('lock') && !this._dragThreshMet && this._ev_md) {
878                 this._fromTimeout = this._dragThreshMet = true;
879                 this.start();
880                 this._alignNode([this._ev_md.pageX, this._ev_md.pageY], true);
881             }
882         },
883         /**
884         * @method removeHandle
885         * @description Remove a Selector added by addHandle
886         * @param {String} str The selector for the handle to be removed. 
887         * @return {Self}
888         * @chainable
889         */
890         removeHandle: function(str) {
891             var key = str;
892             if (str instanceof Y.Node || str instanceof Y.NodeList) {
893                 key = str._yuid;
894             }
895             if (this._handles[key]) {
896                 delete this._handles[key];
897                 this.fire(EV_REMOVE_HANDLE, { handle: str });
898             }
899             return this;
900         },
901         /**
902         * @method addHandle
903         * @description Add a handle to a drag element. Drag only initiates when a mousedown happens on this element.
904         * @param {String} str The selector to test for a valid handle. Must be a child of the element.
905         * @return {Self}
906         * @chainable
907         */
908         addHandle: function(str) {
909             if (!this._handles) {
910                 this._handles = {};
911             }
912             var key = str;
913             if (str instanceof Y.Node || str instanceof Y.NodeList) {
914                 key = str._yuid;
915             }
916             this._handles[key] = str;
917             this.fire(EV_ADD_HANDLE, { handle: str });
918             return this;
919         },
920         /**
921         * @method removeInvalid
922         * @description Remove an invalid handle added by addInvalid
923         * @param {String} str The invalid handle to remove from the internal list.
924         * @return {Self}
925         * @chainable
926         */
927         removeInvalid: function(str) {
928             if (this._invalids[str]) {
929                 this._invalids[str] = null;
930                 delete this._invalids[str];
931                 this.fire(EV_REMOVE_INVALID, { handle: str });
932             }
933             return this;
934         },
935         /**
936         * @method addInvalid
937         * @description Add a selector string to test the handle against. If the test passes the drag operation will not continue.
938         * @param {String} str The selector to test against to determine if this is an invalid drag handle.
939         * @return {Self}
940         * @chainable
941         */
942         addInvalid: function(str) {
943             if (Y.Lang.isString(str)) {
944                 this._invalids[str] = true;
945                 this.fire(EV_ADD_INVALID, { handle: str });
946             }
947             return this;
948         },
949         /**
950         * @private
951         * @method initializer
952         * @description Internal init handler
953         */
954         initializer: function(cfg) {
955             this.get(NODE).dd = this;
956
957             if (!this.get(NODE).get('id')) {
958                 var id = Y.stamp(this.get(NODE));
959                 this.get(NODE).set('id', id);
960             }
961
962             this.actXY = [];
963             
964             this._invalids = Y.clone(this._invalidsDefault, true);
965
966             this._createEvents();
967             
968             if (!this.get(DRAG_NODE)) {
969                 this.set(DRAG_NODE, this.get(NODE));
970             }
971
972             //Fix for #2528096
973             //Don't prep the DD instance until all plugins are loaded.
974             this.on('initializedChange', Y.bind(this._prep, this));
975
976             //Shouldn't have to do this..
977             this.set('groups', this.get('groups'));
978         },
979         /**
980         * @private
981         * @method _prep
982         * @description Attach event listners and add classname
983         */
984         _prep: function() {
985             this._dragThreshMet = false;
986             var node = this.get(NODE);
987             node.addClass(DDM.CSS_PREFIX + '-draggable');
988             node.on(Drag.START_EVENT, Y.bind(this._handleMouseDownEvent, this));
989             node.on('mouseup', Y.bind(this._handleMouseUp, this));
990             node.on('dragstart', Y.bind(this._fixDragStart, this));
991         },
992         /**
993         * @private
994         * @method _unprep
995         * @description Detach event listeners and remove classname
996         */
997         _unprep: function() {
998             var node = this.get(NODE);
999             node.removeClass(DDM.CSS_PREFIX + '-draggable');
1000             node.detachAll();
1001         },
1002         /**
1003         * @method start
1004         * @description Starts the drag operation
1005         * @return {Self}
1006         * @chainable
1007         */
1008         start: function() {
1009             if (!this.get('lock') && !this.get(DRAGGING)) {
1010                 var node = this.get(NODE), ow, oh, xy;
1011                 this._startTime = (new Date()).getTime();
1012
1013                 DDM._start();
1014                 node.addClass(DDM.CSS_PREFIX + '-dragging');
1015                 this.fire(EV_START, {
1016                     pageX: this.nodeXY[0],
1017                     pageY: this.nodeXY[1],
1018                     startTime: this._startTime
1019                 });
1020                 node = this.get(DRAG_NODE);
1021                 xy = this.nodeXY;
1022                 
1023                 ow = node.get(OFFSET_WIDTH);
1024                 oh = node.get(OFFSET_HEIGHT);
1025                 
1026                 if (this.get('startCentered')) {
1027                     this._setStartPosition([xy[0] + (ow / 2), xy[1] + (oh / 2)]);
1028                 }
1029                 
1030                 
1031                 this.region = {
1032                     '0': xy[0], 
1033                     '1': xy[1],
1034                     area: 0,
1035                     top: xy[1],
1036                     right: xy[0] + ow,
1037                     bottom: xy[1] + oh,
1038                     left: xy[0]
1039                 };
1040                 this.set(DRAGGING, true);
1041             }
1042             return this;
1043         },
1044         /**
1045         * @method end
1046         * @description Ends the drag operation
1047         * @return {Self}
1048         * @chainable
1049         */
1050         end: function() {
1051             this._endTime = (new Date()).getTime();
1052             if (this._clickTimeout) {
1053                 this._clickTimeout.cancel();
1054             }
1055             this._dragThreshMet = this._fromTimeout = false;
1056
1057             if (!this.get('lock') && this.get(DRAGGING)) {
1058                 this.fire(EV_END, {
1059                     pageX: this.lastXY[0],
1060                     pageY: this.lastXY[1],
1061                     startTime: this._startTime,
1062                     endTime: this._endTime
1063                 });
1064             }
1065             this.get(NODE).removeClass(DDM.CSS_PREFIX + '-dragging');
1066             this.set(DRAGGING, false);
1067             this.deltaXY = [0, 0];
1068
1069             return this;
1070         },
1071         /**
1072         * @private
1073         * @method _defEndFn
1074         * @description Handler for fixing the selection in IE
1075         */
1076         _defEndFn: function(e) {
1077             this._fixIEMouseUp();
1078             this._ev_md = null;
1079         },
1080         /**
1081         * @private
1082         * @method _prevEndFn
1083         * @description Handler for preventing the drag:end event. It will reset the node back to it's start position
1084         */
1085         _prevEndFn: function(e) {
1086             this._fixIEMouseUp();
1087             //Bug #1852577
1088             this.get(DRAG_NODE).setXY(this.nodeXY);
1089             this._ev_md = null;
1090             this.region = null;
1091         },
1092         /**
1093         * @private
1094         * @method _align
1095         * @description Calculates the offsets and set's the XY that the element will move to.
1096         * @param {Array} xy The xy coords to align with.
1097         */
1098         _align: function(xy) {
1099             this.fire(EV_ALIGN, {pageX: xy[0], pageY: xy[1] });
1100         },
1101         /**
1102         * @private
1103         * @method _defAlignFn
1104         * @description Calculates the offsets and set's the XY that the element will move to.
1105         * @param {Event.Facade} e The drag:align event.
1106         */
1107         _defAlignFn: function(e) {
1108             this.actXY = [e.pageX - this.deltaXY[0], e.pageY - this.deltaXY[1]];
1109         },
1110         /**
1111         * @private
1112         * @method _alignNode
1113         * @description This method performs the alignment before the element move.
1114         * @param {Array} eXY The XY to move the element to, usually comes from the mousemove DOM event.
1115         */
1116         _alignNode: function(eXY) {
1117             this._align(eXY);
1118             this._moveNode();
1119         },
1120         /**
1121         * @private
1122         * @method _moveNode
1123         * @description This method performs the actual element move.
1124         */
1125         _moveNode: function(scroll) {
1126             //if (!this.get(DRAGGING)) {
1127             //    return;
1128             //}
1129             var diffXY = [], diffXY2 = [], startXY = this.nodeXY, xy = this.actXY;
1130
1131             diffXY[0] = (xy[0] - this.lastXY[0]);
1132             diffXY[1] = (xy[1] - this.lastXY[1]);
1133
1134             diffXY2[0] = (xy[0] - this.nodeXY[0]);
1135             diffXY2[1] = (xy[1] - this.nodeXY[1]);
1136
1137
1138             this.region = {
1139                 '0': xy[0], 
1140                 '1': xy[1],
1141                 area: 0,
1142                 top: xy[1],
1143                 right: xy[0] + this.get(DRAG_NODE).get(OFFSET_WIDTH),
1144                 bottom: xy[1] + this.get(DRAG_NODE).get(OFFSET_HEIGHT),
1145                 left: xy[0]
1146             };
1147
1148             this.fire(EV_DRAG, {
1149                 pageX: xy[0],
1150                 pageY: xy[1],
1151                 scroll: scroll,
1152                 info: {
1153                     start: startXY,
1154                     xy: xy,
1155                     delta: diffXY,
1156                     offset: diffXY2
1157                 } 
1158             });
1159             
1160             this.lastXY = xy;
1161         },
1162         /**
1163         * @private
1164         * @method _defDragFn
1165         * @description Default function for drag:drag. Fired from _moveNode.
1166         * @param {Event.Facade} ev The drag:drag event
1167         */
1168         _defDragFn: function(e) {
1169             if (this.get('move')) {
1170                 if (e.scroll) {
1171                     e.scroll.node.set('scrollTop', e.scroll.top);
1172                     e.scroll.node.set('scrollLeft', e.scroll.left);
1173                 }
1174                 this.get(DRAG_NODE).setXY([e.pageX, e.pageY]);
1175                 this.realXY = [e.pageX, e.pageY];
1176             }
1177         },
1178         /**
1179         * @private
1180         * @method _move
1181         * @description Fired from DragDropMgr (DDM) on mousemove.
1182         * @param {Event.Facade} ev The mousemove DOM event
1183         */
1184         _move: function(ev) {
1185             if (this.get('lock')) {
1186                 return false;
1187             } else {
1188                 this.mouseXY = [ev.pageX, ev.pageY];
1189                 if (!this._dragThreshMet) {
1190                     var diffX = Math.abs(this.startXY[0] - ev.pageX),
1191                     diffY = Math.abs(this.startXY[1] - ev.pageY);
1192                     if (diffX > this.get('clickPixelThresh') || diffY > this.get('clickPixelThresh')) {
1193                         this._dragThreshMet = true;
1194                         this.start();
1195                         this._alignNode([ev.pageX, ev.pageY]);
1196                     }
1197                 } else {
1198                     if (this._clickTimeout) {
1199                         this._clickTimeout.cancel();
1200                     }
1201                     this._alignNode([ev.pageX, ev.pageY]);
1202                 }
1203             }
1204         },
1205         /**
1206         * @method stopDrag
1207         * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
1208         * @return {Self}
1209         * @chainable
1210         */
1211         stopDrag: function() {
1212             if (this.get(DRAGGING)) {
1213                 DDM._end();
1214             }
1215             return this;
1216         },
1217         /**
1218         * @private
1219         * @method destructor
1220         * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
1221         */
1222         destructor: function() {
1223             this._unprep();
1224             this.detachAll();
1225             if (this.target) {
1226                 this.target.destroy();
1227             }
1228             DDM._unregDrag(this);
1229         }
1230     });
1231     Y.namespace('DD');    
1232     Y.DD.Drag = Drag;
1233
1234
1235
1236
1237
1238 }, '3.3.0' ,{requires:['dd-ddm-base'], skinnable:false});