]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/dd/dd-drop.js
Release 6.2.0beta4
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / dd / dd-drop.js
1 /*
2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 3.0.0
6 build: 1549
7 */
8 YUI.add('dd-drop', function(Y) {
9
10
11     /**
12      * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
13      * @module dd
14      * @submodule dd-drop
15      */     
16     /**
17      * This class provides the ability to create a Drop Target.
18      * @class Drop
19      * @extends Base
20      * @constructor
21      * @namespace DD
22      */
23
24     var NODE = 'node',
25         DDM = Y.DD.DDM,
26         OFFSET_HEIGHT = 'offsetHeight',
27         OFFSET_WIDTH = 'offsetWidth',
28         /**
29         * @event drop:over
30         * @description Fires when a drag element is over this target.
31         * @bubbles DDM
32         * @type {Event.Custom}
33         */
34         EV_DROP_OVER = 'drop:over',
35         /**
36         * @event drop:enter
37         * @description Fires when a drag element enters this target.
38         * @bubbles DDM
39         * @type {Event.Custom}
40         */
41         EV_DROP_ENTER = 'drop:enter',
42         /**
43         * @event drop:exit
44         * @description Fires when a drag element exits this target.
45         * @bubbles DDM
46         * @type {Event.Custom}
47         */
48         EV_DROP_EXIT = 'drop:exit',
49
50         /**
51         * @event drop:hit
52         * @description Fires when a draggable node is dropped on this Drop Target. (Fired from dd-ddm-drop)
53         * @bubbles DDM
54         * @type {Event.Custom}
55         */
56         
57
58     Drop = function() {
59         this._lazyAddAttrs = false;
60         Drop.superclass.constructor.apply(this, arguments);
61
62
63         //DD init speed up.
64         Y.on('domready', Y.bind(function() {
65             Y.later(100, this, this._createShim);
66         }, this));
67         DDM._regTarget(this);
68
69         /* TODO
70         if (Dom.getStyle(this.el, 'position') == 'fixed') {
71             Event.on(window, 'scroll', function() {
72                 this.activateShim();
73             }, this, true);
74         }
75         */
76     };
77
78     Drop.NAME = 'drop';
79
80     Drop.ATTRS = {
81         /**
82         * @attribute node
83         * @description Y.Node instanace to use as the element to make a Drop Target
84         * @type Node
85         */        
86         node: {
87             setter: function(node) {
88                 var n = Y.Node.get(node);
89                 if (!n) {
90                     Y.error('DD.Drop: Invalid Node Given: ' + node);
91                 }
92                 return n;               
93             }
94         },
95         /**
96         * @attribute groups
97         * @description Array of groups to add this drop into.
98         * @type Array
99         */        
100         groups: {
101             value: ['default'],
102             setter: function(g) {
103                 this._groups = {};
104                 Y.each(g, function(v, k) {
105                     this._groups[v] = true;
106                 }, this);
107                 return g;
108             }
109         },   
110         /**
111         * @attribute padding
112         * @description CSS style padding to make the Drop Target bigger than the node.
113         * @type String
114         */
115         padding: {
116             value: '0',
117             setter: function(p) {
118                 return DDM.cssSizestoObject(p);
119             }
120         },
121         /**
122         * @attribute lock
123         * @description Set to lock this drop element.
124         * @type Boolean
125         */        
126         lock: {
127             value: false,
128             setter: function(lock) {
129                 if (lock) {
130                     this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-locked');
131                 } else {
132                     this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-locked');
133                 }
134                 return lock;
135             }
136         },
137         /**
138         * @attribute bubbles
139         * @description Controls the default bubble parent for this Drop instance. Default: Y.DD.DDM. Set to false to disable bubbling.
140         * @type Object
141         */
142         bubbles: {
143             writeOnce: true,
144             value: Y.DD.DDM
145         }
146     };
147
148     Y.extend(Drop, Y.Base, {
149         /**
150         * @private
151         * @method _createEvents
152         * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
153         */
154         _createEvents: function() {
155             
156             var ev = [
157                 EV_DROP_OVER,
158                 EV_DROP_ENTER,
159                 EV_DROP_EXIT,
160                 'drop:hit'
161             ];
162
163             Y.each(ev, function(v, k) {
164                 this.publish(v, {
165                     type: v,
166                     emitFacade: true,
167                     preventable: false,
168                     bubbles: true,
169                     queuable: false,
170                     prefix: 'drop'
171                 });
172             }, this);
173
174             if (this.get('bubbles')) {
175                 this.addTarget(this.get('bubbles'));
176             }
177             
178         },
179         /**
180         * @private
181         * @property _valid
182         * @description Flag for determining if the target is valid in this operation.
183         * @type Boolean
184         */
185         _valid: null,
186         /**
187         * @private
188         * @property _groups
189         * @description The groups this target belongs to.
190         * @type Array
191         */
192         _groups: null,
193         /**
194         * @property shim
195         * @description Node reference to the targets shim
196         * @type {Object}
197         */
198         shim: null,
199         /**
200         * @property region
201         * @description A region object associated with this target, used for checking regions while dragging.
202         * @type Object
203         */
204         region: null,
205         /**
206         * @property overTarget
207         * @description This flag is tripped when a drag element is over this target.
208         * @type Boolean
209         */
210         overTarget: null,
211         /**
212         * @method inGroup
213         * @description Check if this target is in one of the supplied groups.
214         * @param {Array} groups The groups to check against
215         * @return Boolean
216         */
217         inGroup: function(groups) {
218             this._valid = false;
219             var ret = false;
220             Y.each(groups, function(v, k) {
221                 if (this._groups[v]) {
222                     ret = true;
223                     this._valid = true;
224                 }
225             }, this);
226             return ret;
227         },
228         /**
229         * @private
230         * @method initializer
231         * @description Private lifecycle method
232         */
233         initializer: function() {
234             //this._createEvents();
235             Y.later(100, this, this._createEvents);
236
237             var node = this.get(NODE), id;
238             if (!node.get('id')) {
239                 id = Y.stamp(node);
240                 node.set('id', id);
241             }
242             node.addClass(DDM.CSS_PREFIX + '-drop');
243             //Shouldn't have to do this..
244             this.set('groups', this.get('groups'));           
245         },
246         /**
247         * @private
248         * @method destructor
249         * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
250         */
251         destructor: function() {
252             DDM._unregTarget(this);
253             if (this.shim) {
254                 this.shim.detachAll();
255                 this.shim.get('parentNode').removeChild(this.shim);
256                 this.shim = null;
257             }
258             this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop');
259             this.detachAll();
260         },
261         /**
262         * @private
263         * @method _deactivateShim
264         * @description Removes classes from the target, resets some flags and sets the shims deactive position [-999, -999]
265         */
266         _deactivateShim: function() {
267             if (!this.shim) {
268                 return false;
269             }
270             this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
271             this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
272             this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
273             this.shim.setStyles({
274                 top: '-999px',
275                 left: '-999px',
276                 zIndex: '1'
277             });
278             this.overTarget = false;
279         },
280         /**
281         * @private
282         * @method _activateShim
283         * @description Activates the shim and adds some interaction CSS classes
284         */
285         _activateShim: function() {
286             if (!DDM.activeDrag) {
287                 return false; //Nothing is dragging, no reason to activate.
288             }
289             if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
290                 return false;
291             }
292             if (this.get('lock')) {
293                 return false;
294             }
295             var node = this.get(NODE);
296             //TODO Visibility Check..
297             //if (this.inGroup(DDM.activeDrag.get('groups')) && this.get(NODE).isVisible()) {
298             if (this.inGroup(DDM.activeDrag.get('groups'))) {
299                 node.removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
300                 node.addClass(DDM.CSS_PREFIX + '-drop-active-valid');
301                 DDM._addValid(this);
302                 this.overTarget = false;
303                 this.sizeShim();
304             } else {
305                 DDM._removeValid(this);
306                 node.removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
307                 node.addClass(DDM.CSS_PREFIX + '-drop-active-invalid');
308             }
309         },
310         /**
311         * @method sizeShim
312         * @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..
313         */
314         sizeShim: function() {
315             if (!DDM.activeDrag) {
316                 return false; //Nothing is dragging, no reason to activate.
317             }
318             if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
319                 return false;
320             }
321             if (this.get('lock')) {
322                 return false;
323             }
324             if (!this.shim) {
325                 Y.later(100, this, this.sizeShim);
326                 return false;
327             }
328             var node = this.get(NODE),
329                 nh = node.get(OFFSET_HEIGHT),
330                 nw = node.get(OFFSET_WIDTH),
331                 xy = node.getXY(),
332                 p = this.get('padding'),
333                 dd, dH, dW;
334
335
336             //Apply padding
337             nw = nw + p.left + p.right;
338             nh = nh + p.top + p.bottom;
339             xy[0] = xy[0] - p.left;
340             xy[1] = xy[1] - p.top;
341             
342
343             if (DDM.activeDrag.get('dragMode') === DDM.INTERSECT) {
344                 //Intersect Mode, make the shim bigger
345                 dd = DDM.activeDrag;
346                 dH = dd.get(NODE).get(OFFSET_HEIGHT);
347                 dW = dd.get(NODE).get(OFFSET_WIDTH);
348                 
349                 nh = (nh + dH);
350                 nw = (nw + dW);
351                 xy[0] = xy[0] - (dW - dd.deltaXY[0]);
352                 xy[1] = xy[1] - (dH - dd.deltaXY[1]);
353
354             }
355             
356             //Set the style on the shim
357             this.shim.setStyles({
358                 height: nh + 'px',
359                 width: nw + 'px',
360                 top: xy[1] + 'px',
361                 left: xy[0] + 'px'
362             });
363             
364             //Create the region to be used by intersect when a drag node is over us.
365             this.region = {
366                 '0': xy[0], 
367                 '1': xy[1],
368                 area: 0,
369                 top: xy[1],
370                 right: xy[0] + nw,
371                 bottom: xy[1] + nh,
372                 left: xy[0]
373             };
374         },
375         /**
376         * @private
377         * @method _createShim
378         * @description Creates the Target shim and adds it to the DDM's playground..
379         */
380         _createShim: function() {
381             //No playground, defer
382             if (!DDM._pg) {
383                 Y.later(10, this, this._createShim);
384                 return;
385             }
386             //Shim already here, cancel
387             if (this.shim) {
388                 return;
389             }
390             var s = Y.Node.create('<div id="' + this.get(NODE).get('id') + '_shim"></div>');
391
392             s.setStyles({
393                 height: this.get(NODE).get(OFFSET_HEIGHT) + 'px',
394                 width: this.get(NODE).get(OFFSET_WIDTH) + 'px',
395                 backgroundColor: 'yellow',
396                 opacity: '.5',
397                 zIndex: '1',
398                 overflow: 'hidden',
399                 top: '-900px',
400                 left: '-900px',
401                 position:  'absolute'
402             });
403             DDM._pg.appendChild(s);
404             this.shim = s;
405
406             s.on('mouseover', Y.bind(this._handleOverEvent, this));
407             s.on('mouseout', Y.bind(this._handleOutEvent, this));
408         },
409         /**
410         * @private
411         * @method _handleOverTarget
412         * @description This handles the over target call made from this object or from the DDM
413         */
414         _handleTargetOver: function() {
415             if (DDM.isOverTarget(this)) {
416                 this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-over');
417                 DDM.activeDrop = this;
418                 DDM.otherDrops[this] = this;
419                 if (this.overTarget) {
420                     DDM.activeDrag.fire('drag:over', { drop: this, drag: DDM.activeDrag });
421                     this.fire(EV_DROP_OVER, { drop: this, drag: DDM.activeDrag });
422                 } else {
423                     this.overTarget = true;
424                     this.fire(EV_DROP_ENTER, { drop: this, drag: DDM.activeDrag });
425                     DDM.activeDrag.fire('drag:enter', { drop: this, drag: DDM.activeDrag });
426                     DDM.activeDrag.get(NODE).addClass(DDM.CSS_PREFIX + '-drag-over');
427                     //TODO - Is this needed??
428                     //DDM._handleTargetOver();
429                 }
430             } else {
431                 this._handleOut();
432             }
433         },
434         /**
435         * @private
436         * @method _handleOverEvent
437         * @description Handles the mouseover DOM event on the Target Shim
438         */
439         _handleOverEvent: function() {
440             this.shim.setStyle('zIndex', '999');
441             DDM._addActiveShim(this);
442         },
443         /**
444         * @private
445         * @method _handleOutEvent
446         * @description Handles the mouseout DOM event on the Target Shim
447         */
448         _handleOutEvent: function() {
449             this.shim.setStyle('zIndex', '1');
450             DDM._removeActiveShim(this);
451         },
452         /**
453         * @private
454         * @method _handleOut
455         * @description Handles out of target calls/checks
456         */
457         _handleOut: function(force) {
458             if (!DDM.isOverTarget(this) || force) {
459                 if (this.overTarget) {
460                     this.overTarget = false;
461                     if (!force) {
462                         DDM._removeActiveShim(this);
463                     }
464                     if (DDM.activeDrag) {
465                         this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
466                         DDM.activeDrag.get(NODE).removeClass(DDM.CSS_PREFIX + '-drag-over');
467                         this.fire(EV_DROP_EXIT);
468                         DDM.activeDrag.fire('drag:exit', { drop: this });
469                         delete DDM.otherDrops[this];
470                         //if (DDM.activeDrop === this) {
471                         //    DDM.activeDrop = null;
472                         //}
473                     }
474                 }
475             }
476         }
477     });
478
479     Y.DD.Drop = Drop;
480
481
482
483
484
485 }, '3.0.0' ,{requires:['dd-ddm-drop', 'dd-drag'], skinnable:false});