]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/dd/dd.js
Release 6.2.0beta4
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / dd / dd.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-ddm-base', function(Y) {
9
10
11     /**
12      * Provides the base Drag Drop Manger required for making a Node draggable.
13      * @module dd
14      * @submodule dd-ddm-base
15      */     
16      /**
17      * Provides the base Drag Drop Manger required for making a Node draggable.
18      * @class DDM
19      * @extends Base
20      * @constructor
21      * @namespace DD
22      */
23     
24     var DDMBase = function() {
25         DDMBase.superclass.constructor.apply(this, arguments);
26     };
27
28     DDMBase.NAME = 'ddm';
29
30     DDMBase.ATTRS = {
31         /**
32         * @attribute dragCursor
33         * @description The cursor to apply when dragging, if shimmed the shim will get the cursor.
34         * @type String
35         */
36         dragCursor: {
37             value: 'move'
38         },
39         /**
40         * @attribute clickPixelThresh
41         * @description The number of pixels to move to start a drag operation, default is 3.
42         * @type Number
43         */
44         clickPixelThresh: {
45             value: 3
46         },
47         /**
48         * @attribute clickTimeThresh
49         * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
50         * @type Number
51         */        
52         clickTimeThresh: {
53             value: 1000
54         },
55         /**
56         * @attribute dragMode
57         * @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of all future Drag instances. 
58         * @type String
59         */        
60         dragMode: {
61             value: 'point',
62             setter: function(mode) {
63                 this._setDragMode(mode);
64                 return mode;
65             }           
66         }
67
68     };
69
70     Y.extend(DDMBase, Y.Base, {
71         /**
72         * @property _active
73         * @description flag set when we activate our first drag, so DDM can start listening for events.
74         * @type {Boolean}
75         */
76         _active: null,
77         /**
78         * @private
79         * @method _setDragMode
80         * @description Handler for dragMode attribute setter.
81         * @param String/Number The Number value or the String for the DragMode to default all future drag instances to.
82         * @return Number The Mode to be set
83         */
84         _setDragMode: function(mode) {
85             if (mode === null) {
86                 mode = Y.DD.DDM.get('dragMode');
87             }
88             switch (mode) {
89                 case 1:
90                 case 'intersect':
91                     return 1;
92                 case 2:
93                 case 'strict':
94                     return 2;
95                 case 0:
96                 case 'point':
97                     return 0;
98             }
99             return 0;       
100         },
101         /**
102         * @property CSS_PREFIX
103         * @description The PREFIX to attach to all DD CSS class names
104         * @type {String}
105         */
106         CSS_PREFIX: 'yui-dd',
107         _activateTargets: function() {},        
108         /**
109         * @private
110         * @property _drags
111         * @description Holder for all registered drag elements.
112         * @type {Array}
113         */
114         _drags: [],
115         /**
116         * @property activeDrag
117         * @description A reference to the currently active draggable object.
118         * @type {Drag}
119         */
120         activeDrag: false,
121         /**
122         * @private
123         * @method _regDrag
124         * @description Adds a reference to the drag object to the DDM._drags array, called in the constructor of Drag.
125         * @param {Drag} d The Drag object
126         */
127         _regDrag: function(d) {
128             if (this.getDrag(d.get('node'))) {
129                 return false;
130             }
131             
132             if (!this._active) {
133                 this._setupListeners();
134             }
135             this._drags.push(d);
136             return true;
137         },
138         /**
139         * @private
140         * @method _unregDrag
141         * @description Remove this drag object from the DDM._drags array.
142         * @param {Drag} d The drag object.
143         */
144         _unregDrag: function(d) {
145             var tmp = [];
146             Y.each(this._drags, function(n, i) {
147                 if (n !== d) {
148                     tmp[tmp.length] = n;
149                 }
150             });
151             this._drags = tmp;
152         },
153         /**
154         * @private
155         * @method _setupListeners
156         * @description Add the document listeners.
157         */
158         _setupListeners: function() {
159             this._active = true;
160             var doc = Y.get(document);
161             doc.on('mousemove', Y.bind(this._move, this));
162             //Y.Event.nativeAdd(document, 'mousemove', Y.bind(this._move, this));
163             doc.on('mouseup', Y.bind(this._end, this));
164         },
165         /**
166         * @private
167         * @method _start
168         * @description Internal method used by Drag to signal the start of a drag operation
169         */
170         _start: function() {
171             this.fire('ddm:start');
172             this._startDrag();
173         },
174         /**
175         * @private
176         * @method _startDrag
177         * @description Factory method to be overwritten by other DDM's
178         * @param {Number} x The x position of the drag element
179         * @param {Number} y The y position of the drag element
180         * @param {Number} w The width of the drag element
181         * @param {Number} h The height of the drag element
182         */
183         _startDrag: function() {},
184         /**
185         * @private
186         * @method _endDrag
187         * @description Factory method to be overwritten by other DDM's
188         */
189         _endDrag: function() {},
190         _dropMove: function() {},
191         /**
192         * @private
193         * @method _end
194         * @description Internal method used by Drag to signal the end of a drag operation
195         */
196         _end: function() {
197             if (this.activeDrag) {
198                 this._endDrag();
199                 this.fire('ddm:end');
200                 this.activeDrag.end.call(this.activeDrag);
201                 this.activeDrag = null;
202             }
203         },
204         /**
205         * @method stopDrag
206         * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
207         * @return {Self}
208         * @chainable
209         */       
210         stopDrag: function() {
211             if (this.activeDrag) {
212                 this._end();
213             }
214             return this;
215         },
216         /**
217         * @private
218         * @method _move
219         * @description Internal listener for the mousemove DOM event to pass to the Drag's move method.
220         * @param {Event.Facade} ev The Dom mousemove Event
221         */
222         _move: function(ev) {
223             if (this.activeDrag) {
224                 this.activeDrag._move.call(this.activeDrag, ev);
225                 this._dropMove();
226             }
227         },
228         /**
229         * //TODO Private, rename??...
230         * @private
231         * @method cssSizestoObject
232         * @description Helper method to use to set the gutter from the attribute setter.
233         * @param {String} gutter CSS style string for gutter: '5 0' (sets top and bottom to 5px, left and right to 0px), '1 2 3 4' (top 1px, right 2px, bottom 3px, left 4px)
234         * @return {Object} The gutter Object Literal.
235         */
236         cssSizestoObject: function(gutter) {
237             var x = gutter.split(' ');
238                 
239             switch (x.length) {
240                 case 1: x[1] = x[2] = x[3] = x[0]; break;
241                 case 2: x[2] = x[0]; x[3] = x[1]; break;
242                 case 3: x[3] = x[1]; break;
243             }
244
245             return {
246                 top   : parseInt(x[0],10),
247                 right : parseInt(x[1],10),
248                 bottom: parseInt(x[2],10),
249                 left  : parseInt(x[3],10)
250             };
251         },
252         /**
253         * @method getDrag
254         * @description Get a valid Drag instance back from a Node or a selector string, false otherwise
255         * @param {String/Object} node The Node instance or Selector string to check for a valid Drag Object
256         * @return {Object}
257         */
258         getDrag: function(node) {
259             var drag = false,
260                 n = Y.get(node);
261             if (n instanceof Y.Node) {
262                 Y.each(this._drags, function(v, k) {
263                     if (n.compareTo(v.get('node'))) {
264                         drag = v;
265                     }
266                 });
267             }
268             return drag;
269         }
270     });
271
272     Y.namespace('DD');
273     Y.DD.DDM = new DDMBase();
274
275     /**
276     * @event ddm:start
277     * @description Fires from the DDM before all drag events fire.
278     * @type {Event.Custom}
279     */
280     /**
281     * @event ddm:end
282     * @description Fires from the DDM after the DDM finishes, before the drag end events.
283     * @type {Event.Custom}
284     */
285
286
287
288
289 }, '3.0.0' ,{requires:['node', 'base'], skinnable:false});
290 YUI.add('dd-ddm', function(Y) {
291
292
293     /**
294      * Extends the dd-ddm-base Class to add support for the viewport shim to allow a draggable node to drag to be dragged over an iframe or any other node that traps mousemove events.
295      * It is also required to have Drop Targets enabled, as the viewport shim will contain the shims for the Drop Targets.
296      * @module dd
297      * @submodule dd-ddm
298      * @for DDM
299      * @namespace DD
300      */
301     Y.mix(Y.DD.DDM, {
302         /**
303         * @private
304         * @property _pg
305         * @description The shim placed over the screen to track the mousemove event.
306         * @type {Node}
307         */
308         _pg: null,
309         /**
310         * @private
311         * @property _debugShim
312         * @description Set this to true to set the shims opacity to .5 for debugging it, default: false.
313         * @type {Boolean}
314         */
315         _debugShim: false,
316         _activateTargets: function() {},
317         _deactivateTargets: function() {},
318         _startDrag: function() {
319             if (this.activeDrag.get('useShim')) {
320                 this._pg_activate();
321                 this._activateTargets();
322             }
323         },
324         _endDrag: function() {
325             this._pg_deactivate();
326             this._deactivateTargets();
327         },
328         /**
329         * @private
330         * @method _pg_deactivate
331         * @description Deactivates the shim
332         */
333         _pg_deactivate: function() {
334             this._pg.setStyle('display', 'none');
335         },
336         /**
337         * @private
338         * @method _pg_activate
339         * @description Activates the shim
340         */
341         _pg_activate: function() {
342             var ah = this.activeDrag.get('activeHandle'), cur = 'auto';
343             if (ah) {
344                 cur = ah.getStyle('cursor');
345             }
346             if (cur == 'auto') {
347                 cur = this.get('dragCursor');
348             }
349             
350             this._pg_size();
351             this._pg.setStyles({
352                 top: 0,
353                 left: 0,
354                 display: 'block',
355                 opacity: ((this._debugShim) ? '.5' : '0'),
356                 cursor: cur
357             });
358         },
359         /**
360         * @private
361         * @method _pg_size
362         * @description Sizes the shim on: activatation, window:scroll, window:resize
363         */
364         _pg_size: function() {
365             if (this.activeDrag) {
366                 var b = Y.get('body'),
367                 h = b.get('docHeight'),
368                 w = b.get('docWidth');
369                 this._pg.setStyles({
370                     height: h + 'px',
371                     width: w + 'px'
372                 });
373             }
374         },
375         /**
376         * @private
377         * @method _createPG
378         * @description Creates the shim and adds it's listeners to it.
379         */
380         _createPG: function() {
381             var pg = Y.Node.create('<div></div>'),
382             bd = Y.get('body');
383             pg.setStyles({
384                 top: '0',
385                 left: '0',
386                 position: 'absolute',
387                 zIndex: '9999',
388                 overflow: 'hidden',
389                 backgroundColor: 'red',
390                 display: 'none',
391                 height: '5px',
392                 width: '5px'
393             });
394             pg.set('id', Y.stamp(pg));
395             pg.addClass('yui-dd-shim');
396             if (bd.get('firstChild')) {
397                 bd.insertBefore(pg, bd.get('firstChild'));
398             } else {
399                 bd.appendChild(pg);
400             }
401             this._pg = pg;
402             this._pg.on('mouseup', Y.bind(this._end, this));
403             this._pg.on('mousemove', Y.bind(this._move, this));
404             
405             var win = Y.get(window);
406             Y.on('window:resize', Y.bind(this._pg_size, this));
407             win.on('scroll', Y.bind(this._pg_size, this));
408         }   
409     }, true);
410
411     Y.on('domready', Y.bind(Y.DD.DDM._createPG, Y.DD.DDM));
412
413
414
415
416
417 }, '3.0.0' ,{requires:['dd-ddm-base', 'event-resize'], skinnable:false});
418 YUI.add('dd-ddm-drop', function(Y) {
419
420
421     /**
422      * Extends the dd-ddm Class to add support for the placement of Drop Target shims inside the viewport shim. It also handles all Drop Target related events and interactions.
423      * @module dd
424      * @submodule dd-ddm-drop
425      * @for DDM
426      * @namespace DD
427      */
428
429     //TODO CSS class name for the bestMatch..
430     Y.mix(Y.DD.DDM, {
431         /**
432         * @private
433         * @property _noShim
434         * @description This flag turns off the use of the mouseover/mouseout shim. It should not be used unless you know what you are doing.
435         * @type {Boolean}
436         */
437         _noShim: false,
438         /**
439         * @private
440         * @property _activeShims
441         * @description Placeholder for all active shims on the page
442         * @type {Array}
443         */
444         _activeShims: [],
445         /**
446         * @private
447         * @method _hasActiveShim
448         * @description This method checks the _activeShims Object to see if there is a shim active.
449         * @return {Boolean}
450         */
451         _hasActiveShim: function() {
452             if (this._noShim) {
453                 return true;
454             }
455             return this._activeShims.length;
456         },
457         /**
458         * @private
459         * @method _addActiveShim 
460         * @description Adds a Drop Target to the list of active shims
461         * @param {Object} d The Drop instance to add to the list.
462         */
463         _addActiveShim: function(d) {
464             this._activeShims[this._activeShims.length] = d;
465         },
466         /**
467         * @private
468         * @method _removeActiveShim 
469         * @description Removes a Drop Target to the list of active shims
470         * @param {Object} d The Drop instance to remove from the list.
471         */
472         _removeActiveShim: function(d) {
473             var s = [];
474             Y.each(this._activeShims, function(v, k) {
475                 if (v._yuid !== d._yuid) {
476                     s[s.length] = v;
477                 }
478                 
479             });
480             this._activeShims = s;
481         },
482         /**
483         * @method syncActiveShims
484         * @description This method will sync the position of the shims on the Drop Targets that are currently active.
485         * @param {Boolean} force Resize/sync all Targets.
486         */
487         syncActiveShims: function(force) {
488             Y.later(0, this, function(force) {
489                 var drops = ((force) ? this.targets : this._lookup());
490                 Y.each(drops, function(v, k) {
491                     v.sizeShim.call(v);
492                 }, this);
493             }, force);
494         },
495         /**
496         * @private
497         * @property mode
498         * @description The mode that the drag operations will run in 0 for Point, 1 for Intersect, 2 for Strict
499         * @type Number
500         */
501         mode: 0,
502         /**
503         * @private
504         * @property POINT
505         * @description In point mode, a Drop is targeted by the cursor being over the Target
506         * @type Number
507         */
508         POINT: 0,
509         /**
510         * @private
511         * @property INTERSECT
512         * @description In intersect mode, a Drop is targeted by "part" of the drag node being over the Target
513         * @type Number
514         */
515         INTERSECT: 1,
516         /**
517         * @private
518         * @property STRICT
519         * @description In strict mode, a Drop is targeted by the "entire" drag node being over the Target
520         * @type Number
521         */
522         STRICT: 2,
523         /**
524         * @property useHash
525         * @description Should we only check targets that are in the viewport on drags (for performance), default: true
526         * @type {Boolean}
527         */
528         useHash: true,
529         /**
530         * @property activeDrop
531         * @description A reference to the active Drop Target
532         * @type {Object}
533         */
534         activeDrop: null,
535         /**
536         * @property validDrops
537         * @description An array of the valid Drop Targets for this interaction.
538         * @type {Array}
539         */
540         //TODO Change array/object literals to be in sync..
541         validDrops: [],
542         /**
543         * @property otherDrops
544         * @description An object literal of Other Drop Targets that we encountered during this interaction (in the case of overlapping Drop Targets)
545         * @type {Object}
546         */
547         otherDrops: {},
548         /**
549         * @property targets
550         * @description All of the Targets
551         * @type {Array}
552         */
553         targets: [],
554         /**
555         * @private 
556         * @method _addValid
557         * @description Add a Drop Target to the list of Valid Targets. This list get's regenerated on each new drag operation.
558         * @param {Object} drop
559         * @return {Self}
560         * @chainable
561         */
562         _addValid: function(drop) {
563             this.validDrops[this.validDrops.length] = drop;
564             return this;
565         },
566         /**
567         * @private 
568         * @method _removeValid
569         * @description Removes a Drop Target from the list of Valid Targets. This list get's regenerated on each new drag operation.
570         * @param {Object} drop
571         * @return {Self}
572         * @chainable
573         */
574         _removeValid: function(drop) {
575             var drops = [];
576             Y.each(this.validDrops, function(v, k) {
577                 if (v !== drop) {
578                     drops[drops.length] = v;
579                 }
580             });
581
582             this.validDrops = drops;
583             return this;
584         },
585         /**
586         * @method isOverTarget
587         * @description Check to see if the Drag element is over the target, method varies on current mode
588         * @param {Object} drop The drop to check against
589         * @return {Boolean}
590         */
591         isOverTarget: function(drop) {
592             if (this.activeDrag && drop) {
593                 var xy = this.activeDrag.mouseXY, r, dMode = this.activeDrag.get('dragMode'),
594                     aRegion;
595                 if (xy && this.activeDrag) {
596                     aRegion = this.activeDrag.region;
597                     if (dMode == this.STRICT) {
598                         return this.activeDrag.get('dragNode').inRegion(drop.region, true, aRegion);
599                     } else {
600                         if (drop && drop.shim) {
601                             if ((dMode == this.INTERSECT) && this._noShim) {
602                                 r = ((aRegion) ? aRegion : this.activeDrag.get('node'));
603                                 return drop.get('node').intersect(r).inRegion;
604                             } else {
605                                 return drop.shim.intersect({
606                                     top: xy[1],
607                                     bottom: xy[1],
608                                     left: xy[0], 
609                                     right: xy[0]
610                                 }, drop.region).inRegion;
611                             }
612                         } else {
613                             return false;
614                         }
615                     }
616                 } else {
617                     return false;
618                 }
619             } else {
620                 return false;
621             }
622         },
623         /**
624         * @method clearCache
625         * @description Clears the cache data used for this interaction.
626         */
627         clearCache: function() {
628             this.validDrops = [];
629             this.otherDrops = {};
630             this._activeShims = [];
631         },
632         /**
633         * @private
634         * @method _activateTargets
635         * @description Clear the cache and activate the shims of all the targets
636         */
637         _activateTargets: function() {
638             this.clearCache();
639             Y.each(this.targets, function(v, k) {
640                 v._activateShim.apply(v, []);
641             }, this);
642             this._handleTargetOver();
643             
644         },
645         /**
646         * @method getBestMatch
647         * @description This method will gather the area for all potential targets and see which has the hightest covered area and return it.
648         * @param {Array} drops An Array of drops to scan for the best match.
649         * @param {Boolean} all If present, it returns an Array. First item is best match, second is an Array of the other items in the original Array.
650         * @return {Object or Array} 
651         */
652         getBestMatch: function(drops, all) {
653             var biggest = null, area = 0, out;
654             
655             Y.each(drops, function(v, k) {
656                 var inter = this.activeDrag.get('dragNode').intersect(v.get('node'));
657                 v.region.area = inter.area;
658
659                 if (inter.inRegion) {
660                     if (inter.area > area) {
661                         area = inter.area;
662                         biggest = v;
663                     }
664                 }
665             }, this);
666             if (all) {
667                 out = [];
668                 //TODO Sort the others in numeric order by area covered..
669                 Y.each(drops, function(v, k) {
670                     if (v !== biggest) {
671                         out[out.length] = v;
672                     }
673                 }, this);
674                 return [biggest, out];
675             } else {
676                 return biggest;
677             }
678         },
679         /**
680         * @private
681         * @method _deactivateTargets
682         * @description This method fires the drop:hit, drag:drophit, drag:dropmiss methods and deactivates the shims..
683         */
684         _deactivateTargets: function() {
685             var other = [], tmp,
686                 activeDrag = this.activeDrag,
687                 activeDrop = this.activeDrop;
688             
689             //TODO why is this check so hard??
690             if (activeDrag && activeDrop && this.otherDrops[activeDrop]) {
691                 if (!activeDrag.get('dragMode')) {
692                     //TODO otherDrops -- private..
693                     other = this.otherDrops;
694                     delete other[activeDrop];
695                 } else {
696                     tmp = this.getBestMatch(this.otherDrops, true);
697                     activeDrop = tmp[0];
698                     other = tmp[1];
699                 }
700                 activeDrag.get('node').removeClass(this.CSS_PREFIX + '-drag-over');
701                 if (activeDrop) {
702                     activeDrop.fire('drop:hit', { drag: activeDrag, drop: activeDrop, others: other });
703                     activeDrag.fire('drag:drophit', { drag: activeDrag,  drop: activeDrop, others: other });
704                 }
705             } else if (activeDrag) {
706                 activeDrag.get('node').removeClass(this.CSS_PREFIX + '-drag-over');
707                 activeDrag.fire('drag:dropmiss', { pageX: activeDrag.lastXY[0], pageY: activeDrag.lastXY[1] });
708             } else {
709             }
710             
711             this.activeDrop = null;
712
713             Y.each(this.targets, function(v, k) {
714                 v._deactivateShim.apply(v, []);
715             }, this);
716         },
717         /**
718         * @private
719         * @method _dropMove
720         * @description This method is called when the move method is called on the Drag Object.
721         */
722         _dropMove: function() {
723             if (this._hasActiveShim()) {
724                 this._handleTargetOver();
725             } else {
726                 Y.each(this.otherDrops, function(v, k) {
727                     v._handleOut.apply(v, []);
728                 });
729             }
730         },
731         /**
732         * @private
733         * @method _lookup
734         * @description Filters the list of Drops down to those in the viewport.
735         * @return {Array} The valid Drop Targets that are in the viewport.
736         */
737         _lookup: function() {
738             if (!this.useHash || this._noShim) {
739                 return this.validDrops;
740             }
741             var drops = [];
742             //Only scan drop shims that are in the Viewport
743             Y.each(this.validDrops, function(v, k) {
744                 if (v.shim && v.shim.inViewportRegion(false, v.region)) {
745                     drops[drops.length] = v;
746                 }
747             });
748             return drops;
749                 
750         },
751         /**
752         * @private
753         * @method _handleTargetOver
754         * @description This method execs _handleTargetOver on all valid Drop Targets
755         */
756         _handleTargetOver: function() {
757             var drops = this._lookup();
758             Y.each(drops, function(v, k) {
759                 v._handleTargetOver.call(v);
760             }, this);
761         },
762         /**
763         * @private
764         * @method _regTarget
765         * @description Add the passed in Target to the targets collection
766         * @param {Object} t The Target to add to the targets collection
767         */
768         _regTarget: function(t) {
769             this.targets[this.targets.length] = t;
770         },
771         /**
772         * @private
773         * @method _unregTarget
774         * @description Remove the passed in Target from the targets collection
775         * @param {Object} drop The Target to remove from the targets collection
776         */
777         _unregTarget: function(drop) {
778             var targets = [], vdrops;
779             Y.each(this.targets, function(v, k) {
780                 if (v != drop) {
781                     targets[targets.length] = v;
782                 }
783             }, this);
784             this.targets = targets;
785
786             vdrops = [];
787             Y.each(this.validDrops, function(v, k) {
788                 if (v !== drop) {
789                     vdrops[vdrops.length] = v;
790                 }
791             });
792
793             this.validDrops = vdrops;
794         },
795         /**
796         * @method getDrop
797         * @description Get a valid Drop instance back from a Node or a selector string, false otherwise
798         * @param {String/Object} node The Node instance or Selector string to check for a valid Drop Object
799         * @return {Object}
800         */
801         getDrop: function(node) {
802             var drop = false,
803                 n = Y.Node.get(node);
804             if (n instanceof Y.Node) {
805                 Y.each(this.targets, function(v, k) {
806                     if (n.compareTo(v.get('node'))) {
807                         drop = v;
808                     }
809                 });
810             }
811             return drop;
812         }
813     }, true);
814     
815
816
817
818
819
820
821 }, '3.0.0' ,{requires:['dd-ddm'], skinnable:false});
822 YUI.add('dd-drag', function(Y) {
823
824
825     /**
826      * 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.
827      * @module dd
828      * @submodule dd-drag
829      */     
830     /**
831      * This class provides the ability to drag a Node.
832      * @class Drag
833      * @extends Base
834      * @constructor
835      * @namespace DD
836      */
837
838     var DDM = Y.DD.DDM,
839         NODE = 'node',
840         DRAGGING = 'dragging',
841         DRAG_NODE = 'dragNode',
842         OFFSET_HEIGHT = 'offsetHeight',
843         OFFSET_WIDTH = 'offsetWidth',        
844         MOUSE_UP = 'mouseup',
845         MOUSE_DOWN = 'mousedown',
846         DRAG_START = 'dragstart',
847         /**
848         * @event drag:mouseDown
849         * @description Handles the mousedown DOM event, checks to see if you have a valid handle then starts the drag timers.
850         * @preventable _defMouseDownFn
851         * @param {Event.Facade} ev The mousedown event.
852         * @bubbles DDM
853         * @type {Event.Custom}
854         */
855         EV_MOUSE_DOWN = 'drag:mouseDown',
856         /**
857         * @event drag:afterMouseDown
858         * @description Fires after the mousedown event has been cleared.
859         * @param {Event.Facade} ev The mousedown event.
860         * @bubbles DDM
861         * @type {Event.Custom}
862         */
863         EV_AFTER_MOUSE_DOWN = 'drag:afterMouseDown',
864         /**
865         * @event drag:removeHandle
866         * @description Fires after a handle is removed.
867         * @bubbles DDM
868         * @type {Event.Custom}
869         */
870         EV_REMOVE_HANDLE = 'drag:removeHandle',
871         /**
872         * @event drag:addHandle
873         * @description Fires after a handle is added.
874         * @bubbles DDM
875         * @type {Event.Custom}
876         */
877         EV_ADD_HANDLE = 'drag:addHandle',
878         /**
879         * @event drag:removeInvalid
880         * @description Fires after an invalid selector is removed.
881         * @bubbles DDM
882         * @type {Event.Custom}
883         */
884         EV_REMOVE_INVALID = 'drag:removeInvalid',
885         /**
886         * @event drag:addInvalid
887         * @description Fires after an invalid selector is added.
888         * @bubbles DDM
889         * @type {Event.Custom}
890         */
891         EV_ADD_INVALID = 'drag:addInvalid',
892         /**
893         * @event drag:start
894         * @description Fires at the start of a drag operation.
895         * @bubbles DDM
896         * @type {Event.Custom}
897         */
898         EV_START = 'drag:start',
899         /**
900         * @event drag:end
901         * @description Fires at the end of a drag operation.
902         * @bubbles DDM
903         * @type {Event.Custom}
904         */
905         EV_END = 'drag:end',
906         /**
907         * @event drag:drag
908         * @description Fires every mousemove during a drag operation.
909         * @bubbles DDM
910         * @type {Event.Custom}
911         */
912         EV_DRAG = 'drag:drag',
913         /**
914         * @event drag:align
915         * @preventable _defAlignFn
916         * @description Fires when this node is aligned.
917         * @bubbles DDM
918         * @type {Event.Custom}
919         */
920         EV_ALIGN = 'drag:align',
921         /**
922         * @event drag:over
923         * @description Fires when this node is over a Drop Target. (Fired from dd-drop)
924         * @bubbles DDM
925         * @type {Event.Custom}
926         */
927         /**
928         * @event drag:enter
929         * @description Fires when this node enters a Drop Target. (Fired from dd-drop)
930         * @bubbles DDM
931         * @type {Event.Custom}
932         */
933         /**
934         * @event drag:exit
935         * @description Fires when this node exits a Drop Target. (Fired from dd-drop)
936         * @bubbles DDM
937         * @type {Event.Custom}
938         */
939         /**
940         * @event drag:drophit
941         * @description Fires when this node is dropped on a valid Drop Target. (Fired from dd-ddm-drop)
942         * @bubbles DDM
943         * @type {Event.Custom}
944         */
945         /**
946         * @event drag:dropmiss
947         * @description Fires when this node is dropped on an invalid Drop Target. (Fired from dd-ddm-drop)
948         * @bubbles DDM
949         * @type {Event.Custom}
950         */
951     
952     Drag = function(o) {
953         this._lazyAddAttrs = false;
954         Drag.superclass.constructor.apply(this, arguments);
955
956         var valid = DDM._regDrag(this);
957         if (!valid) {
958             Y.error('Failed to register node, already in use: ' + o.node);
959         }
960     };
961
962     Drag.NAME = 'drag';
963
964     Drag.ATTRS = {
965         /**
966         * @attribute node
967         * @description Y.Node instanace to use as the element to initiate a drag operation
968         * @type Node
969         */
970         node: {
971             setter: function(node) {
972                 var n = Y.get(node);
973                 if (!n) {
974                     Y.error('DD.Drag: Invalid Node Given: ' + node);
975                 } else {
976                     n = n.item(0);
977                 }
978                 return n;
979             }
980         },
981         /**
982         * @attribute dragNode
983         * @description Y.Node instanace to use as the draggable element, defaults to node
984         * @type Node
985         */
986         dragNode: {
987             setter: function(node) {
988                 var n = Y.Node.get(node);
989                 if (!n) {
990                     Y.error('DD.Drag: Invalid dragNode Given: ' + node);
991                 }
992                 return n;
993             }
994         },
995         /**
996         * @attribute offsetNode
997         * @description Offset the drag element by the difference in cursor position: default true
998         * @type Boolean
999         */
1000         offsetNode: {
1001             value: true
1002         },
1003         /**
1004         * @attribute clickPixelThresh
1005         * @description The number of pixels to move to start a drag operation, default is 3.
1006         * @type Number
1007         */
1008         clickPixelThresh: {
1009             value: DDM.get('clickPixelThresh')
1010         },
1011         /**
1012         * @attribute clickTimeThresh
1013         * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
1014         * @type Number
1015         */
1016         clickTimeThresh: {
1017             value: DDM.get('clickTimeThresh')
1018         },
1019         /**
1020         * @attribute lock
1021         * @description Set to lock this drag element so that it can't be dragged: default false.
1022         * @type Boolean
1023         */
1024         lock: {
1025             value: false,
1026             setter: function(lock) {
1027                 if (lock) {
1028                     this.get(NODE).addClass(DDM.CSS_PREFIX + '-locked');
1029                 } else {
1030                     this.get(NODE).removeClass(DDM.CSS_PREFIX + '-locked');
1031                 }
1032                 return lock;
1033             }
1034         },
1035         /**
1036         * @attribute data
1037         * @description A payload holder to store arbitrary data about this drag object, can be used to store any value.
1038         * @type Mixed
1039         */
1040         data: {
1041             value: false
1042         },
1043         /**
1044         * @attribute move
1045         * @description If this is false, the drag element will not move with the cursor: default true. Can be used to "resize" the element.
1046         * @type Boolean
1047         */
1048         move: {
1049             value: true
1050         },
1051         /**
1052         * @attribute useShim
1053         * @description Use the protective shim on all drag operations: default true. Only works with dd-ddm, not dd-ddm-base.
1054         * @type Boolean
1055         */
1056         useShim: {
1057             value: true
1058         },
1059         /**
1060         * @attribute activeHandle
1061         * @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.
1062         * @type Node
1063         */
1064         activeHandle: {
1065             value: false
1066         },
1067         /**
1068         * @attribute primaryButtonOnly
1069         * @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.
1070         * @type Boolean
1071         */
1072         primaryButtonOnly: {
1073             value: true
1074         },
1075         /**
1076         * @attribute dragging
1077         * @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.
1078         * @type Boolean
1079         */
1080         dragging: {
1081             value: false
1082         },
1083         parent: {
1084             value: false
1085         },
1086         /**
1087         * @attribute target
1088         * @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.
1089         * @type Boolean
1090         */
1091         target: {
1092             value: false,
1093             setter: function(config) {
1094                 this._handleTarget(config);
1095                 return config;
1096             }
1097         },
1098         /**
1099         * @attribute dragMode
1100         * @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of this Drag instance.
1101         * @type String
1102         */
1103         dragMode: {
1104             value: null,
1105             setter: function(mode) {
1106                 return DDM._setDragMode(mode);
1107             }
1108         },
1109         /**
1110         * @attribute groups
1111         * @description Array of groups to add this drag into.
1112         * @type Array
1113         */
1114         groups: {
1115             value: ['default'],
1116             getter: function() {
1117                 if (!this._groups) {
1118                     this._groups = {};
1119                 }
1120                 var ret = [];
1121                 Y.each(this._groups, function(v, k) {
1122                     ret[ret.length] = k;
1123                 });
1124                 return ret;
1125             },
1126             setter: function(g) {
1127                 this._groups = {};
1128                 Y.each(g, function(v, k) {
1129                     this._groups[v] = true;
1130                 }, this);
1131                 return g;
1132             }
1133         },
1134         /**
1135         * @attribute handles
1136         * @description Array of valid handles to add. Adding something here will set all handles, even if previously added with addHandle
1137         * @type Array
1138         */
1139         handles: {
1140             value: null,
1141             setter: function(g) {
1142                 if (g) {
1143                     this._handles = {};
1144                     Y.each(g, function(v, k) {
1145                         this._handles[v] = true;
1146                     }, this);
1147                 } else {
1148                     this._handles = null;
1149                 }
1150                 return g;
1151             }
1152         },
1153         /**
1154         * @attribute bubbles
1155         * @description Controls the default bubble parent for this Drag instance. Default: Y.DD.DDM. Set to false to disable bubbling.
1156         * @type Object
1157         */
1158         bubbles: {
1159             writeOnce: true,
1160             value: Y.DD.DDM
1161         }
1162     };
1163
1164     Y.extend(Drag, Y.Base, {
1165         /**
1166         * @method addToGroup
1167         * @description Add this Drag instance to a group, this should be used for on-the-fly group additions.
1168         * @param {String} g The group to add this Drag Instance to.
1169         * @return {Self}
1170         * @chainable
1171         */
1172         addToGroup: function(g) {
1173             this._groups[g] = true;
1174             DDM._activateTargets();
1175             return this;
1176         },
1177         /**
1178         * @method removeFromGroup
1179         * @description Remove this Drag instance from a group, this should be used for on-the-fly group removals.
1180         * @param {String} g The group to remove this Drag Instance from.
1181         * @return {Self}
1182         * @chainable
1183         */
1184         removeFromGroup: function(g) {
1185             delete this._groups[g];
1186             DDM._activateTargets();
1187             return this;
1188         },
1189         /**
1190         * @property target
1191         * @description This will be a reference to the Drop instance associated with this drag if the target: true config attribute is set..
1192         * @type {Object}
1193         */
1194         target: null,
1195         /**
1196         * @private
1197         * @method _handleTarget
1198         * @description Attribute handler for the target config attribute.
1199         * @param {Boolean/Object}
1200         * @return {Boolean/Object}
1201         */
1202         _handleTarget: function(config) {
1203             if (Y.DD.Drop) {
1204                 if (config === false) {
1205                     if (this.target) {
1206                         DDM._unregTarget(this.target);
1207                         this.target = null;
1208                     }
1209                     return false;
1210                 } else {
1211                     if (!Y.Lang.isObject(config)) {
1212                         config = {};
1213                     }
1214                     config.bubbles = ('bubbles' in config) ? config.bubbles : this.get('bubbles');
1215                     config.node = this.get(NODE);
1216                     config.groups = config.groups || this.get('groups');
1217                     this.target = new Y.DD.Drop(config);
1218                 }
1219             } else {
1220                 return false;
1221             }
1222         },
1223         /**
1224         * @private
1225         * @property _groups
1226         * @description Storage Array for the groups this drag belongs to.
1227         * @type {Array}
1228         */
1229         _groups: null,
1230         /**
1231         * @private
1232         * @method _createEvents
1233         * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
1234         */
1235         _createEvents: function() {
1236             
1237             this.publish(EV_MOUSE_DOWN, {
1238                 defaultFn: this._defMouseDownFn,
1239                 queuable: false,
1240                 emitFacade: true,
1241                 bubbles: true,
1242                 prefix: 'drag'
1243             });
1244             
1245             this.publish(EV_ALIGN, {
1246                 defaultFn: this._defAlignFn,
1247                 queuable: false,
1248                 emitFacade: true,
1249                 bubbles: true,
1250                 prefix: 'drag'
1251             });
1252             
1253             this.publish(EV_DRAG, {
1254                 defaultFn: this._defDragFn,
1255                 queuable: false,
1256                 emitFacade: true,
1257                 bubbles: true,
1258                 prefix: 'drag'
1259             });
1260             
1261             this.publish(EV_END, {
1262                 preventedFn: this._prevEndFn,
1263                 queuable: false,
1264                 emitFacade: true,
1265                 bubbles: true,
1266                 prefix: 'drag'
1267             });
1268             
1269             var ev = [
1270                 EV_AFTER_MOUSE_DOWN,
1271                 EV_REMOVE_HANDLE,
1272                 EV_ADD_HANDLE,
1273                 EV_REMOVE_INVALID,
1274                 EV_ADD_INVALID,
1275                 EV_START,
1276                 'drag:drophit',
1277                 'drag:dropmiss',
1278                 'drag:over',
1279                 'drag:enter',
1280                 'drag:exit'
1281             ];
1282             
1283             Y.each(ev, function(v, k) {
1284                 this.publish(v, {
1285                     type: v,
1286                     emitFacade: true,
1287                     bubbles: true,
1288                     preventable: false,
1289                     queuable: false,
1290                     prefix: 'drag'
1291                 });
1292             }, this);
1293
1294             if (this.get('bubbles')) {
1295                 this.addTarget(this.get('bubbles'));
1296             }
1297             
1298            
1299         },
1300         /**
1301         * @private
1302         * @property _ev_md
1303         * @description A private reference to the mousedown DOM event
1304         * @type {Event.Facade}
1305         */
1306         _ev_md: null,
1307         /**
1308         * @private
1309         * @property _startTime
1310         * @description The getTime of the mousedown event. Not used, just here in case someone wants/needs to use it.
1311         * @type Date
1312         */
1313         _startTime: null,
1314         /**
1315         * @private
1316         * @property _endTime
1317         * @description The getTime of the mouseup event. Not used, just here in case someone wants/needs to use it.
1318         * @type Date
1319         */
1320         _endTime: null,
1321         /**
1322         * @private
1323         * @property _handles
1324         * @description A private hash of the valid drag handles
1325         * @type {Object}
1326         */
1327         _handles: null,
1328         /**
1329         * @private
1330         * @property _invalids
1331         * @description A private hash of the invalid selector strings
1332         * @type {Object}
1333         */
1334         _invalids: null,
1335         /**
1336         * @private
1337         * @property _invalidsDefault
1338         * @description A private hash of the default invalid selector strings: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true}
1339         * @type {Object}
1340         */
1341         _invalidsDefault: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true },
1342         /**
1343         * @private
1344         * @property _dragThreshMet
1345         * @description Private flag to see if the drag threshhold was met
1346         * @type {Boolean}
1347         */
1348         _dragThreshMet: null,
1349         /**
1350         * @private
1351         * @property _fromTimeout
1352         * @description Flag to determine if the drag operation came from a timeout
1353         * @type {Boolean}
1354         */
1355         _fromTimeout: null,
1356         /**
1357         * @private
1358         * @property _clickTimeout
1359         * @description Holder for the setTimeout call
1360         * @type {Boolean}
1361         */
1362         _clickTimeout: null,
1363         /**
1364         * @property deltaXY
1365         * @description The offset of the mouse position to the element's position
1366         * @type {Array}
1367         */
1368         deltaXY: null,
1369         /**
1370         * @property startXY
1371         * @description The initial mouse position
1372         * @type {Array}
1373         */
1374         startXY: null,
1375         /**
1376         * @property nodeXY
1377         * @description The initial element position
1378         * @type {Array}
1379         */
1380         nodeXY: null,
1381         /**
1382         * @property lastXY
1383         * @description The position of the element as it's moving (for offset calculations)
1384         * @type {Array}
1385         */
1386         lastXY: null,
1387         /**
1388         * @property actXY
1389         * @description The xy that the node will be set to. Changing this will alter the position as it's dragged.
1390         * @type {Array}
1391         */
1392         actXY: null,
1393         /**
1394         * @property realXY
1395         * @description The real xy position of the node.
1396         * @type {Array}
1397         */
1398         realXY: null,
1399         /**
1400         * @property mouseXY
1401         * @description The XY coords of the mousemove
1402         * @type {Array}
1403         */
1404         mouseXY: null,
1405         /**
1406         * @property region
1407         * @description A region object associated with this drag, used for checking regions while dragging.
1408         * @type Object
1409         */
1410         region: null,       
1411         /**
1412         * @private
1413         * @method _handleMouseUp
1414         * @description Handler for the mouseup DOM event
1415         * @param {Event.Facade}
1416         */
1417         _handleMouseUp: function(ev) {
1418             this._fixIEMouseUp();
1419             if (DDM.activeDrag) {
1420                 DDM._end();
1421             }
1422         },
1423         /** 
1424         * @private
1425         * @method _fixDragStart
1426         * @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.
1427         */
1428         _fixDragStart: function(e) {
1429             e.preventDefault();
1430         },
1431         /** 
1432         * @private
1433         * @method _ieSelectFix
1434         * @description The function we use as the onselectstart handler when we start a drag in Internet Explorer
1435         */
1436         _ieSelectFix: function() {
1437             return false;
1438         },
1439         /** 
1440         * @private
1441         * @property _ieSelectBack
1442         * @description We will hold a copy of the current "onselectstart" method on this property, and reset it after we are done using it.
1443         */
1444         _ieSelectBack: null,
1445         /**
1446         * @private
1447         * @method _fixIEMouseDown
1448         * @description This method copies the onselectstart listner on the document to the _ieSelectFix property
1449         */
1450         _fixIEMouseDown: function() {
1451             if (Y.UA.ie) {
1452                 this._ieSelectBack = Y.config.doc.body.onselectstart;
1453                 Y.config.doc.body.onselectstart = this._ieSelectFix;
1454             }           
1455         },
1456         /**
1457         * @private
1458         * @method _fixIEMouseUp
1459         * @description This method copies the _ieSelectFix property back to the onselectstart listner on the document.
1460         */
1461         _fixIEMouseUp: function() {
1462             if (Y.UA.ie) {
1463                 Y.config.doc.body.onselectstart = this._ieSelectBack;
1464             }           
1465         },
1466         /**
1467         * @private
1468         * @method _handleMouseDownEvent
1469         * @description Handler for the mousedown DOM event
1470         * @param {Event.Facade}
1471         */
1472         _handleMouseDownEvent: function(ev) {
1473             this.fire(EV_MOUSE_DOWN, { ev: ev });
1474         },
1475         /**
1476         * @private
1477         * @method _defMouseDownFn
1478         * @description Handler for the mousedown DOM event
1479         * @param {Event.Facade}
1480         */
1481         _defMouseDownFn: function(e) {
1482             var ev = e.ev;
1483             this._dragThreshMet = false;
1484             this._ev_md = ev;
1485             
1486             if (this.get('primaryButtonOnly') && ev.button > 1) {
1487                 return false;
1488             }
1489             if (this.validClick(ev)) {
1490                 this._fixIEMouseDown();
1491                 ev.halt();
1492                 this._setStartPosition([ev.pageX, ev.pageY]);
1493
1494                 DDM.activeDrag = this;
1495                 
1496                 this._clickTimeout = Y.later(this.get('clickTimeThresh'), this, this._timeoutCheck);
1497             }
1498             this.fire(EV_AFTER_MOUSE_DOWN, { ev: ev });
1499         },
1500         /**
1501         * @method validClick
1502         * @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.
1503         * @param {Event.Facade}
1504         * @return {Boolean}
1505         */
1506         validClick: function(ev) {
1507             var r = false, n = false,
1508             tar = ev.target,
1509             hTest = null,
1510             els = null,
1511             set = false;
1512             if (this._handles) {
1513                 Y.each(this._handles, function(i, n) {
1514                     if (Y.Lang.isString(n)) {
1515                         //Am I this or am I inside this
1516                         if (tar.test(n + ', ' + n + ' *') && !hTest) {
1517                             hTest = n;
1518                             r = true;
1519                         }
1520                     }
1521                 });
1522             } else {
1523                 n = this.get(NODE)
1524                 if (n.contains(tar) || n.compareTo(tar)) {
1525                     r = true;
1526                 }
1527             }
1528             if (r) {
1529                 if (this._invalids) {
1530                     Y.each(this._invalids, function(i, n) {
1531                         if (Y.Lang.isString(n)) {
1532                             //Am I this or am I inside this
1533                             if (tar.test(n + ', ' + n + ' *')) {
1534                                 r = false;
1535                             }
1536                         }
1537                     });
1538                 }
1539             }
1540             if (r) {
1541                 if (hTest) {
1542                     els = ev.currentTarget.queryAll(hTest);
1543                     set = false;
1544                     els.each(function(n, i) {
1545                         if ((n.contains(tar) || n.compareTo(tar)) && !set) {
1546                             set = true;
1547                             this.set('activeHandle', n);
1548                         }
1549                     }, this);
1550                 } else {
1551                     this.set('activeHandle', this.get(NODE));
1552                 }
1553             }
1554             return r;
1555         },
1556         /**
1557         * @private
1558         * @method _setStartPosition
1559         * @description Sets the current position of the Element and calculates the offset
1560         * @param {Array} xy The XY coords to set the position to.
1561         */
1562         _setStartPosition: function(xy) {
1563             this.startXY = xy;
1564             
1565             this.nodeXY = this.lastXY = this.realXY = this.get(NODE).getXY();
1566
1567             if (this.get('offsetNode')) {
1568                 this.deltaXY = [(this.startXY[0] - this.nodeXY[0]), (this.startXY[1] - this.nodeXY[1])];
1569             } else {
1570                 this.deltaXY = [0, 0];
1571             }
1572         },
1573         /**
1574         * @private
1575         * @method _timeoutCheck
1576         * @description The method passed to setTimeout to determine if the clickTimeThreshold was met.
1577         */
1578         _timeoutCheck: function() {
1579             if (!this.get('lock') && !this._dragThreshMet) {
1580                 this._fromTimeout = this._dragThreshMet = true;
1581                 this.start();
1582                 this._alignNode([this._ev_md.pageX, this._ev_md.pageY], true);
1583             }
1584         },
1585         /**
1586         * @method removeHandle
1587         * @description Remove a Selector added by addHandle
1588         * @param {String} str The selector for the handle to be removed. 
1589         * @return {Self}
1590         * @chainable
1591         */
1592         removeHandle: function(str) {
1593             if (this._handles[str]) {
1594                 delete this._handles[str];
1595                 this.fire(EV_REMOVE_HANDLE, { handle: str });
1596             }
1597             return this;
1598         },
1599         /**
1600         * @method addHandle
1601         * @description Add a handle to a drag element. Drag only initiates when a mousedown happens on this element.
1602         * @param {String} str The selector to test for a valid handle. Must be a child of the element.
1603         * @return {Self}
1604         * @chainable
1605         */
1606         addHandle: function(str) {
1607             if (!this._handles) {
1608                 this._handles = {};
1609             }
1610             if (Y.Lang.isString(str)) {
1611                 this._handles[str] = true;
1612                 this.fire(EV_ADD_HANDLE, { handle: str });
1613             }
1614             return this;
1615         },
1616         /**
1617         * @method removeInvalid
1618         * @description Remove an invalid handle added by addInvalid
1619         * @param {String} str The invalid handle to remove from the internal list.
1620         * @return {Self}
1621         * @chainable
1622         */
1623         removeInvalid: function(str) {
1624             if (this._invalids[str]) {
1625                 this._invalids[str] = null;
1626                 delete this._invalids[str];
1627                 this.fire(EV_REMOVE_INVALID, { handle: str });
1628             }
1629             return this;
1630         },
1631         /**
1632         * @method addInvalid
1633         * @description Add a selector string to test the handle against. If the test passes the drag operation will not continue.
1634         * @param {String} str The selector to test against to determine if this is an invalid drag handle.
1635         * @return {Self}
1636         * @chainable
1637         */
1638         addInvalid: function(str) {
1639             if (Y.Lang.isString(str)) {
1640                 this._invalids[str] = true;
1641                 this.fire(EV_ADD_INVALID, { handle: str });
1642             }
1643             return this;
1644         },
1645         /**
1646         * @private
1647         * @method initializer
1648         * @description Internal init handler
1649         */
1650         initializer: function() {
1651             this.get(NODE).dd = this;
1652
1653             if (!this.get(NODE).get('id')) {
1654                 var id = Y.stamp(this.get(NODE));
1655                 this.get(NODE).set('id', id);
1656             }
1657
1658             this.actXY = [];
1659             
1660             this._invalids = Y.clone(this._invalidsDefault, true);
1661
1662             this._createEvents();
1663             
1664             if (!this.get(DRAG_NODE)) {
1665                 this.set(DRAG_NODE, this.get(NODE));
1666             }
1667
1668             //Fix for #2528096
1669             //Don't prep the DD instance until all plugins are loaded.
1670             this.on('initializedChange', Y.bind(this._prep, this));
1671
1672             //Shouldn't have to do this..
1673             this.set('groups', this.get('groups'));
1674         },
1675         /**
1676         * @private
1677         * @method _prep
1678         * @description Attach event listners and add classname
1679         */
1680         _prep: function() {
1681             this._dragThreshMet = false;
1682             var node = this.get(NODE);
1683             node.addClass(DDM.CSS_PREFIX + '-draggable');
1684             node.on(MOUSE_DOWN, Y.bind(this._handleMouseDownEvent, this));
1685             node.on(MOUSE_UP, Y.bind(this._handleMouseUp, this));
1686             node.on(DRAG_START, Y.bind(this._fixDragStart, this));
1687         },
1688         /**
1689         * @private
1690         * @method _unprep
1691         * @description Detach event listeners and remove classname
1692         */
1693         _unprep: function() {
1694             var node = this.get(NODE);
1695             node.removeClass(DDM.CSS_PREFIX + '-draggable');
1696             node.detachAll();
1697         },
1698         /**
1699         * @method start
1700         * @description Starts the drag operation
1701         * @return {Self}
1702         * @chainable
1703         */
1704         start: function() {
1705             if (!this.get('lock') && !this.get(DRAGGING)) {
1706                 var node = this.get(NODE), ow = node.get(OFFSET_WIDTH), oh = node.get(OFFSET_HEIGHT);
1707                 this._startTime = (new Date()).getTime();
1708
1709                 DDM._start();
1710                 node.addClass(DDM.CSS_PREFIX + '-dragging');
1711                 this.fire(EV_START, {
1712                     pageX: this.nodeXY[0],
1713                     pageY: this.nodeXY[1],
1714                     startTime: this._startTime
1715                 });
1716                 var xy = this.nodeXY;
1717
1718                 
1719                 this.region = {
1720                     '0': xy[0], 
1721                     '1': xy[1],
1722                     area: 0,
1723                     top: xy[1],
1724                     right: xy[0] + ow,
1725                     bottom: xy[1] + oh,
1726                     left: xy[0]
1727                 };
1728                 this.set(DRAGGING, true);
1729             }
1730             return this;
1731         },
1732         /**
1733         * @method end
1734         * @description Ends the drag operation
1735         * @return {Self}
1736         * @chainable
1737         */
1738         end: function() {
1739             this._endTime = (new Date()).getTime();
1740             if (this._clickTimeout) {
1741                 this._clickTimeout.cancel();
1742             }
1743             this._dragThreshMet = false;
1744             this._fromTimeout = false;
1745             if (!this.get('lock') && this.get(DRAGGING)) {
1746                 this.fire(EV_END, {
1747                     pageX: this.lastXY[0],
1748                     pageY: this.lastXY[1],
1749                     startTime: this._startTime,
1750                     endTime: this._endTime
1751                 });
1752             }
1753             this.get(NODE).removeClass(DDM.CSS_PREFIX + '-dragging');
1754             this.set(DRAGGING, false);
1755             this.deltaXY = [0, 0];
1756
1757             return this;
1758         },
1759         /**
1760         * @private
1761         * @method _prevEndFn
1762         * @description Handler for preventing the drag:end event. It will reset the node back to it's start position
1763         */
1764         _prevEndFn: function(e) {
1765             //Bug #1852577
1766             this.get(DRAG_NODE).setXY(this.nodeXY);
1767         },
1768         /**
1769         * @private
1770         * @method _align
1771         * @description Calculates the offsets and set's the XY that the element will move to.
1772         * @param {Array} xy The xy coords to align with.
1773         */
1774         _align: function(xy) {
1775             this.fire(EV_ALIGN, {pageX: xy[0], pageY: xy[1] });
1776         },
1777         /**
1778         * @private
1779         * @method _defAlignFn
1780         * @description Calculates the offsets and set's the XY that the element will move to.
1781         * @param {Event.Facade} e The drag:align event.
1782         */
1783         _defAlignFn: function(e) {
1784             this.actXY = [e.pageX - this.deltaXY[0], e.pageY - this.deltaXY[1]];
1785         },
1786         /**
1787         * @private
1788         * @method _alignNode
1789         * @description This method performs the alignment before the element move.
1790         * @param {Array} eXY The XY to move the element to, usually comes from the mousemove DOM event.
1791         */
1792         _alignNode: function(eXY) {
1793             this._align(eXY);
1794             this._moveNode();
1795         },
1796         /**
1797         * @private
1798         * @method _moveNode
1799         * @description This method performs the actual element move.
1800         */
1801         _moveNode: function(scroll) {
1802             //if (!this.get(DRAGGING)) {
1803             //    return;
1804             //}
1805             var diffXY = [], diffXY2 = [], startXY = this.nodeXY, xy = this.actXY;
1806
1807             diffXY[0] = (xy[0] - this.lastXY[0]);
1808             diffXY[1] = (xy[1] - this.lastXY[1]);
1809
1810             diffXY2[0] = (xy[0] - this.nodeXY[0]);
1811             diffXY2[1] = (xy[1] - this.nodeXY[1]);
1812
1813
1814             this.region = {
1815                 '0': xy[0], 
1816                 '1': xy[1],
1817                 area: 0,
1818                 top: xy[1],
1819                 right: xy[0] + this.get(DRAG_NODE).get(OFFSET_WIDTH),
1820                 bottom: xy[1] + this.get(DRAG_NODE).get(OFFSET_HEIGHT),
1821                 left: xy[0]
1822             };
1823
1824             this.fire(EV_DRAG, {
1825                 pageX: xy[0],
1826                 pageY: xy[1],
1827                 scroll: scroll,
1828                 info: {
1829                     start: startXY,
1830                     xy: xy,
1831                     delta: diffXY,
1832                     offset: diffXY2
1833                 } 
1834             });
1835             
1836             this.lastXY = xy;
1837         },
1838         /**
1839         * @private
1840         * @method _defDragFn
1841         * @description Default function for drag:drag. Fired from _moveNode.
1842         * @param {Event.Facade} ev The drag:drag event
1843         */
1844         _defDragFn: function(e) {
1845             if (this.get('move')) {
1846                 if (e.scroll) {
1847                     e.scroll.node.set('scrollTop', e.scroll.top);
1848                     e.scroll.node.set('scrollLeft', e.scroll.left);
1849                 }
1850                 this.get(DRAG_NODE).setXY([e.pageX, e.pageY]);
1851                 this.realXY = [e.pageX, e.pageY];
1852             }
1853         },
1854         /**
1855         * @private
1856         * @method _move
1857         * @description Fired from DragDropMgr (DDM) on mousemove.
1858         * @param {Event.Facade} ev The mousemove DOM event
1859         */
1860         _move: function(ev) {
1861             if (this.get('lock')) {
1862                 return false;
1863             } else {
1864                 this.mouseXY = [ev.pageX, ev.pageY];
1865                 if (!this._dragThreshMet) {
1866                     var diffX = Math.abs(this.startXY[0] - ev.pageX),
1867                     diffY = Math.abs(this.startXY[1] - ev.pageY);
1868                     if (diffX > this.get('clickPixelThresh') || diffY > this.get('clickPixelThresh')) {
1869                         this._dragThreshMet = true;
1870                         this.start();
1871                         this._alignNode([ev.pageX, ev.pageY]);
1872                     }
1873                 } else {
1874                     if (this._clickTimeout) {
1875                         this._clickTimeout.cancel();
1876                     }
1877                     this._alignNode([ev.pageX, ev.pageY]);
1878                 }
1879             }
1880         },
1881         /**
1882         * @method stopDrag
1883         * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
1884         * @return {Self}
1885         * @chainable
1886         */
1887         stopDrag: function() {
1888             if (this.get(DRAGGING)) {
1889                 DDM._end();
1890             }
1891             return this;
1892         },
1893         /**
1894         * @private
1895         * @method destructor
1896         * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
1897         */
1898         destructor: function() {
1899             this._unprep();
1900             this.detachAll();
1901             if (this.target) {
1902                 this.target.destroy();
1903             }
1904             DDM._unregDrag(this);
1905         }
1906     });
1907     Y.namespace('DD');    
1908     Y.DD.Drag = Drag;
1909
1910
1911
1912
1913
1914 }, '3.0.0' ,{requires:['dd-ddm-base'], skinnable:false});
1915 YUI.add('dd-proxy', function(Y) {
1916
1917
1918     /**
1919      * 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.
1920      * @module dd
1921      * @submodule dd-proxy
1922      */
1923     /**
1924      * This plugin for dd-drag is for creating a proxy drag node, instead of dragging the original node.
1925      * @class DDProxy
1926      * @extends Base
1927      * @constructor
1928      * @namespace Plugin     
1929      */
1930     var DDM = Y.DD.DDM,
1931         NODE = 'node',
1932         DRAG_NODE = 'dragNode',
1933         HOST = 'host',
1934         TRUE = true;
1935
1936     var P = function(config) {
1937         P.superclass.constructor.apply(this, arguments);
1938     };
1939     
1940     P.NAME = 'DDProxy';
1941     /**
1942     * @property proxy
1943     * @description The Proxy instance will be placed on the Drag instance under the proxy namespace.
1944     * @type {String}
1945     */
1946     P.NS = 'proxy';
1947
1948     P.ATTRS = {
1949         host: {
1950         },
1951         /**
1952         * @attribute moveOnEnd
1953         * @description Move the original node at the end of the drag. Default: true
1954         * @type Boolean
1955         */
1956         moveOnEnd: {
1957             value: TRUE
1958         },
1959         /**
1960         * @attribute hideOnEnd
1961         * @description Hide the drag node at the end of the drag. Default: true
1962         * @type Boolean
1963         */
1964         hideOnEnd: {
1965             value: TRUE
1966         },
1967         /**
1968         * @attribute resizeFrame
1969         * @description Make the Proxy node assume the size of the original node. Default: true
1970         * @type Boolean
1971         */
1972         resizeFrame: {
1973             value: TRUE
1974         },
1975         /**
1976         * @attribute positionProxy
1977         * @description Make the Proxy node appear in the same place as the original node. Default: true
1978         * @type Boolean
1979         */
1980         positionProxy: {
1981             value: TRUE
1982         },
1983         /**
1984         * @attribute borderStyle
1985         * @description The default border style for the border of the proxy. Default: 1px solid #808080
1986         * @type Boolean
1987         */
1988         borderStyle: {
1989             value: '1px solid #808080'
1990         }
1991     };
1992
1993     var proto = {
1994         /**
1995         * @private
1996         * @property _hands
1997         * @description Holds the event handles for setting the proxy
1998         */
1999         _hands: null,
2000         /**
2001         * @private
2002         * @method _init
2003         * @description Handler for the proxy config attribute
2004         */
2005         _init: function() {
2006             if (!DDM._proxy) {
2007                 Y.on('domready', Y.bind(this._init, this));
2008                 return;
2009             }
2010             if (!this._hands) {
2011                 this._hands = [];
2012             }
2013             var i, h, h1, host = this.get(HOST), dnode = host.get(DRAG_NODE);
2014             if (dnode.compareTo(host.get(NODE))) {
2015                 if (DDM._proxy) {
2016                     host.set(DRAG_NODE, DDM._proxy);
2017                 }
2018             }
2019             Y.each(this._hands, function(v) {
2020                 v.detach();
2021             });
2022             h = DDM.on('ddm:start', Y.bind(function() {
2023                 if (DDM.activeDrag === host) {
2024                     DDM._setFrame(host);
2025                 }
2026             }, this));
2027             h1 = DDM.on('ddm:end', Y.bind(function() {
2028                 if (host.get('dragging')) {
2029                     if (this.get('moveOnEnd')) {
2030                         host.get(NODE).setXY(host.lastXY);
2031                     }
2032                     if (this.get('hideOnEnd')) {
2033                         host.get(DRAG_NODE).setStyle('display', 'none');
2034                     }
2035                 }
2036             }, this));
2037             this._hands = [h, h1];
2038         },
2039         initializer: function() {
2040             this._init();
2041         },
2042         destructor: function() {
2043             var host = this.get(HOST);
2044             Y.each(this._hands, function(v) {
2045                 v.detach();
2046             });
2047             host.set(DRAG_NODE, host.get(NODE));
2048         }
2049     };
2050     
2051     Y.namespace('Plugin');
2052     Y.extend(P, Y.Base, proto);
2053     Y.Plugin.DDProxy = P;
2054
2055     //Add a couple of methods to the DDM
2056     Y.mix(DDM, {
2057         /**
2058         * @private
2059         * @for DDM
2060         * @namespace DD
2061         * @method _createFrame
2062         * @description Create the proxy element if it doesn't already exist and set the DD.DDM._proxy value
2063         */
2064         _createFrame: function() {
2065             if (!DDM._proxy) {
2066                 DDM._proxy = TRUE;
2067
2068                 var p = Y.Node.create('<div></div>'),
2069                 b = Y.Node.get('body');
2070
2071                 p.setStyles({
2072                     position: 'absolute',
2073                     display: 'none',
2074                     zIndex: '999',
2075                     top: '-999px',
2076                     left: '-999px'
2077                 });
2078
2079                 b.insertBefore(p, b.get('firstChild'));
2080                 p.set('id', Y.stamp(p));
2081                 p.addClass(DDM.CSS_PREFIX + '-proxy');
2082                 DDM._proxy = p;
2083             }
2084         },
2085         /**
2086         * @private
2087         * @for DDM
2088         * @namespace DD
2089         * @method _setFrame
2090         * @description If resizeProxy is set to true (default) it will resize the proxy element to match the size of the Drag Element.
2091         * If positionProxy is set to true (default) it will position the proxy element in the same location as the Drag Element.
2092         */
2093         _setFrame: function(drag) {
2094             var n = drag.get(NODE), d = drag.get(DRAG_NODE), ah, cur = 'auto';
2095             if (drag.proxy.get('resizeFrame')) {
2096                 DDM._proxy.setStyles({
2097                     height: n.get('offsetHeight') + 'px',
2098                     width: n.get('offsetWidth') + 'px'
2099                 });
2100             }
2101             
2102             ah = DDM.activeDrag.get('activeHandle');
2103             if (ah) {
2104                 cur = ah.getStyle('cursor');
2105             }
2106             if (cur == 'auto') {
2107                 cur = DDM.get('dragCursor');
2108             }
2109
2110
2111             d.setStyles({
2112                 visibility: 'hidden',
2113                 display: 'block',
2114                 cursor: cur,
2115                 border: drag.proxy.get('borderStyle')
2116             });
2117
2118
2119
2120             if (drag.proxy.get('positionProxy')) {
2121                 d.setXY(drag.nodeXY);
2122             }
2123             d.setStyle('visibility', 'visible');
2124         }
2125     });
2126
2127     //Create the frame when DOM is ready
2128     Y.on('domready', Y.bind(DDM._createFrame, DDM));
2129
2130
2131
2132 }, '3.0.0' ,{requires:['dd-ddm', 'dd-drag'], skinnable:false});
2133 YUI.add('dd-constrain', function(Y) {
2134
2135
2136     /**
2137      * 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.
2138      * @module dd
2139      * @submodule dd-constrain
2140      */
2141     /**
2142      * This is a plugin for the dd-drag module to add the constraining methods to it. It supports constraining to a renodenode or viewport. It anode* supports tick based moves and XY axis constraints.
2143      * @class DragConstrained
2144      * @extends Base
2145      * @constructor
2146      * @namespace Plugin     
2147      */
2148
2149     var DRAG_NODE = 'dragNode',
2150         OFFSET_HEIGHT = 'offsetHeight',
2151         OFFSET_WIDTH = 'offsetWidth',
2152         HOST = 'host',
2153         CON_2_REGION = 'constrain2region',
2154         CON_2_NODE = 'constrain2node',
2155         TICK_X_ARRAY = 'tickXArray',
2156         TICK_Y_ARRAY = 'tickYArray',
2157         DDM = Y.DD.DDM,
2158         TOP = 'top',
2159         RIGHT = 'right',
2160         BOTTOM = 'bottom',
2161         LEFT = 'left',
2162         proto = null;
2163
2164     var C = function(config) {
2165         C.superclass.constructor.apply(this, arguments);
2166     };
2167     
2168     C.NAME = 'DragConstrained';
2169     /**
2170     * @property con
2171     * @description The Constrained instance will be placed on the Drag instance under the con namespace.
2172     * @type {String}
2173     */
2174     C.NS = 'con';
2175
2176     C.ATTRS = {
2177         host: {
2178         },
2179         /**
2180         * @attribute stickX
2181         * @description Stick the drag movement to the X-Axis. Default: false
2182         * @type Boolean
2183         */        
2184         stickX: {
2185             value: false
2186         },
2187         /**
2188         * @attribute stickY
2189         * @description Stick the drag movement to the Y-Axis
2190         * @type Boolean
2191         */        
2192         stickY: {
2193             value: false
2194         },
2195         /**
2196         * @attribute tickX
2197         * @description The X tick offset the drag node should snap to on each drag move. False for no ticks. Default: false
2198         * @type Number/false
2199         */        
2200         tickX: {
2201             value: false
2202         },
2203         /**
2204         * @attribute tickY
2205         * @description The Y tick offset the drag node should snap to on each drag move. False for no ticks. Default: false
2206         * @type Number/false
2207         */        
2208         tickY: {
2209             value: false
2210         },
2211         /**
2212         * @attribute tickXArray
2213         * @description An array of page coordinates to use as X ticks for drag movement.
2214         * @type Array
2215         */
2216         tickXArray: {
2217             value: false
2218         },
2219         /**
2220         * @attribute tickYArray
2221         * @description An array of page coordinates to use as Y ticks for drag movement.
2222         * @type Array
2223         */
2224         tickYArray: {
2225             value: false
2226         },
2227         /**
2228         * @attribute constrain2region
2229         * @description An Object Literal containing a valid region (top, right, bottom, left) of page positions to constrain the drag node to.
2230         * @type Object
2231         */
2232         constrain2region: {
2233             value: false,
2234             getter: function(r) {
2235                 if (Y.Lang.isObject(r)) {
2236                     var o = {};
2237                     Y.mix(o, r);
2238                     return o;
2239                 } else {
2240                     return false;
2241                 }
2242             },
2243             setter: function (r) {
2244                 if (Y.Lang.isObject(r)) {
2245                     if (Y.Lang.isNumber(r[TOP]) && Y.Lang.isNumber(r[RIGHT]) && Y.Lang.isNumber(r[LEFT]) && Y.Lang.isNumber(r[BOTTOM])) {
2246                         var o = {};
2247                         Y.mix(o, r);
2248                         return o;
2249                     } else {
2250                         return false;
2251                     }
2252                 } else if (r !== false) {
2253                     return false;
2254                 }
2255                 return r;
2256             }
2257         },
2258         /**
2259         * @attribute gutter
2260         * @description CSS style string for the gutter of a region (supports negative values): '5 0' (sets top and bottom to 5px, left and right to 0px), '1 2 3 4' (top 1px, right 2px, bottom 3px, left 4px)        
2261         * @type String
2262         */
2263         gutter: {
2264             value: '0',
2265             setter: function(gutter) {
2266                 return Y.DD.DDM.cssSizestoObject(gutter);
2267             }
2268         },
2269         /**
2270         * @attribute constrain2node
2271         * @description Will attempt to constrain the drag node to the boundaries of this node.
2272         * @type Object
2273         */
2274         constrain2node: {
2275             value: false,
2276             setter: function(n) {
2277                 if (!this.get(CON_2_REGION)) {
2278                     var node = Y.Node.get(n);
2279                     if (node) {
2280                         return node;
2281                     }
2282                 } else if (this.get(CON_2_REGION) !== false) {
2283                 }
2284                 return false;
2285             }
2286         },
2287         /**
2288         * @attribute constrain2view
2289         * @description Will attempt to constrain the drag node to the boundaries of the viewport region.
2290         * @type Object
2291         */
2292         constrain2view: {
2293             value: false
2294         }
2295     };
2296
2297     proto = {
2298         initializer: function() {
2299             this.get(HOST).on('drag:start', Y.bind(this._handleStart, this));
2300             this.get(HOST).after('drag:align', Y.bind(this.align, this));
2301         },
2302         /**
2303         * @private
2304         * @method _handleStart
2305         * @description Fires on drag:start and clears the _regionCache
2306         */
2307         _handleStart: function() {
2308             this._regionCache = null;
2309         },
2310         /**
2311         * @private
2312         * @property _regionCache
2313         * @description Store a cache of the region that we are constraining to
2314         * @type Object
2315         */
2316         _regionCache: null,
2317         /**
2318         * @private
2319         * @method _cacheRegion
2320         * @description Get's the region and caches it, called from window.resize and when the cache is null
2321         */
2322         _cacheRegion: function() {
2323             this._regionCache = this.get(CON_2_NODE).get('region');
2324         },
2325         /**
2326         * @method getRegion
2327         * @description Get the active region: viewport, node, custom region
2328         * @param {Boolean} inc Include the node's height and width
2329         * @return {Object}
2330         */
2331         getRegion: function(inc) {
2332             var r = {}, oh = null, ow = null,
2333                 g = this.get('gutter'),
2334                 host = this.get(HOST);
2335
2336             if (this.get(CON_2_NODE)) {
2337                 if (!this._regionCache) {
2338                     Y.on('resize', Y.bind(this._cacheRegion, this), window);
2339                     this._cacheRegion();
2340                 }
2341                 r = Y.clone(this._regionCache);
2342             } else if (this.get(CON_2_REGION)) {
2343                 r = this.get(CON_2_REGION);
2344             } else if (this.get('constrain2view')) {
2345                 r = host.get(DRAG_NODE).get('viewportRegion');
2346             } else {
2347                 return false;
2348             }
2349
2350             Y.each(g, function(i, n) {
2351                 if ((n == RIGHT) || (n == BOTTOM)) {
2352                     r[n] -= i;
2353                 } else {
2354                     r[n] += i;
2355                 }
2356             });
2357             if (inc) {
2358                 oh = host.get(DRAG_NODE).get(OFFSET_HEIGHT);
2359                 ow = host.get(DRAG_NODE).get(OFFSET_WIDTH);
2360                 r[RIGHT] = r[RIGHT] - ow;
2361                 r[BOTTOM] = r[BOTTOM] - oh;
2362             }
2363             return r;
2364         },
2365         /**
2366         * @private
2367         * @method _checkRegion
2368         * @description Check if xy is inside a given region, if not change to it be inside.
2369         * @param {Array} _xy The XY to check if it's in the current region, if it isn't inside the region, it will reset the xy array to be inside the region.
2370         * @return {Array} The new XY that is inside the region
2371         */
2372         _checkRegion: function(_xy) {
2373             var oxy = _xy,
2374                 r = this.getRegion(),
2375                 host = this.get(HOST),
2376                 oh = host.get(DRAG_NODE).get(OFFSET_HEIGHT),
2377                 ow = host.get(DRAG_NODE).get(OFFSET_WIDTH);
2378             
2379                 if (oxy[1] > (r[BOTTOM] - oh)) {
2380                     _xy[1] = (r[BOTTOM] - oh);
2381                 }
2382                 if (r[TOP] > oxy[1]) {
2383                     _xy[1] = r[TOP];
2384
2385                 }
2386                 if (oxy[0] > (r[RIGHT] - ow)) {
2387                     _xy[0] = (r[RIGHT] - ow);
2388                 }
2389                 if (r[LEFT] > oxy[0]) {
2390                     _xy[0] = r[LEFT];
2391                 }
2392
2393             return _xy;
2394         },
2395         /**
2396         * @method inRegion
2397         * @description Checks if the XY passed or the dragNode is inside the active region.
2398         * @param {Array} xy Optional XY to check, if not supplied this.get('dragNode').getXY() is used.
2399         * @return {Boolean} True if the XY is inside the region, false otherwise.
2400         */
2401         inRegion: function(xy) {
2402             xy = xy || this.get(HOST).get(DRAG_NODE).getXY();
2403
2404             var _xy = this._checkRegion([xy[0], xy[1]]),
2405                 inside = false;
2406                 if ((xy[0] === _xy[0]) && (xy[1] === _xy[1])) {
2407                     inside = true;
2408                 }
2409             return inside;
2410         },
2411         /**
2412         * @method align
2413         * @description Modifies the Drag.actXY method from the after drag:align event. This is where the constraining happens.
2414         */
2415         align: function() {
2416             var host = this.get(HOST),
2417                 _xy = host.actXY,
2418                 r = this.getRegion(true);
2419
2420             if (this.get('stickX')) {
2421                 _xy[1] = (host.startXY[1] - host.deltaXY[1]);
2422             }
2423             if (this.get('stickY')) {
2424                 _xy[0] = (host.startXY[0] - host.deltaXY[0]);
2425             }
2426
2427             if (r) {
2428                 _xy = this._checkRegion(_xy);
2429             }
2430                 
2431             _xy = this._checkTicks(_xy, r);
2432             host.actXY = _xy;
2433         },
2434         /**
2435         * @private
2436         * @method _checkTicks
2437         * @description This method delegates the proper helper method for tick calculations
2438         * @param {Array} xy The XY coords for the Drag
2439         * @param {Object} r The optional region that we are bound to.
2440         * @return {Array} The calced XY coords
2441         */
2442         _checkTicks: function(xy, r) {
2443             var host = this.get(HOST),
2444                 lx = (host.startXY[0] - host.deltaXY[0]),
2445                 ly = (host.startXY[1] - host.deltaXY[1]),
2446                 xt = this.get('tickX'),
2447                 yt = this.get('tickY');
2448                 if (xt && !this.get(TICK_X_ARRAY)) {
2449                     xy[0] = DDM._calcTicks(xy[0], lx, xt, r[LEFT], r[RIGHT]);
2450                 }
2451                 if (yt && !this.get(TICK_Y_ARRAY)) {
2452                     xy[1] = DDM._calcTicks(xy[1], ly, yt, r[TOP], r[BOTTOM]);
2453                 }
2454                 if (this.get(TICK_X_ARRAY)) {
2455                     xy[0] = DDM._calcTickArray(xy[0], this.get(TICK_X_ARRAY), r[LEFT], r[RIGHT]);
2456                 }
2457                 if (this.get(TICK_Y_ARRAY)) {
2458                     xy[1] = DDM._calcTickArray(xy[1], this.get(TICK_Y_ARRAY), r[TOP], r[BOTTOM]);
2459                 }
2460
2461             return xy;
2462         }
2463     };
2464
2465     Y.namespace('Plugin');
2466     Y.extend(C, Y.Base, proto);
2467     Y.Plugin.DDConstrained = C;
2468
2469     Y.mix(DDM, {
2470         /**
2471         * @for DDM
2472         * @namespace DD
2473         * @private
2474         * @method _calcTicks
2475         * @description Helper method to calculate the tick offsets for a given position
2476         * @param {Number} pos The current X or Y position
2477         * @param {Number} start The start X or Y position
2478         * @param {Number} tick The X or Y tick increment
2479         * @param {Number} off1 The min offset that we can't pass (region)
2480         * @param {Number} off2 The max offset that we can't pass (region)
2481         * @return {Number} The new position based on the tick calculation
2482         */
2483         _calcTicks: function(pos, start, tick, off1, off2) {
2484             var ix = ((pos - start) / tick),
2485                 min = Math.floor(ix),
2486                 max = Math.ceil(ix);
2487                 if ((min !== 0) || (max !== 0)) {
2488                     if ((ix >= min) && (ix <= max)) {
2489                         pos = (start + (tick * min));
2490                         if (off1 && off2) {
2491                             if (pos < off1) {
2492                                 pos = (start + (tick * (min + 1)));
2493                             }
2494                             if (pos > off2) {
2495                                 pos = (start + (tick * (min - 1)));
2496                             }
2497                         }
2498                     }
2499                 }
2500                 return pos;
2501         },
2502         /**
2503         * @for DDM
2504         * @namespace DD
2505         * @private
2506         * @method _calcTickArray
2507         * @description This method is used with the tickXArray and tickYArray config options
2508         * @param {Number} pos The current X or Y position
2509         * @param {Number} ticks The array containing our custom tick positions.
2510         * @param {Number} off1 The min offset that we can't pass (region)
2511         * @param {Number} off2 The max offset that we can't pass (region)
2512         * @return The tick position
2513         */
2514         _calcTickArray: function(pos, ticks, off1, off2) {
2515             var i = 0, len = ticks.length, next = 0,
2516                 diff1, diff2, ret;
2517
2518             if (!ticks || (ticks.length === 0)) {
2519                 return pos;
2520             } else if (ticks[0] >= pos) {
2521                 return ticks[0];
2522             } else {
2523                 for (i = 0; i < len; i++) {
2524                     next = (i + 1);
2525                     if (ticks[next] && ticks[next] >= pos) {
2526                         diff1 = pos - ticks[i];
2527                         diff2 = ticks[next] - pos;
2528                         ret = (diff2 > diff1) ? ticks[i] : ticks[next];
2529                         if (off1 && off2) {
2530                             if (ret > off2) {
2531                                 if (ticks[i]) {
2532                                     ret = ticks[i];
2533                                 } else {
2534                                     ret = ticks[len - 1];
2535                                 }
2536                             }
2537                         }
2538                         return ret;
2539                     }
2540                     
2541                 }
2542                 return ticks[ticks.length - 1];
2543             }
2544         }
2545     });
2546
2547
2548
2549
2550 }, '3.0.0' ,{requires:['dd-drag'], skinnable:false});
2551 YUI.add('dd-scroll', function(Y) {
2552
2553
2554     /**
2555      * 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.
2556      * @module dd
2557      * @submodule dd-scroll
2558      */
2559     /**
2560      * This class is the base scroller class used to create the Plugin.DDNodeScroll and Plugin.DDWinScroll.
2561      * This class should not be called on it's own, it's designed to be a plugin.
2562      * @class Scroll
2563      * @extends Base
2564      * @namespace DD
2565      * @constructor
2566      */
2567
2568     var S = function() {
2569         S.superclass.constructor.apply(this, arguments);
2570
2571     },
2572     HOST = 'host',
2573     BUFFER = 'buffer',
2574     PARENT_SCROLL = 'parentScroll',
2575     WINDOW_SCROLL = 'windowScroll',
2576     SCROLL_TOP = 'scrollTop',
2577     SCROLL_LEFT = 'scrollLeft',
2578     OFFSET_WIDTH = 'offsetWidth',
2579     OFFSET_HEIGHT = 'offsetHeight';
2580
2581
2582     S.ATTRS = {
2583         /**
2584         * @attribute parentScroll
2585         * @description Internal config option to hold the node that we are scrolling. Should not be set by the developer.
2586         * @type Node
2587         */
2588         parentScroll: {
2589             value: false,
2590             setter: function(node) {
2591                 if (node) {
2592                     return node;
2593                 }
2594                 return false;
2595             }
2596         },
2597         /**
2598         * @attribute buffer
2599         * @description The number of pixels from the edge of the screen to turn on scrolling. Default: 30
2600         * @type Number
2601         */
2602         buffer: {
2603             value: 30
2604         },
2605         /**
2606         * @attribute scrollDelay
2607         * @description The number of milliseconds delay to pass to the auto scroller. Default: 235
2608         * @type Number
2609         */
2610         scrollDelay: {
2611             value: 235
2612         },
2613         /**
2614         * @attribute host
2615         * @description The host we are plugged into.
2616         * @type Object
2617         */
2618         host: {
2619             value: null
2620         },
2621         /**
2622         * @attribute windowScroll
2623         * @description Turn on window scroll support, default: false
2624         * @type Boolean
2625         */
2626         windowScroll: {
2627             value: false
2628         },
2629         /**
2630         * @attribute vertical
2631         * @description Allow vertical scrolling, default: true.
2632         * @type Boolean
2633         */
2634         vertical: {
2635             value: true
2636         },
2637         /**
2638         * @attribute horizontal
2639         * @description Allow horizontal scrolling, default: true.
2640         * @type Boolean
2641         */
2642         horizontal: {
2643             value: true
2644         }
2645     };
2646
2647     Y.extend(S, Y.Base, {
2648         /**
2649         * @private
2650         * @property _scrolling
2651         * @description Tells if we are actively scrolling or not.
2652         * @type Boolean
2653         */
2654         _scrolling: null,
2655         /**
2656         * @private
2657         * @property _vpRegionCache
2658         * @description Cache of the Viewport dims.
2659         * @type Object
2660         */
2661         _vpRegionCache: null,
2662         /**
2663         * @private
2664         * @property _dimCache
2665         * @description Cache of the dragNode dims.
2666         * @type Object
2667         */
2668         _dimCache: null,
2669         /**
2670         * @private
2671         * @property _scrollTimer
2672         * @description Holder for the Timer object returned from Y.later.
2673         * @type {Y.later}
2674         */
2675         _scrollTimer: null,
2676         /**
2677         * @private
2678         * @method _getVPRegion
2679         * @description Sets the _vpRegionCache property with an Object containing the dims from the viewport.
2680         */        
2681         _getVPRegion: function() {
2682             var r = {};
2683             //if (!this._vpRegionCache) {
2684                 var n = this.get(PARENT_SCROLL),
2685                 b = this.get(BUFFER),
2686                 ws = this.get(WINDOW_SCROLL),
2687                 xy = ((ws) ? [] : n.getXY()),
2688                 w = ((ws) ? 'winWidth' : OFFSET_WIDTH),
2689                 h = ((ws) ? 'winHeight' : OFFSET_HEIGHT),
2690                 t = ((ws) ? n.get(SCROLL_TOP) : xy[1]),
2691                 l = ((ws) ? n.get(SCROLL_LEFT) : xy[0]);
2692
2693                 r = {
2694                     top: t + b,
2695                     right: (n.get(w) + l) - b,
2696                     bottom: (n.get(h) + t) - b,
2697                     left: l + b
2698                 };
2699                 this._vpRegionCache = r;
2700             //} else {
2701             //    r = this._vpRegionCache;
2702             //}
2703             return r;
2704         },
2705         initializer: function() {
2706             var h = this.get(HOST);
2707             h.after('drag:start', Y.bind(this.start, this));
2708             h.after('drag:end', Y.bind(this.end, this));
2709             h.on('drag:align', Y.bind(this.align, this));
2710
2711             //TODO - This doesn't work yet??
2712             Y.get(window).on('scroll', Y.bind(function() {
2713                 this._vpRegionCache = null;
2714             }, this));
2715         },
2716         /**
2717         * @private
2718         * @method _checkWinScroll
2719         * @description Check to see if we need to fire the scroll timer. If scroll timer is running this will scroll the window.
2720         * @param {Boolean} move Should we move the window. From Y.later
2721         */        
2722         _checkWinScroll: function(move) {
2723             var r = this._getVPRegion(),
2724                 ho = this.get(HOST),
2725                 ws = this.get(WINDOW_SCROLL),
2726                 xy = ho.lastXY,
2727                 scroll = false,
2728                 b = this.get(BUFFER),
2729                 win = this.get(PARENT_SCROLL),
2730                 sTop = win.get(SCROLL_TOP),
2731                 sLeft = win.get(SCROLL_LEFT),
2732                 w = this._dimCache.w,
2733                 h = this._dimCache.h,
2734                 bottom = xy[1] + h,
2735                 top = xy[1],
2736                 right = xy[0] + w,
2737                 left = xy[0],
2738                 nt = top,
2739                 nl = left,
2740                 st = sTop,
2741                 sl = sLeft;
2742             
2743             if (this.get('horizontal')) {
2744                 if (left <= r.left) {
2745                     scroll = true;
2746                     nl = xy[0] - ((ws) ? b : 0);
2747                     sl = sLeft - b;
2748                 }
2749                 if (right >= r.right) {
2750                     scroll = true;
2751                     nl = xy[0] + ((ws) ? b : 0);
2752                     sl = sLeft + b;
2753                 }
2754             }
2755             if (this.get('vertical')) {
2756                 if (bottom >= r.bottom) {
2757                     scroll = true;
2758                     nt = xy[1] + ((ws) ? b : 0);
2759                     st = sTop + b;
2760
2761                 }
2762                 if (top <= r.top) {
2763                     scroll = true;
2764                     nt = xy[1] - ((ws) ? b : 0);
2765                     st = sTop - b;
2766                 }
2767             }
2768
2769             if (st < 0) {
2770                 st = 0;
2771                 nt = xy[1];
2772             }
2773
2774             if (sl < 0) {
2775                 sl = 0;
2776                 nl = xy[0];
2777             }
2778
2779             if (nt < 0) {
2780                 nt = xy[1];
2781             }
2782             if (nl < 0) {
2783                 nl = xy[0];
2784             }
2785             if (move) {
2786                 ho.actXY = [nl, nt];
2787                 ho._moveNode({ node: win, top: st, left: sl});
2788                 if (!st && !sl) {
2789                     this._cancelScroll();
2790                 }
2791             } else {
2792                 if (scroll) {
2793                     this._initScroll();
2794                 } else {
2795                     this._cancelScroll();
2796                 }
2797             }
2798         },
2799         /**
2800         * @private
2801         * @method _initScroll
2802         * @description Cancel a previous scroll timer and init a new one.
2803         */        
2804         _initScroll: function() {
2805             this._cancelScroll();
2806             this._scrollTimer = Y.Lang.later(this.get('scrollDelay'), this, this._checkWinScroll, [true], true);
2807
2808         },
2809         /**
2810         * @private
2811         * @method _cancelScroll
2812         * @description Cancel a currently running scroll timer.
2813         */        
2814         _cancelScroll: function() {
2815             this._scrolling = false;
2816             if (this._scrollTimer) {
2817                 this._scrollTimer.cancel();
2818                 delete this._scrollTimer;
2819             }
2820         },
2821         /**
2822         * @method align
2823         * @description Called from the drag:align event to determine if we need to scroll.
2824         */        
2825         align: function(e) {
2826             if (this._scrolling) {
2827                 this._cancelScroll();
2828                 e.preventDefault();
2829             }
2830             if (!this._scrolling) {
2831                 this._checkWinScroll();
2832             }
2833         },
2834         /**
2835         * @private
2836         * @method _setDimCache
2837         * @description Set the cache of the dragNode dims.
2838         */        
2839         _setDimCache: function() {
2840             var node = this.get(HOST).get('dragNode');
2841             this._dimCache = {
2842                 h: node.get(OFFSET_HEIGHT),
2843                 w: node.get(OFFSET_WIDTH)
2844             };
2845         },
2846         /**
2847         * @method start
2848         * @description Called from the drag:start event
2849         */
2850         start: function() {
2851             this._setDimCache();
2852         },
2853         /**
2854         * @method end
2855         * @description Called from the drag:end event
2856         */
2857         end: function(xy) {
2858             this._dimCache = null;
2859             this._cancelScroll();
2860         },
2861         /**
2862         * @method toString
2863         * @description General toString method for logging
2864         * @return String name for the object
2865         */
2866         toString: function() {
2867             return S.NAME + ' #' + this.get('node').get('id');
2868         }
2869     });
2870
2871     Y.namespace('Plugin');
2872
2873     
2874     /**
2875      * Extends the Scroll class to make the window scroll while dragging.
2876      * @class DDWindowScroll
2877      * @extends DD.Scroll
2878      * @namespace Plugin
2879      * @constructor
2880      */
2881     var WS = function() {
2882         WS.superclass.constructor.apply(this, arguments);
2883     };
2884     WS.ATTRS = Y.merge(S.ATTRS, {
2885         /**
2886         * @attribute windowScroll
2887         * @description Turn on window scroll support, default: true
2888         * @type Boolean
2889         */
2890         windowScroll: {
2891             value: true,
2892             setter: function(scroll) {
2893                 if (scroll) {
2894                     this.set(PARENT_SCROLL, Y.get(window));
2895                 }
2896                 return scroll;
2897             }
2898         }
2899     });
2900     Y.extend(WS, S, {
2901         //Shouldn't have to do this..
2902         initializer: function() {
2903             this.set('windowScroll', this.get('windowScroll'));
2904         }
2905     });
2906     WS.NAME = WS.NS = 'winscroll';
2907     Y.Plugin.DDWinScroll = WS;
2908     
2909
2910     /**
2911      * Extends the Scroll class to make a parent node scroll while dragging.
2912      * @class DDNodeScroll
2913      * @extends DD.Scroll
2914      * @namespace Plugin
2915      * @constructor
2916      */
2917     var NS = function() {
2918         NS.superclass.constructor.apply(this, arguments);
2919
2920     };
2921     NS.ATTRS = Y.merge(S.ATTRS, {
2922         /**
2923         * @attribute node
2924         * @description The node we want to scroll. Used to set the internal parentScroll attribute.
2925         * @type Node
2926         */
2927         node: {
2928             value: false,
2929             setter: function(node) {
2930                 var n = Y.get(node);
2931                 if (!n) {
2932                     if (node !== false) {
2933                         Y.error('DDNodeScroll: Invalid Node Given: ' + node);
2934                     }
2935                 } else {
2936                     n = n.item(0);
2937                     this.set(PARENT_SCROLL, n);
2938                 }
2939                 return n;
2940             }
2941         }
2942     });
2943     Y.extend(NS, S, {
2944         //Shouldn't have to do this..
2945         initializer: function() {
2946             this.set('node', this.get('node'));
2947         }
2948     });
2949     NS.NAME = NS.NS = 'nodescroll';
2950     Y.Plugin.DDNodeScroll = NS;
2951
2952     Y.DD.Scroll = S;    
2953
2954
2955
2956 }, '3.0.0' ,{skinnable:false, requires:['dd-drag'], optional:['dd-proxy']});
2957 YUI.add('dd-plugin', function(Y) {
2958
2959
2960        /**
2961         * This is a simple Drag plugin that can be attached to a Node via the plug method.
2962         * @module dd
2963         * @submodule dd-plugin
2964         */
2965        /**
2966         * This is a simple Drag plugin that can be attached to a Node via the plug method.
2967         * @class Drag
2968         * @extends DD.Drag
2969         * @constructor
2970         * @namespace Plugin
2971         */
2972
2973
2974         var Drag = function(config) {
2975             config.node = ((Y.Widget && config.host instanceof Y.Widget) ? config.host.get('boundingBox') : config.host);
2976             Drag.superclass.constructor.apply(this, arguments);
2977         };
2978         
2979         /**
2980         * @property NAME
2981         * @description dd-plugin
2982         * @type {String}
2983         */
2984         Drag.NAME = "dd-plugin";
2985
2986         /**
2987         * @property NS
2988         * @description The Drag instance will be placed on the Node instance under the dd namespace. It can be accessed via Node.dd;
2989         * @type {String}
2990         */
2991         Drag.NS = "dd";
2992
2993
2994         Y.extend(Drag, Y.DD.Drag);
2995         Y.namespace('Plugin');
2996         Y.Plugin.Drag = Drag;
2997
2998
2999
3000
3001
3002 }, '3.0.0' ,{skinnable:false, requires:['dd-drag'], optional:['dd-constrain', 'dd-proxy']});
3003 YUI.add('dd-drop', function(Y) {
3004
3005
3006     /**
3007      * 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.
3008      * @module dd
3009      * @submodule dd-drop
3010      */     
3011     /**
3012      * This class provides the ability to create a Drop Target.
3013      * @class Drop
3014      * @extends Base
3015      * @constructor
3016      * @namespace DD
3017      */
3018
3019     var NODE = 'node',
3020         DDM = Y.DD.DDM,
3021         OFFSET_HEIGHT = 'offsetHeight',
3022         OFFSET_WIDTH = 'offsetWidth',
3023         /**
3024         * @event drop:over
3025         * @description Fires when a drag element is over this target.
3026         * @bubbles DDM
3027         * @type {Event.Custom}
3028         */
3029         EV_DROP_OVER = 'drop:over',
3030         /**
3031         * @event drop:enter
3032         * @description Fires when a drag element enters this target.
3033         * @bubbles DDM
3034         * @type {Event.Custom}
3035         */
3036         EV_DROP_ENTER = 'drop:enter',
3037         /**
3038         * @event drop:exit
3039         * @description Fires when a drag element exits this target.
3040         * @bubbles DDM
3041         * @type {Event.Custom}
3042         */
3043         EV_DROP_EXIT = 'drop:exit',
3044
3045         /**
3046         * @event drop:hit
3047         * @description Fires when a draggable node is dropped on this Drop Target. (Fired from dd-ddm-drop)
3048         * @bubbles DDM
3049         * @type {Event.Custom}
3050         */
3051         
3052
3053     Drop = function() {
3054         this._lazyAddAttrs = false;
3055         Drop.superclass.constructor.apply(this, arguments);
3056
3057
3058         //DD init speed up.
3059         Y.on('domready', Y.bind(function() {
3060             Y.later(100, this, this._createShim);
3061         }, this));
3062         DDM._regTarget(this);
3063
3064         /* TODO
3065         if (Dom.getStyle(this.el, 'position') == 'fixed') {
3066             Event.on(window, 'scroll', function() {
3067                 this.activateShim();
3068             }, this, true);
3069         }
3070         */
3071     };
3072
3073     Drop.NAME = 'drop';
3074
3075     Drop.ATTRS = {
3076         /**
3077         * @attribute node
3078         * @description Y.Node instanace to use as the element to make a Drop Target
3079         * @type Node
3080         */        
3081         node: {
3082             setter: function(node) {
3083                 var n = Y.Node.get(node);
3084                 if (!n) {
3085                     Y.error('DD.Drop: Invalid Node Given: ' + node);
3086                 }
3087                 return n;               
3088             }
3089         },
3090         /**
3091         * @attribute groups
3092         * @description Array of groups to add this drop into.
3093         * @type Array
3094         */        
3095         groups: {
3096             value: ['default'],
3097             setter: function(g) {
3098                 this._groups = {};
3099                 Y.each(g, function(v, k) {
3100                     this._groups[v] = true;
3101                 }, this);
3102                 return g;
3103             }
3104         },   
3105         /**
3106         * @attribute padding
3107         * @description CSS style padding to make the Drop Target bigger than the node.
3108         * @type String
3109         */
3110         padding: {
3111             value: '0',
3112             setter: function(p) {
3113                 return DDM.cssSizestoObject(p);
3114             }
3115         },
3116         /**
3117         * @attribute lock
3118         * @description Set to lock this drop element.
3119         * @type Boolean
3120         */        
3121         lock: {
3122             value: false,
3123             setter: function(lock) {
3124                 if (lock) {
3125                     this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-locked');
3126                 } else {
3127                     this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-locked');
3128                 }
3129                 return lock;
3130             }
3131         },
3132         /**
3133         * @attribute bubbles
3134         * @description Controls the default bubble parent for this Drop instance. Default: Y.DD.DDM. Set to false to disable bubbling.
3135         * @type Object
3136         */
3137         bubbles: {
3138             writeOnce: true,
3139             value: Y.DD.DDM
3140         }
3141     };
3142
3143     Y.extend(Drop, Y.Base, {
3144         /**
3145         * @private
3146         * @method _createEvents
3147         * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
3148         */
3149         _createEvents: function() {
3150             
3151             var ev = [
3152                 EV_DROP_OVER,
3153                 EV_DROP_ENTER,
3154                 EV_DROP_EXIT,
3155                 'drop:hit'
3156             ];
3157
3158             Y.each(ev, function(v, k) {
3159                 this.publish(v, {
3160                     type: v,
3161                     emitFacade: true,
3162                     preventable: false,
3163                     bubbles: true,
3164                     queuable: false,
3165                     prefix: 'drop'
3166                 });
3167             }, this);
3168
3169             if (this.get('bubbles')) {
3170                 this.addTarget(this.get('bubbles'));
3171             }
3172             
3173         },
3174         /**
3175         * @private
3176         * @property _valid
3177         * @description Flag for determining if the target is valid in this operation.
3178         * @type Boolean
3179         */
3180         _valid: null,
3181         /**
3182         * @private
3183         * @property _groups
3184         * @description The groups this target belongs to.
3185         * @type Array
3186         */
3187         _groups: null,
3188         /**
3189         * @property shim
3190         * @description Node reference to the targets shim
3191         * @type {Object}
3192         */
3193         shim: null,
3194         /**
3195         * @property region
3196         * @description A region object associated with this target, used for checking regions while dragging.
3197         * @type Object
3198         */
3199         region: null,
3200         /**
3201         * @property overTarget
3202         * @description This flag is tripped when a drag element is over this target.
3203         * @type Boolean
3204         */
3205         overTarget: null,
3206         /**
3207         * @method inGroup
3208         * @description Check if this target is in one of the supplied groups.
3209         * @param {Array} groups The groups to check against
3210         * @return Boolean
3211         */
3212         inGroup: function(groups) {
3213             this._valid = false;
3214             var ret = false;
3215             Y.each(groups, function(v, k) {
3216                 if (this._groups[v]) {
3217                     ret = true;
3218                     this._valid = true;
3219                 }
3220             }, this);
3221             return ret;
3222         },
3223         /**
3224         * @private
3225         * @method initializer
3226         * @description Private lifecycle method
3227         */
3228         initializer: function() {
3229             //this._createEvents();
3230             Y.later(100, this, this._createEvents);
3231
3232             var node = this.get(NODE), id;
3233             if (!node.get('id')) {
3234                 id = Y.stamp(node);
3235                 node.set('id', id);
3236             }
3237             node.addClass(DDM.CSS_PREFIX + '-drop');
3238             //Shouldn't have to do this..
3239             this.set('groups', this.get('groups'));           
3240         },
3241         /**
3242         * @private
3243         * @method destructor
3244         * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
3245         */
3246         destructor: function() {
3247             DDM._unregTarget(this);
3248             if (this.shim) {
3249                 this.shim.detachAll();
3250                 this.shim.get('parentNode').removeChild(this.shim);
3251                 this.shim = null;
3252             }
3253             this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop');
3254             this.detachAll();
3255         },
3256         /**
3257         * @private
3258         * @method _deactivateShim
3259         * @description Removes classes from the target, resets some flags and sets the shims deactive position [-999, -999]
3260         */
3261         _deactivateShim: function() {
3262             if (!this.shim) {
3263                 return false;
3264             }
3265             this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
3266             this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
3267             this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
3268             this.shim.setStyles({
3269                 top: '-999px',
3270                 left: '-999px',
3271                 zIndex: '1'
3272             });
3273             this.overTarget = false;
3274         },
3275         /**
3276         * @private
3277         * @method _activateShim
3278         * @description Activates the shim and adds some interaction CSS classes
3279         */
3280         _activateShim: function() {
3281             if (!DDM.activeDrag) {
3282                 return false; //Nothing is dragging, no reason to activate.
3283             }
3284             if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
3285                 return false;
3286             }
3287             if (this.get('lock')) {
3288                 return false;
3289             }
3290             var node = this.get(NODE);
3291             //TODO Visibility Check..
3292             //if (this.inGroup(DDM.activeDrag.get('groups')) && this.get(NODE).isVisible()) {
3293             if (this.inGroup(DDM.activeDrag.get('groups'))) {
3294                 node.removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
3295                 node.addClass(DDM.CSS_PREFIX + '-drop-active-valid');
3296                 DDM._addValid(this);
3297                 this.overTarget = false;
3298                 this.sizeShim();
3299             } else {
3300                 DDM._removeValid(this);
3301                 node.removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
3302                 node.addClass(DDM.CSS_PREFIX + '-drop-active-invalid');
3303             }
3304         },
3305         /**
3306         * @method sizeShim
3307         * @description Positions and sizes the shim with the raw data from the node, this can be used to programatically adjust the Targets shim for Animation..
3308         */
3309         sizeShim: function() {
3310             if (!DDM.activeDrag) {
3311                 return false; //Nothing is dragging, no reason to activate.
3312             }
3313             if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
3314                 return false;
3315             }
3316             if (this.get('lock')) {
3317                 return false;
3318             }
3319             if (!this.shim) {
3320                 Y.later(100, this, this.sizeShim);
3321                 return false;
3322             }
3323             var node = this.get(NODE),
3324                 nh = node.get(OFFSET_HEIGHT),
3325                 nw = node.get(OFFSET_WIDTH),
3326                 xy = node.getXY(),
3327                 p = this.get('padding'),
3328                 dd, dH, dW;
3329
3330
3331             //Apply padding
3332             nw = nw + p.left + p.right;
3333             nh = nh + p.top + p.bottom;
3334             xy[0] = xy[0] - p.left;
3335             xy[1] = xy[1] - p.top;
3336             
3337
3338             if (DDM.activeDrag.get('dragMode') === DDM.INTERSECT) {
3339                 //Intersect Mode, make the shim bigger
3340                 dd = DDM.activeDrag;
3341                 dH = dd.get(NODE).get(OFFSET_HEIGHT);
3342                 dW = dd.get(NODE).get(OFFSET_WIDTH);
3343                 
3344                 nh = (nh + dH);
3345                 nw = (nw + dW);
3346                 xy[0] = xy[0] - (dW - dd.deltaXY[0]);
3347                 xy[1] = xy[1] - (dH - dd.deltaXY[1]);
3348
3349             }
3350             
3351             //Set the style on the shim
3352             this.shim.setStyles({
3353                 height: nh + 'px',
3354                 width: nw + 'px',
3355                 top: xy[1] + 'px',
3356                 left: xy[0] + 'px'
3357             });
3358             
3359             //Create the region to be used by intersect when a drag node is over us.
3360             this.region = {
3361                 '0': xy[0], 
3362                 '1': xy[1],
3363                 area: 0,
3364                 top: xy[1],
3365                 right: xy[0] + nw,
3366                 bottom: xy[1] + nh,
3367                 left: xy[0]
3368             };
3369         },
3370         /**
3371         * @private
3372         * @method _createShim
3373         * @description Creates the Target shim and adds it to the DDM's playground..
3374         */
3375         _createShim: function() {
3376             //No playground, defer
3377             if (!DDM._pg) {
3378                 Y.later(10, this, this._createShim);
3379                 return;
3380             }
3381             //Shim already here, cancel
3382             if (this.shim) {
3383                 return;
3384             }
3385             var s = Y.Node.create('<div id="' + this.get(NODE).get('id') + '_shim"></div>');
3386
3387             s.setStyles({
3388                 height: this.get(NODE).get(OFFSET_HEIGHT) + 'px',
3389                 width: this.get(NODE).get(OFFSET_WIDTH) + 'px',
3390                 backgroundColor: 'yellow',
3391                 opacity: '.5',
3392                 zIndex: '1',
3393                 overflow: 'hidden',
3394                 top: '-900px',
3395                 left: '-900px',
3396                 position:  'absolute'
3397             });
3398             DDM._pg.appendChild(s);
3399             this.shim = s;
3400
3401             s.on('mouseover', Y.bind(this._handleOverEvent, this));
3402             s.on('mouseout', Y.bind(this._handleOutEvent, this));
3403         },
3404         /**
3405         * @private
3406         * @method _handleOverTarget
3407         * @description This handles the over target call made from this object or from the DDM
3408         */
3409         _handleTargetOver: function() {
3410             if (DDM.isOverTarget(this)) {
3411                 this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-over');
3412                 DDM.activeDrop = this;
3413                 DDM.otherDrops[this] = this;
3414                 if (this.overTarget) {
3415                     DDM.activeDrag.fire('drag:over', { drop: this, drag: DDM.activeDrag });
3416                     this.fire(EV_DROP_OVER, { drop: this, drag: DDM.activeDrag });
3417                 } else {
3418                     this.overTarget = true;
3419                     this.fire(EV_DROP_ENTER, { drop: this, drag: DDM.activeDrag });
3420                     DDM.activeDrag.fire('drag:enter', { drop: this, drag: DDM.activeDrag });
3421                     DDM.activeDrag.get(NODE).addClass(DDM.CSS_PREFIX + '-drag-over');
3422                     //TODO - Is this needed??
3423                     //DDM._handleTargetOver();
3424                 }
3425             } else {
3426                 this._handleOut();
3427             }
3428         },
3429         /**
3430         * @private
3431         * @method _handleOverEvent
3432         * @description Handles the mouseover DOM event on the Target Shim
3433         */
3434         _handleOverEvent: function() {
3435             this.shim.setStyle('zIndex', '999');
3436             DDM._addActiveShim(this);
3437         },
3438         /**
3439         * @private
3440         * @method _handleOutEvent
3441         * @description Handles the mouseout DOM event on the Target Shim
3442         */
3443         _handleOutEvent: function() {
3444             this.shim.setStyle('zIndex', '1');
3445             DDM._removeActiveShim(this);
3446         },
3447         /**
3448         * @private
3449         * @method _handleOut
3450         * @description Handles out of target calls/checks
3451         */
3452         _handleOut: function(force) {
3453             if (!DDM.isOverTarget(this) || force) {
3454                 if (this.overTarget) {
3455                     this.overTarget = false;
3456                     if (!force) {
3457                         DDM._removeActiveShim(this);
3458                     }
3459                     if (DDM.activeDrag) {
3460                         this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
3461                         DDM.activeDrag.get(NODE).removeClass(DDM.CSS_PREFIX + '-drag-over');
3462                         this.fire(EV_DROP_EXIT);
3463                         DDM.activeDrag.fire('drag:exit', { drop: this });
3464                         delete DDM.otherDrops[this];
3465                         //if (DDM.activeDrop === this) {
3466                         //    DDM.activeDrop = null;
3467                         //}
3468                     }
3469                 }
3470             }
3471         }
3472     });
3473
3474     Y.DD.Drop = Drop;
3475
3476
3477
3478
3479
3480 }, '3.0.0' ,{requires:['dd-ddm-drop', 'dd-drag'], skinnable:false});
3481 YUI.add('dd-drop-plugin', function(Y) {
3482
3483
3484        /**
3485         * This is a simple Drop plugin that can be attached to a Node via the plug method.
3486         * @module dd
3487         * @submodule dd-drop-plugin
3488         */
3489        /**
3490         * This is a simple Drop plugin that can be attached to a Node via the plug method.
3491         * @class Drop
3492         * @extends DD.Drop
3493         * @constructor
3494         * @namespace Plugin
3495         */
3496
3497
3498         var Drop = function(config) {
3499             config.node = config.host;
3500             Drop.superclass.constructor.apply(this, arguments);
3501         };
3502         
3503         /**
3504         * @property NAME
3505         * @description dd-drop-plugin
3506         * @type {String}
3507         */
3508         Drop.NAME = "dd-drop-plugin";
3509         /**
3510         * @property NS
3511         * @description The Drop instance will be placed on the Node instance under the drop namespace. It can be accessed via Node.drop;
3512         * @type {String}
3513         */
3514         Drop.NS = "drop";
3515
3516
3517         Y.extend(Drop, Y.DD.Drop);
3518         Y.namespace('Plugin');
3519         Y.Plugin.Drop = Drop;
3520
3521
3522
3523
3524
3525 }, '3.0.0' ,{requires:['dd-drop'], skinnable:false});
3526
3527
3528 YUI.add('dd', function(Y){}, '3.0.0' ,{use:['dd-ddm-base', 'dd-ddm', 'dd-ddm-drop', 'dd-drag', 'dd-proxy', 'dd-constrain', 'dd-plugin', 'dd-drop', 'dd-drop-plugin', 'dd-scroll'], skinnable:false});
3529