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