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