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