|
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.0b1 |
|
6 build: 1163 |
|
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: '2' |
|
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.0b1' ,{requires:['dd-ddm-drop', 'dd-drag'], skinnable:false}); |