2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
8 YUI.add('dd-ddm-base', function(Y) {
12 * Provides the base Drag Drop Manger required for making a Node draggable.
14 * @submodule dd-ddm-base
17 * Provides the base Drag Drop Manger required for making a Node draggable.
24 var DDMBase = function() {
25 DDMBase.superclass.constructor.apply(this, arguments);
32 * @attribute dragCursor
33 * @description The cursor to apply when dragging, if shimmed the shim will get the cursor.
40 * @attribute clickPixelThresh
41 * @description The number of pixels to move to start a drag operation, default is 3.
48 * @attribute clickTimeThresh
49 * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
56 * @attribute throttleTime
57 * @description The number of milliseconds to throttle the mousemove event. Default: 150
66 * @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of all future Drag instances.
71 setter: function(mode) {
72 this._setDragMode(mode);
79 Y.extend(DDMBase, Y.Base, {
80 _createPG: function() {},
83 * @description flag set when we activate our first drag, so DDM can start listening for events.
89 * @method _setDragMode
90 * @description Handler for dragMode attribute setter.
91 * @param String/Number The Number value or the String for the DragMode to default all future drag instances to.
92 * @return Number The Mode to be set
94 _setDragMode: function(mode) {
96 mode = Y.DD.DDM.get('dragMode');
112 * @property CSS_PREFIX
113 * @description The PREFIX to attach to all DD CSS class names
116 CSS_PREFIX: Y.ClassNameManager.getClassName('dd'),
117 _activateTargets: function() {},
121 * @description Holder for all registered drag elements.
126 * @property activeDrag
127 * @description A reference to the currently active draggable object.
134 * @description Adds a reference to the drag object to the DDM._drags array, called in the constructor of Drag.
135 * @param {Drag} d The Drag object
137 _regDrag: function(d) {
138 if (this.getDrag(d.get('node'))) {
143 this._setupListeners();
151 * @description Remove this drag object from the DDM._drags array.
152 * @param {Drag} d The drag object.
154 _unregDrag: function(d) {
156 Y.each(this._drags, function(n, i) {
165 * @method _setupListeners
166 * @description Add the document listeners.
168 _setupListeners: function() {
172 var doc = Y.one(Y.config.doc);
173 doc.on('mousemove', Y.throttle(Y.bind(this._move, this), this.get('throttleTime')));
174 doc.on('mouseup', Y.bind(this._end, this));
179 * @description Internal method used by Drag to signal the start of a drag operation
182 this.fire('ddm:start');
188 * @description Factory method to be overwritten by other DDM's
189 * @param {Number} x The x position of the drag element
190 * @param {Number} y The y position of the drag element
191 * @param {Number} w The width of the drag element
192 * @param {Number} h The height of the drag element
194 _startDrag: function() {},
198 * @description Factory method to be overwritten by other DDM's
200 _endDrag: function() {},
201 _dropMove: function() {},
205 * @description Internal method used by Drag to signal the end of a drag operation
208 if (this.activeDrag) {
210 this.fire('ddm:end');
211 this.activeDrag.end.call(this.activeDrag);
212 this.activeDrag = null;
217 * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
221 stopDrag: function() {
222 if (this.activeDrag) {
230 * @description Internal listener for the mousemove DOM event to pass to the Drag's move method.
231 * @param {Event.Facade} ev The Dom mousemove Event
233 _move: function(ev) {
234 if (this.activeDrag) {
235 this.activeDrag._move.call(this.activeDrag, ev);
240 * //TODO Private, rename??...
242 * @method cssSizestoObject
243 * @description Helper method to use to set the gutter from the attribute setter.
244 * @param {String} gutter CSS style string for gutter: '5 0' (sets top and bottom to 5px, left and right to 0px), '1 2 3 4' (top 1px, right 2px, bottom 3px, left 4px)
245 * @return {Object} The gutter Object Literal.
247 cssSizestoObject: function(gutter) {
248 var x = gutter.split(' ');
251 case 1: x[1] = x[2] = x[3] = x[0]; break;
252 case 2: x[2] = x[0]; x[3] = x[1]; break;
253 case 3: x[3] = x[1]; break;
257 top : parseInt(x[0],10),
258 right : parseInt(x[1],10),
259 bottom: parseInt(x[2],10),
260 left : parseInt(x[3],10)
265 * @description Get a valid Drag instance back from a Node or a selector string, false otherwise
266 * @param {String/Object} node The Node instance or Selector string to check for a valid Drag Object
269 getDrag: function(node) {
272 if (n instanceof Y.Node) {
273 Y.each(this._drags, function(v, k) {
274 if (n.compareTo(v.get('node'))) {
282 * @method swapPosition
283 * @description Swap the position of 2 nodes based on their CSS positioning.
284 * @param {Node} n1 The first node to swap
285 * @param {Node} n2 The first node to swap
288 swapPosition: function(n1, n2) {
289 n1 = Y.DD.DDM.getNode(n1);
290 n2 = Y.DD.DDM.getNode(n2);
291 var xy1 = n1.getXY(),
300 * @description Return a node instance from the given node, selector string or Y.Base extended object.
301 * @param {Node/Object/String} n The node to resolve.
304 getNode: function(n) {
306 if (Y.Widget && (n instanceof Y.Widget)) {
307 n = n.get('boundingBox');
318 * @description Swap the position of 2 nodes based on their DOM location.
319 * @param {Node} n1 The first node to swap
320 * @param {Node} n2 The first node to swap
323 swapNode: function(n1, n2) {
324 n1 = Y.DD.DDM.getNode(n1);
325 n2 = Y.DD.DDM.getNode(n2);
326 var p = n2.get('parentNode'),
327 s = n2.get('nextSibling');
330 p.insertBefore(n1, n2);
331 } else if (n2 == n1.get('nextSibling')) {
332 p.insertBefore(n2, n1);
334 n1.get('parentNode').replaceChild(n2, n1);
335 p.insertBefore(n1, s);
342 Y.DD.DDM = new DDMBase();
346 * @description Fires from the DDM before all drag events fire.
347 * @type {Event.Custom}
351 * @description Fires from the DDM after the DDM finishes, before the drag end events.
352 * @type {Event.Custom}
358 }, '3.3.0' ,{requires:['node', 'base', 'yui-throttle', 'classnamemanager'], skinnable:false});
359 YUI.add('dd-ddm', function(Y) {
363 * Extends the dd-ddm-base Class to add support for the viewport shim to allow a draggable node to drag to be dragged over an iframe or any other node that traps mousemove events.
364 * It is also required to have Drop Targets enabled, as the viewport shim will contain the shims for the Drop Targets.
374 * @description The shim placed over the screen to track the mousemove event.
380 * @property _debugShim
381 * @description Set this to true to set the shims opacity to .5 for debugging it, default: false.
385 _activateTargets: function() { },
386 _deactivateTargets: function() {},
387 _startDrag: function() {
388 if (this.activeDrag && this.activeDrag.get('useShim')) {
390 this._activateTargets();
393 _endDrag: function() {
394 this._pg_deactivate();
395 this._deactivateTargets();
399 * @method _pg_deactivate
400 * @description Deactivates the shim
402 _pg_deactivate: function() {
403 this._pg.setStyle('display', 'none');
407 * @method _pg_activate
408 * @description Activates the shim
410 _pg_activate: function() {
411 var ah = this.activeDrag.get('activeHandle'), cur = 'auto';
413 cur = ah.getStyle('cursor');
416 cur = this.get('dragCursor');
424 opacity: ((this._debugShim) ? '.5' : '0'),
431 * @description Sizes the shim on: activatation, window:scroll, window:resize
433 _pg_size: function() {
434 if (this.activeDrag) {
435 var b = Y.one('body'),
436 h = b.get('docHeight'),
437 w = b.get('docWidth');
447 * @description Creates the shim and adds it's listeners to it.
449 _createPG: function() {
450 var pg = Y.Node.create('<div></div>'),
451 bd = Y.one('body'), win;
455 position: 'absolute',
458 backgroundColor: 'red',
463 pg.set('id', Y.stamp(pg));
464 pg.addClass(Y.DD.DDM.CSS_PREFIX + '-shim');
467 this._pg.on('mousemove', Y.throttle(Y.bind(this._move, this), this.get('throttleTime')));
468 this._pg.on('mouseup', Y.bind(this._end, this));
471 Y.on('window:resize', Y.bind(this._pg_size, this));
472 win.on('scroll', Y.bind(this._pg_size, this));
479 }, '3.3.0' ,{requires:['dd-ddm-base', 'event-resize'], skinnable:false});
480 YUI.add('dd-ddm-drop', function(Y) {
484 * Extends the dd-ddm Class to add support for the placement of Drop Target shims inside the viewport shim. It also handles all Drop Target related events and interactions.
486 * @submodule dd-ddm-drop
491 //TODO CSS class name for the bestMatch..
496 * @description This flag turns off the use of the mouseover/mouseout shim. It should not be used unless you know what you are doing.
502 * @property _activeShims
503 * @description Placeholder for all active shims on the page
509 * @method _hasActiveShim
510 * @description This method checks the _activeShims Object to see if there is a shim active.
513 _hasActiveShim: function() {
517 return this._activeShims.length;
521 * @method _addActiveShim
522 * @description Adds a Drop Target to the list of active shims
523 * @param {Object} d The Drop instance to add to the list.
525 _addActiveShim: function(d) {
526 this._activeShims[this._activeShims.length] = d;
530 * @method _removeActiveShim
531 * @description Removes a Drop Target to the list of active shims
532 * @param {Object} d The Drop instance to remove from the list.
534 _removeActiveShim: function(d) {
536 Y.each(this._activeShims, function(v, k) {
537 if (v._yuid !== d._yuid) {
542 this._activeShims = s;
545 * @method syncActiveShims
546 * @description This method will sync the position of the shims on the Drop Targets that are currently active.
547 * @param {Boolean} force Resize/sync all Targets.
549 syncActiveShims: function(force) {
550 Y.later(0, this, function(force) {
551 var drops = ((force) ? this.targets : this._lookup());
552 Y.each(drops, function(v, k) {
560 * @description The mode that the drag operations will run in 0 for Point, 1 for Intersect, 2 for Strict
567 * @description In point mode, a Drop is targeted by the cursor being over the Target
573 * @property INTERSECT
574 * @description In intersect mode, a Drop is targeted by "part" of the drag node being over the Target
581 * @description In strict mode, a Drop is targeted by the "entire" drag node being over the Target
587 * @description Should we only check targets that are in the viewport on drags (for performance), default: true
592 * @property activeDrop
593 * @description A reference to the active Drop Target
598 * @property validDrops
599 * @description An array of the valid Drop Targets for this interaction.
602 //TODO Change array/object literals to be in sync..
605 * @property otherDrops
606 * @description An object literal of Other Drop Targets that we encountered during this interaction (in the case of overlapping Drop Targets)
612 * @description All of the Targets
619 * @description Add a Drop Target to the list of Valid Targets. This list get's regenerated on each new drag operation.
620 * @param {Object} drop
624 _addValid: function(drop) {
625 this.validDrops[this.validDrops.length] = drop;
630 * @method _removeValid
631 * @description Removes a Drop Target from the list of Valid Targets. This list get's regenerated on each new drag operation.
632 * @param {Object} drop
636 _removeValid: function(drop) {
638 Y.each(this.validDrops, function(v, k) {
640 drops[drops.length] = v;
644 this.validDrops = drops;
648 * @method isOverTarget
649 * @description Check to see if the Drag element is over the target, method varies on current mode
650 * @param {Object} drop The drop to check against
653 isOverTarget: function(drop) {
654 if (this.activeDrag && drop) {
655 var xy = this.activeDrag.mouseXY, r, dMode = this.activeDrag.get('dragMode'),
656 aRegion, node = drop.shim;
657 if (xy && this.activeDrag) {
658 aRegion = this.activeDrag.region;
659 if (dMode == this.STRICT) {
660 return this.activeDrag.get('dragNode').inRegion(drop.region, true, aRegion);
662 if (drop && drop.shim) {
663 if ((dMode == this.INTERSECT) && this._noShim) {
664 r = ((aRegion) ? aRegion : this.activeDrag.get('node'));
665 return drop.get('node').intersect(r, drop.region).inRegion;
668 node = drop.get('node');
670 return node.intersect({
675 }, drop.region).inRegion;
690 * @description Clears the cache data used for this interaction.
692 clearCache: function() {
693 this.validDrops = [];
694 this.otherDrops = {};
695 this._activeShims = [];
699 * @method _activateTargets
700 * @description Clear the cache and activate the shims of all the targets
702 _activateTargets: function() {
705 Y.each(this.targets, function(v, k) {
707 if (v.get('noShim') == true) {
708 this._noShim = false;
711 this._handleTargetOver();
715 * @method getBestMatch
716 * @description This method will gather the area for all potential targets and see which has the hightest covered area and return it.
717 * @param {Array} drops An Array of drops to scan for the best match.
718 * @param {Boolean} all If present, it returns an Array. First item is best match, second is an Array of the other items in the original Array.
719 * @return {Object or Array}
721 getBestMatch: function(drops, all) {
722 var biggest = null, area = 0, out;
724 Y.each(drops, function(v, k) {
725 var inter = this.activeDrag.get('dragNode').intersect(v.get('node'));
726 v.region.area = inter.area;
728 if (inter.inRegion) {
729 if (inter.area > area) {
737 //TODO Sort the others in numeric order by area covered..
738 Y.each(drops, function(v, k) {
743 return [biggest, out];
750 * @method _deactivateTargets
751 * @description This method fires the drop:hit, drag:drophit, drag:dropmiss methods and deactivates the shims..
753 _deactivateTargets: function() {
755 activeDrag = this.activeDrag,
756 activeDrop = this.activeDrop;
758 //TODO why is this check so hard??
759 if (activeDrag && activeDrop && this.otherDrops[activeDrop]) {
760 if (!activeDrag.get('dragMode')) {
761 //TODO otherDrops -- private..
762 other = this.otherDrops;
763 delete other[activeDrop];
765 tmp = this.getBestMatch(this.otherDrops, true);
769 activeDrag.get('node').removeClass(this.CSS_PREFIX + '-drag-over');
771 activeDrop.fire('drop:hit', { drag: activeDrag, drop: activeDrop, others: other });
772 activeDrag.fire('drag:drophit', { drag: activeDrag, drop: activeDrop, others: other });
774 } else if (activeDrag && activeDrag.get('dragging')) {
775 activeDrag.get('node').removeClass(this.CSS_PREFIX + '-drag-over');
776 activeDrag.fire('drag:dropmiss', { pageX: activeDrag.lastXY[0], pageY: activeDrag.lastXY[1] });
780 this.activeDrop = null;
782 Y.each(this.targets, function(v, k) {
783 v._deactivateShim([]);
789 * @description This method is called when the move method is called on the Drag Object.
791 _dropMove: function() {
792 if (this._hasActiveShim()) {
793 this._handleTargetOver();
795 Y.each(this.otherDrops, function(v, k) {
796 v._handleOut.apply(v, []);
803 * @description Filters the list of Drops down to those in the viewport.
804 * @return {Array} The valid Drop Targets that are in the viewport.
806 _lookup: function() {
807 if (!this.useHash || this._noShim) {
808 return this.validDrops;
811 //Only scan drop shims that are in the Viewport
812 Y.each(this.validDrops, function(v, k) {
813 if (v.shim && v.shim.inViewportRegion(false, v.region)) {
814 drops[drops.length] = v;
822 * @method _handleTargetOver
823 * @description This method execs _handleTargetOver on all valid Drop Targets
825 _handleTargetOver: function() {
826 var drops = this._lookup();
827 Y.each(drops, function(v, k) {
828 v._handleTargetOver.call(v);
834 * @description Add the passed in Target to the targets collection
835 * @param {Object} t The Target to add to the targets collection
837 _regTarget: function(t) {
838 this.targets[this.targets.length] = t;
842 * @method _unregTarget
843 * @description Remove the passed in Target from the targets collection
844 * @param {Object} drop The Target to remove from the targets collection
846 _unregTarget: function(drop) {
847 var targets = [], vdrops;
848 Y.each(this.targets, function(v, k) {
850 targets[targets.length] = v;
853 this.targets = targets;
856 Y.each(this.validDrops, function(v, k) {
858 vdrops[vdrops.length] = v;
862 this.validDrops = vdrops;
866 * @description Get a valid Drop instance back from a Node or a selector string, false otherwise
867 * @param {String/Object} node The Node instance or Selector string to check for a valid Drop Object
870 getDrop: function(node) {
873 if (n instanceof Y.Node) {
874 Y.each(this.targets, function(v, k) {
875 if (n.compareTo(v.get('node'))) {
890 }, '3.3.0' ,{requires:['dd-ddm'], skinnable:false});
891 YUI.add('dd-drag', function(Y) {
895 * Provides the ability to drag a Node.
900 * Provides the ability to drag a Node.
909 DRAGGING = 'dragging',
910 DRAG_NODE = 'dragNode',
911 OFFSET_HEIGHT = 'offsetHeight',
912 OFFSET_WIDTH = 'offsetWidth',
914 * @event drag:mouseup
915 * @description Handles the mouseup DOM event, does nothing internally just fires.
917 * @type {Event.Custom}
920 * @event drag:mouseDown
921 * @description Handles the mousedown DOM event, checks to see if you have a valid handle then starts the drag timers.
922 * @preventable _defMouseDownFn
923 * @param {Event.Facade} event An Event Facade object with the following specific property added:
924 * <dl><dt>ev</dt><dd>The original mousedown event.</dd></dl>
926 * @type {Event.Custom}
928 EV_MOUSE_DOWN = 'drag:mouseDown',
930 * @event drag:afterMouseDown
931 * @description Fires after the mousedown event has been cleared.
932 * @param {Event.Facade} event An Event Facade object with the following specific property added:
933 * <dl><dt>ev</dt><dd>The original mousedown event.</dd></dl>
935 * @type {Event.Custom}
937 EV_AFTER_MOUSE_DOWN = 'drag:afterMouseDown',
939 * @event drag:removeHandle
940 * @description Fires after a handle is removed.
941 * @param {Event.Facade} event An Event Facade object with the following specific property added:
942 * <dl><dt>handle</dt><dd>The handle that was removed.</dd></dl>
944 * @type {Event.Custom}
946 EV_REMOVE_HANDLE = 'drag:removeHandle',
948 * @event drag:addHandle
949 * @description Fires after a handle is added.
950 * @param {Event.Facade} event An Event Facade object with the following specific property added:
951 * <dl><dt>handle</dt><dd>The handle that was added.</dd></dl>
953 * @type {Event.Custom}
955 EV_ADD_HANDLE = 'drag:addHandle',
957 * @event drag:removeInvalid
958 * @description Fires after an invalid selector is removed.
959 * @param {Event.Facade} event An Event Facade object with the following specific property added:
960 * <dl><dt>handle</dt><dd>The handle that was removed.</dd></dl>
962 * @type {Event.Custom}
964 EV_REMOVE_INVALID = 'drag:removeInvalid',
966 * @event drag:addInvalid
967 * @description Fires after an invalid selector is added.
968 * @param {Event.Facade} event An Event Facade object with the following specific property added:
969 * <dl><dt>handle</dt><dd>The handle that was added.</dd></dl>
971 * @type {Event.Custom}
973 EV_ADD_INVALID = 'drag:addInvalid',
976 * @description Fires at the start of a drag operation.
977 * @param {Event.Facade} event An Event Facade object with the following specific property added:
979 * <dt>pageX</dt><dd>The original node position X.</dd>
980 * <dt>pageY</dt><dd>The original node position Y.</dd>
981 * <dt>startTime</dt><dd>The startTime of the event. getTime on the current Date object.</dd>
984 * @type {Event.Custom}
986 EV_START = 'drag:start',
989 * @description Fires at the end of a drag operation.
990 * @param {Event.Facade} event An Event Facade object with the following specific property added:
992 * <dt>pageX</dt><dd>The current node position X.</dd>
993 * <dt>pageY</dt><dd>The current node position Y.</dd>
994 * <dt>startTime</dt><dd>The startTime of the event, from the start event.</dd>
995 * <dt>endTime</dt><dd>The endTime of the event. getTime on the current Date object.</dd>
998 * @type {Event.Custom}
1000 EV_END = 'drag:end',
1003 * @description Fires every mousemove during a drag operation.
1004 * @param {Event.Facade} event An Event Facade object with the following specific property added:
1006 * <dt>pageX</dt><dd>The current node position X.</dd>
1007 * <dt>pageY</dt><dd>The current node position Y.</dd>
1008 * <dt>scroll</dt><dd>Should a scroll action occur.</dd>
1009 * <dt>info</dt><dd>Object hash containing calculated XY arrays: start, xy, delta, offset</dd>
1012 * @type {Event.Custom}
1014 EV_DRAG = 'drag:drag',
1017 * @preventable _defAlignFn
1018 * @description Fires when this node is aligned.
1019 * @param {Event.Facade} event An Event Facade object with the following specific property added:
1021 * <dt>pageX</dt><dd>The current node position X.</dd>
1022 * <dt>pageY</dt><dd>The current node position Y.</dd>
1025 * @type {Event.Custom}
1027 EV_ALIGN = 'drag:align',
1030 * @description Fires when this node is over a Drop Target. (Fired from dd-drop)
1031 * @param {Event.Facade} event An Event Facade object with the following specific property added:
1033 * <dt>drop</dt><dd>The drop object at the time of the event.</dd>
1034 * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
1037 * @type {Event.Custom}
1041 * @description Fires when this node enters a Drop Target. (Fired from dd-drop)
1042 * @param {Event.Facade} event An Event Facade object with the following specific property added:
1044 * <dt>drop</dt><dd>The drop object at the time of the event.</dd>
1045 * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
1048 * @type {Event.Custom}
1052 * @description Fires when this node exits a Drop Target. (Fired from dd-drop)
1053 * @param {Event.Facade} event An Event Facade object with the following specific property added:
1055 * <dt>drop</dt><dd>The drop object at the time of the event.</dd>
1058 * @type {Event.Custom}
1061 * @event drag:drophit
1062 * @description Fires when this node is dropped on a valid Drop Target. (Fired from dd-ddm-drop)
1063 * @param {Event.Facade} event An Event Facade object with the following specific property added:
1065 * <dt>drop</dt><dd>The best guess on what was dropped on.</dd>
1066 * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
1067 * <dt>others</dt><dd>An array of all the other drop targets that was dropped on.</dd>
1070 * @type {Event.Custom}
1073 * @event drag:dropmiss
1074 * @description Fires when this node is dropped on an invalid Drop Target. (Fired from dd-ddm-drop)
1075 * @param {Event.Facade} event An Event Facade object with the following specific property added:
1077 * <dt>pageX</dt><dd>The current node position X.</dd>
1078 * <dt>pageY</dt><dd>The current node position Y.</dd>
1081 * @type {Event.Custom}
1084 Drag = function(o) {
1085 this._lazyAddAttrs = false;
1086 Drag.superclass.constructor.apply(this, arguments);
1088 var valid = DDM._regDrag(this);
1090 Y.error('Failed to register node, already in use: ' + o.node);
1097 * This property defaults to "mousedown", but when drag-gestures is loaded, it is changed to "gesturemovestart"
1099 * @property START_EVENT
1101 Drag.START_EVENT = 'mousedown';
1106 * @description Y.Node instance to use as the element to initiate a drag operation
1110 setter: function(node) {
1111 var n = Y.one(node);
1113 Y.error('DD.Drag: Invalid Node Given: ' + node);
1119 * @attribute dragNode
1120 * @description Y.Node instance to use as the draggable element, defaults to node
1124 setter: function(node) {
1125 var n = Y.one(node);
1127 Y.error('DD.Drag: Invalid dragNode Given: ' + node);
1133 * @attribute offsetNode
1134 * @description Offset the drag element by the difference in cursor position: default true
1141 * @attribute startCentered
1142 * @description Center the dragNode to the mouse position on drag:start: default false
1149 * @attribute clickPixelThresh
1150 * @description The number of pixels to move to start a drag operation, default is 3.
1154 value: DDM.get('clickPixelThresh')
1157 * @attribute clickTimeThresh
1158 * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
1162 value: DDM.get('clickTimeThresh')
1166 * @description Set to lock this drag element so that it can't be dragged: default false.
1171 setter: function(lock) {
1173 this.get(NODE).addClass(DDM.CSS_PREFIX + '-locked');
1175 this.get(NODE).removeClass(DDM.CSS_PREFIX + '-locked');
1182 * @description A payload holder to store arbitrary data about this drag object, can be used to store any value.
1190 * @description If this is false, the drag element will not move with the cursor: default true. Can be used to "resize" the element.
1197 * @attribute useShim
1198 * @description Use the protective shim on all drag operations: default true. Only works with dd-ddm, not dd-ddm-base.
1205 * @attribute activeHandle
1206 * @description This config option is set by Drag to inform you of which handle fired the drag event (in the case that there are several handles): default false.
1213 * @attribute primaryButtonOnly
1214 * @description By default a drag operation will only begin if the mousedown occurred with the primary mouse button. Setting this to false will allow for all mousedown events to trigger a drag.
1217 primaryButtonOnly: {
1221 * @attribute dragging
1222 * @description This attribute is not meant to be used by the implementor, it is meant to be used as an Event tracker so you can listen for it to change.
1233 * @description This attribute only works if the dd-drop module has been loaded. It will make this node a drop target as well as draggable.
1238 setter: function(config) {
1239 this._handleTarget(config);
1244 * @attribute dragMode
1245 * @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of this Drag instance.
1250 setter: function(mode) {
1251 return DDM._setDragMode(mode);
1256 * @description Array of groups to add this drag into.
1261 getter: function() {
1262 if (!this._groups) {
1266 Y.each(this._groups, function(v, k) {
1267 ret[ret.length] = k;
1271 setter: function(g) {
1273 Y.each(g, function(v, k) {
1274 this._groups[v] = true;
1280 * @attribute handles
1281 * @description Array of valid handles to add. Adding something here will set all handles, even if previously added with addHandle
1286 setter: function(g) {
1289 Y.each(g, function(v, k) {
1291 if (v instanceof Y.Node || v instanceof Y.NodeList) {
1294 this._handles[key] = v;
1297 this._handles = null;
1304 * @attribute bubbles
1305 * @description Controls the default bubble parent for this Drag instance. Default: Y.DD.DDM. Set to false to disable bubbling. Use bubbleTargets in config
1309 setter: function(t) {
1315 * @attribute haltDown
1316 * @description Should the mousedown event be halted. Default: true
1324 Y.extend(Drag, Y.Base, {
1327 * @property _bubbleTargets
1328 * @description The default bubbleTarget for this object. Default: Y.DD.DDM
1330 _bubbleTargets: Y.DD.DDM,
1332 * @method addToGroup
1333 * @description Add this Drag instance to a group, this should be used for on-the-fly group additions.
1334 * @param {String} g The group to add this Drag Instance to.
1338 addToGroup: function(g) {
1339 this._groups[g] = true;
1340 DDM._activateTargets();
1344 * @method removeFromGroup
1345 * @description Remove this Drag instance from a group, this should be used for on-the-fly group removals.
1346 * @param {String} g The group to remove this Drag Instance from.
1350 removeFromGroup: function(g) {
1351 delete this._groups[g];
1352 DDM._activateTargets();
1357 * @description This will be a reference to the Drop instance associated with this drag if the target: true config attribute is set..
1363 * @method _handleTarget
1364 * @description Attribute handler for the target config attribute.
1365 * @param {Boolean/Object}
1366 * @return {Boolean/Object}
1368 _handleTarget: function(config) {
1370 if (config === false) {
1372 DDM._unregTarget(this.target);
1377 if (!Y.Lang.isObject(config)) {
1380 config.bubbleTargets = ('bubbleTargets' in config) ? config.bubbleTargets : Y.Object.values(this._yuievt.targets);
1381 config.node = this.get(NODE);
1382 config.groups = config.groups || this.get('groups');
1383 this.target = new Y.DD.Drop(config);
1392 * @description Storage Array for the groups this drag belongs to.
1398 * @method _createEvents
1399 * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
1401 _createEvents: function() {
1403 this.publish(EV_MOUSE_DOWN, {
1404 defaultFn: this._defMouseDownFn,
1411 this.publish(EV_ALIGN, {
1412 defaultFn: this._defAlignFn,
1419 this.publish(EV_DRAG, {
1420 defaultFn: this._defDragFn,
1427 this.publish(EV_END, {
1428 defaultFn: this._defEndFn,
1429 preventedFn: this._prevEndFn,
1437 EV_AFTER_MOUSE_DOWN,
1450 Y.each(ev, function(v, k) {
1464 * @description A private reference to the mousedown DOM event
1465 * @type {Event.Facade}
1470 * @property _startTime
1471 * @description The getTime of the mousedown event. Not used, just here in case someone wants/needs to use it.
1477 * @property _endTime
1478 * @description The getTime of the mouseup event. Not used, just here in case someone wants/needs to use it.
1484 * @property _handles
1485 * @description A private hash of the valid drag handles
1491 * @property _invalids
1492 * @description A private hash of the invalid selector strings
1498 * @property _invalidsDefault
1499 * @description A private hash of the default invalid selector strings: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true}
1502 _invalidsDefault: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true },
1505 * @property _dragThreshMet
1506 * @description Private flag to see if the drag threshhold was met
1509 _dragThreshMet: null,
1512 * @property _fromTimeout
1513 * @description Flag to determine if the drag operation came from a timeout
1519 * @property _clickTimeout
1520 * @description Holder for the setTimeout call
1523 _clickTimeout: null,
1526 * @description The offset of the mouse position to the element's position
1532 * @description The initial mouse position
1538 * @description The initial element position
1544 * @description The position of the element as it's moving (for offset calculations)
1550 * @description The xy that the node will be set to. Changing this will alter the position as it's dragged.
1556 * @description The real xy position of the node.
1562 * @description The XY coords of the mousemove
1568 * @description A region object associated with this drag, used for checking regions while dragging.
1574 * @method _handleMouseUp
1575 * @description Handler for the mouseup DOM event
1576 * @param {Event.Facade}
1578 _handleMouseUp: function(ev) {
1579 this.fire('drag:mouseup');
1580 this._fixIEMouseUp();
1581 if (DDM.activeDrag) {
1587 * @method _fixDragStart
1588 * @description The function we use as the ondragstart handler when we start a drag in Internet Explorer. This keeps IE from blowing up on images as drag handles.
1590 _fixDragStart: function(e) {
1595 * @method _ieSelectFix
1596 * @description The function we use as the onselectstart handler when we start a drag in Internet Explorer
1598 _ieSelectFix: function() {
1603 * @property _ieSelectBack
1604 * @description We will hold a copy of the current "onselectstart" method on this property, and reset it after we are done using it.
1606 _ieSelectBack: null,
1609 * @method _fixIEMouseDown
1610 * @description This method copies the onselectstart listner on the document to the _ieSelectFix property
1612 _fixIEMouseDown: function(e) {
1614 this._ieSelectBack = Y.config.doc.body.onselectstart;
1615 Y.config.doc.body.onselectstart = this._ieSelectFix;
1620 * @method _fixIEMouseUp
1621 * @description This method copies the _ieSelectFix property back to the onselectstart listner on the document.
1623 _fixIEMouseUp: function() {
1625 Y.config.doc.body.onselectstart = this._ieSelectBack;
1630 * @method _handleMouseDownEvent
1631 * @description Handler for the mousedown DOM event
1632 * @param {Event.Facade}
1634 _handleMouseDownEvent: function(ev) {
1635 this.fire(EV_MOUSE_DOWN, { ev: ev });
1639 * @method _defMouseDownFn
1640 * @description Handler for the mousedown DOM event
1641 * @param {Event.Facade}
1643 _defMouseDownFn: function(e) {
1646 this._dragThreshMet = false;
1649 if (this.get('primaryButtonOnly') && ev.button > 1) {
1652 if (this.validClick(ev)) {
1653 this._fixIEMouseDown(ev);
1654 if (this.get('haltDown')) {
1657 ev.preventDefault();
1660 this._setStartPosition([ev.pageX, ev.pageY]);
1662 DDM.activeDrag = this;
1664 this._clickTimeout = Y.later(this.get('clickTimeThresh'), this, this._timeoutCheck);
1666 this.fire(EV_AFTER_MOUSE_DOWN, { ev: ev });
1669 * @method validClick
1670 * @description Method first checks to see if we have handles, if so it validates the click against the handle. Then if it finds a valid handle, it checks it against the invalid handles list. Returns true if a good handle was used, false otherwise.
1671 * @param {Event.Facade}
1674 validClick: function(ev) {
1675 var r = false, n = false,
1681 if (this._handles) {
1682 Y.each(this._handles, function(i, n) {
1683 if (i instanceof Y.Node || i instanceof Y.NodeList) {
1686 if (nlist instanceof Y.Node) {
1687 nlist = new Y.NodeList(i._node);
1689 nlist.each(function(nl) {
1690 if (nl.contains(tar)) {
1695 } else if (Y.Lang.isString(n)) {
1696 //Am I this or am I inside this
1697 if (tar.test(n + ', ' + n + ' *') && !hTest) {
1705 if (n.contains(tar) || n.compareTo(tar)) {
1710 if (this._invalids) {
1711 Y.each(this._invalids, function(i, n) {
1712 if (Y.Lang.isString(n)) {
1713 //Am I this or am I inside this
1714 if (tar.test(n + ', ' + n + ' *')) {
1723 els = ev.currentTarget.all(hTest);
1725 els.each(function(n, i) {
1726 if ((n.contains(tar) || n.compareTo(tar)) && !set) {
1728 this.set('activeHandle', n);
1732 this.set('activeHandle', this.get(NODE));
1739 * @method _setStartPosition
1740 * @description Sets the current position of the Element and calculates the offset
1741 * @param {Array} xy The XY coords to set the position to.
1743 _setStartPosition: function(xy) {
1746 this.nodeXY = this.lastXY = this.realXY = this.get(NODE).getXY();
1748 if (this.get('offsetNode')) {
1749 this.deltaXY = [(this.startXY[0] - this.nodeXY[0]), (this.startXY[1] - this.nodeXY[1])];
1751 this.deltaXY = [0, 0];
1756 * @method _timeoutCheck
1757 * @description The method passed to setTimeout to determine if the clickTimeThreshold was met.
1759 _timeoutCheck: function() {
1760 if (!this.get('lock') && !this._dragThreshMet && this._ev_md) {
1761 this._fromTimeout = this._dragThreshMet = true;
1763 this._alignNode([this._ev_md.pageX, this._ev_md.pageY], true);
1767 * @method removeHandle
1768 * @description Remove a Selector added by addHandle
1769 * @param {String} str The selector for the handle to be removed.
1773 removeHandle: function(str) {
1775 if (str instanceof Y.Node || str instanceof Y.NodeList) {
1778 if (this._handles[key]) {
1779 delete this._handles[key];
1780 this.fire(EV_REMOVE_HANDLE, { handle: str });
1786 * @description Add a handle to a drag element. Drag only initiates when a mousedown happens on this element.
1787 * @param {String} str The selector to test for a valid handle. Must be a child of the element.
1791 addHandle: function(str) {
1792 if (!this._handles) {
1796 if (str instanceof Y.Node || str instanceof Y.NodeList) {
1799 this._handles[key] = str;
1800 this.fire(EV_ADD_HANDLE, { handle: str });
1804 * @method removeInvalid
1805 * @description Remove an invalid handle added by addInvalid
1806 * @param {String} str The invalid handle to remove from the internal list.
1810 removeInvalid: function(str) {
1811 if (this._invalids[str]) {
1812 this._invalids[str] = null;
1813 delete this._invalids[str];
1814 this.fire(EV_REMOVE_INVALID, { handle: str });
1819 * @method addInvalid
1820 * @description Add a selector string to test the handle against. If the test passes the drag operation will not continue.
1821 * @param {String} str The selector to test against to determine if this is an invalid drag handle.
1825 addInvalid: function(str) {
1826 if (Y.Lang.isString(str)) {
1827 this._invalids[str] = true;
1828 this.fire(EV_ADD_INVALID, { handle: str });
1834 * @method initializer
1835 * @description Internal init handler
1837 initializer: function(cfg) {
1838 this.get(NODE).dd = this;
1840 if (!this.get(NODE).get('id')) {
1841 var id = Y.stamp(this.get(NODE));
1842 this.get(NODE).set('id', id);
1847 this._invalids = Y.clone(this._invalidsDefault, true);
1849 this._createEvents();
1851 if (!this.get(DRAG_NODE)) {
1852 this.set(DRAG_NODE, this.get(NODE));
1856 //Don't prep the DD instance until all plugins are loaded.
1857 this.on('initializedChange', Y.bind(this._prep, this));
1859 //Shouldn't have to do this..
1860 this.set('groups', this.get('groups'));
1865 * @description Attach event listners and add classname
1868 this._dragThreshMet = false;
1869 var node = this.get(NODE);
1870 node.addClass(DDM.CSS_PREFIX + '-draggable');
1871 node.on(Drag.START_EVENT, Y.bind(this._handleMouseDownEvent, this));
1872 node.on('mouseup', Y.bind(this._handleMouseUp, this));
1873 node.on('dragstart', Y.bind(this._fixDragStart, this));
1878 * @description Detach event listeners and remove classname
1880 _unprep: function() {
1881 var node = this.get(NODE);
1882 node.removeClass(DDM.CSS_PREFIX + '-draggable');
1887 * @description Starts the drag operation
1892 if (!this.get('lock') && !this.get(DRAGGING)) {
1893 var node = this.get(NODE), ow, oh, xy;
1894 this._startTime = (new Date()).getTime();
1897 node.addClass(DDM.CSS_PREFIX + '-dragging');
1898 this.fire(EV_START, {
1899 pageX: this.nodeXY[0],
1900 pageY: this.nodeXY[1],
1901 startTime: this._startTime
1903 node = this.get(DRAG_NODE);
1906 ow = node.get(OFFSET_WIDTH);
1907 oh = node.get(OFFSET_HEIGHT);
1909 if (this.get('startCentered')) {
1910 this._setStartPosition([xy[0] + (ow / 2), xy[1] + (oh / 2)]);
1923 this.set(DRAGGING, true);
1929 * @description Ends the drag operation
1934 this._endTime = (new Date()).getTime();
1935 if (this._clickTimeout) {
1936 this._clickTimeout.cancel();
1938 this._dragThreshMet = this._fromTimeout = false;
1940 if (!this.get('lock') && this.get(DRAGGING)) {
1942 pageX: this.lastXY[0],
1943 pageY: this.lastXY[1],
1944 startTime: this._startTime,
1945 endTime: this._endTime
1948 this.get(NODE).removeClass(DDM.CSS_PREFIX + '-dragging');
1949 this.set(DRAGGING, false);
1950 this.deltaXY = [0, 0];
1957 * @description Handler for fixing the selection in IE
1959 _defEndFn: function(e) {
1960 this._fixIEMouseUp();
1965 * @method _prevEndFn
1966 * @description Handler for preventing the drag:end event. It will reset the node back to it's start position
1968 _prevEndFn: function(e) {
1969 this._fixIEMouseUp();
1971 this.get(DRAG_NODE).setXY(this.nodeXY);
1978 * @description Calculates the offsets and set's the XY that the element will move to.
1979 * @param {Array} xy The xy coords to align with.
1981 _align: function(xy) {
1982 this.fire(EV_ALIGN, {pageX: xy[0], pageY: xy[1] });
1986 * @method _defAlignFn
1987 * @description Calculates the offsets and set's the XY that the element will move to.
1988 * @param {Event.Facade} e The drag:align event.
1990 _defAlignFn: function(e) {
1991 this.actXY = [e.pageX - this.deltaXY[0], e.pageY - this.deltaXY[1]];
1995 * @method _alignNode
1996 * @description This method performs the alignment before the element move.
1997 * @param {Array} eXY The XY to move the element to, usually comes from the mousemove DOM event.
1999 _alignNode: function(eXY) {
2006 * @description This method performs the actual element move.
2008 _moveNode: function(scroll) {
2009 //if (!this.get(DRAGGING)) {
2012 var diffXY = [], diffXY2 = [], startXY = this.nodeXY, xy = this.actXY;
2014 diffXY[0] = (xy[0] - this.lastXY[0]);
2015 diffXY[1] = (xy[1] - this.lastXY[1]);
2017 diffXY2[0] = (xy[0] - this.nodeXY[0]);
2018 diffXY2[1] = (xy[1] - this.nodeXY[1]);
2026 right: xy[0] + this.get(DRAG_NODE).get(OFFSET_WIDTH),
2027 bottom: xy[1] + this.get(DRAG_NODE).get(OFFSET_HEIGHT),
2031 this.fire(EV_DRAG, {
2047 * @method _defDragFn
2048 * @description Default function for drag:drag. Fired from _moveNode.
2049 * @param {Event.Facade} ev The drag:drag event
2051 _defDragFn: function(e) {
2052 if (this.get('move')) {
2054 e.scroll.node.set('scrollTop', e.scroll.top);
2055 e.scroll.node.set('scrollLeft', e.scroll.left);
2057 this.get(DRAG_NODE).setXY([e.pageX, e.pageY]);
2058 this.realXY = [e.pageX, e.pageY];
2064 * @description Fired from DragDropMgr (DDM) on mousemove.
2065 * @param {Event.Facade} ev The mousemove DOM event
2067 _move: function(ev) {
2068 if (this.get('lock')) {
2071 this.mouseXY = [ev.pageX, ev.pageY];
2072 if (!this._dragThreshMet) {
2073 var diffX = Math.abs(this.startXY[0] - ev.pageX),
2074 diffY = Math.abs(this.startXY[1] - ev.pageY);
2075 if (diffX > this.get('clickPixelThresh') || diffY > this.get('clickPixelThresh')) {
2076 this._dragThreshMet = true;
2078 this._alignNode([ev.pageX, ev.pageY]);
2081 if (this._clickTimeout) {
2082 this._clickTimeout.cancel();
2084 this._alignNode([ev.pageX, ev.pageY]);
2090 * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
2094 stopDrag: function() {
2095 if (this.get(DRAGGING)) {
2102 * @method destructor
2103 * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
2105 destructor: function() {
2109 this.target.destroy();
2111 DDM._unregDrag(this);
2121 }, '3.3.0' ,{requires:['dd-ddm-base'], skinnable:false});
2122 YUI.add('dd-proxy', function(Y) {
2126 * Plugin for dd-drag for creating a proxy drag node, instead of dragging the original node.
2128 * @submodule dd-proxy
2131 * Plugin for dd-drag for creating a proxy drag node, instead of dragging the original node.
2139 DRAG_NODE = 'dragNode',
2142 P = function(config) {
2143 P.superclass.constructor.apply(this, arguments);
2153 * @description The Proxy instance will be placed on the Drag instance under the proxy namespace.
2162 * @attribute moveOnEnd
2163 * @description Move the original node at the end of the drag. Default: true
2170 * @attribute hideOnEnd
2171 * @description Hide the drag node at the end of the drag. Default: true
2178 * @attribute resizeFrame
2179 * @description Make the Proxy node assume the size of the original node. Default: true
2186 * @attribute positionProxy
2187 * @description Make the Proxy node appear in the same place as the original node. Default: true
2194 * @attribute borderStyle
2195 * @description The default border style for the border of the proxy. Default: 1px solid #808080
2199 value: '1px solid #808080'
2202 * @attribute cloneNode
2203 * @description Should the node be cloned into the proxy for you. Default: false
2215 * @description Holds the event handles for setting the proxy
2221 * @description Handler for the proxy config attribute
2226 Y.on('domready', Y.bind(this._init, this));
2232 var h, h1, host = this.get(HOST), dnode = host.get(DRAG_NODE);
2233 if (dnode.compareTo(host.get(NODE))) {
2235 host.set(DRAG_NODE, DDM._proxy);
2238 Y.each(this._hands, function(v) {
2241 h = DDM.on('ddm:start', Y.bind(function() {
2242 if (DDM.activeDrag === host) {
2243 DDM._setFrame(host);
2246 h1 = DDM.on('ddm:end', Y.bind(function() {
2247 if (host.get('dragging')) {
2248 if (this.get('moveOnEnd')) {
2249 host.get(NODE).setXY(host.lastXY);
2251 if (this.get('hideOnEnd')) {
2252 host.get(DRAG_NODE).setStyle('display', 'none');
2254 if (this.get('cloneNode')) {
2255 host.get(DRAG_NODE).remove();
2256 host.set(DRAG_NODE, DDM._proxy);
2260 this._hands = [h, h1];
2262 initializer: function() {
2265 destructor: function() {
2266 var host = this.get(HOST);
2267 Y.each(this._hands, function(v) {
2270 host.set(DRAG_NODE, host.get(NODE));
2273 var host = this.get(HOST),
2275 c = n.cloneNode(true);
2278 c.setAttribute('id', Y.guid());
2279 c.setStyle('position', 'absolute');
2280 n.get('parentNode').appendChild(c);
2281 host.set(DRAG_NODE, c);
2286 Y.namespace('Plugin');
2287 Y.extend(P, Y.Base, proto);
2288 Y.Plugin.DDProxy = P;
2290 //Add a couple of methods to the DDM
2296 * @method _createFrame
2297 * @description Create the proxy element if it doesn't already exist and set the DD.DDM._proxy value
2299 _createFrame: function() {
2303 var p = Y.Node.create('<div></div>'),
2307 position: 'absolute',
2315 p.set('id', Y.guid());
2316 p.addClass(DDM.CSS_PREFIX + '-proxy');
2325 * @description If resizeProxy is set to true (default) it will resize the proxy element to match the size of the Drag Element.
2326 * If positionProxy is set to true (default) it will position the proxy element in the same location as the Drag Element.
2328 _setFrame: function(drag) {
2329 var n = drag.get(NODE), d = drag.get(DRAG_NODE), ah, cur = 'auto';
2331 ah = DDM.activeDrag.get('activeHandle');
2333 cur = ah.getStyle('cursor');
2335 if (cur == 'auto') {
2336 cur = DDM.get('dragCursor');
2340 visibility: 'hidden',
2343 border: drag.proxy.get('borderStyle')
2346 if (drag.proxy.get('cloneNode')) {
2347 d = drag.proxy.clone();
2350 if (drag.proxy.get('resizeFrame')) {
2352 height: n.get('offsetHeight') + 'px',
2353 width: n.get('offsetWidth') + 'px'
2357 if (drag.proxy.get('positionProxy')) {
2358 d.setXY(drag.nodeXY);
2360 d.setStyle('visibility', 'visible');
2364 //Create the frame when DOM is ready
2365 //Y.on('domready', Y.bind(DDM._createFrame, DDM));
2369 }, '3.3.0' ,{requires:['dd-ddm', 'dd-drag'], skinnable:false});
2370 YUI.add('dd-constrain', function(Y) {
2374 * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
2376 * @submodule dd-constrain
2379 * Plugin for the dd-drag module to add the constraining methods to it. It supports constraining to a node or viewport. It supports tick based moves and XY axis constraints.
2380 * @class DDConstrained
2386 var DRAG_NODE = 'dragNode',
2387 OFFSET_HEIGHT = 'offsetHeight',
2388 OFFSET_WIDTH = 'offsetWidth',
2390 TICK_X_ARRAY = 'tickXArray',
2391 TICK_Y_ARRAY = 'tickYArray',
2401 * @event drag:tickAlignX
2402 * @description Fires when this node is aligned with the tickX value.
2403 * @param {Event.Facade} event An Event Facade object
2404 * @type {Event.Custom}
2406 EV_TICK_ALIGN_X = 'drag:tickAlignX',
2409 * @event drag:tickAlignY
2410 * @description Fires when this node is aligned with the tickY value.
2411 * @param {Event.Facade} event An Event Facade object
2412 * @type {Event.Custom}
2414 EV_TICK_ALIGN_Y = 'drag:tickAlignY',
2416 C = function(config) {
2417 this._lazyAddAttrs = false;
2418 C.superclass.constructor.apply(this, arguments);
2421 C.NAME = 'ddConstrained';
2428 * @description The Constrained instance will be placed on the Drag instance under the con namespace.
2438 * @description Stick the drag movement to the X-Axis. Default: false
2446 * @description Stick the drag movement to the Y-Axis
2454 * @description The X tick offset the drag node should snap to on each drag move. False for no ticks. Default: false
2455 * @type Number/false
2462 * @description The Y tick offset the drag node should snap to on each drag move. False for no ticks. Default: false
2463 * @type Number/false
2469 * @attribute tickXArray
2470 * @description An array of page coordinates to use as X ticks for drag movement.
2477 * @attribute tickYArray
2478 * @description An array of page coordinates to use as Y ticks for drag movement.
2486 * @description CSS style string for the gutter of a region (supports negative values): '5 0' (sets top and bottom to 5px, left and right to 0px), '1 2 3 4' (top 1px, right 2px, bottom 3px, left 4px)
2491 setter: function(gutter) {
2492 return Y.DD.DDM.cssSizestoObject(gutter);
2496 * @attribute constrain
2497 * @description Will attempt to constrain the drag node to the boundaries. Arguments:<br>
2498 * 'view': Contrain to Viewport<br>
2499 * '#selector_string': Constrain to this node<br>
2500 * '{Region Object}': An Object Literal containing a valid region (top, right, bottom, left) of page positions
2501 * @type {String/Object/Node}
2505 setter: function(con) {
2506 var node = Y.one(con);
2515 * @attribute constrain2region
2516 * @description An Object Literal containing a valid region (top, right, bottom, left) of page positions to constrain the drag node to.
2520 setter: function(r) {
2521 return this.set('constrain', r);
2526 * @attribute constrain2node
2527 * @description Will attempt to constrain the drag node to the boundaries of this node.
2531 setter: function(n) {
2532 return this.set('constrain', Y.one(n));
2537 * @attribute constrain2view
2538 * @description Will attempt to constrain the drag node to the boundaries of the viewport region.
2542 setter: function(n) {
2543 return this.set('constrain', VIEW);
2547 * @attribute cacheRegion
2548 * @description Should the region be cached for performace. Default: true
2557 _lastTickXFired: null,
2558 _lastTickYFired: null,
2560 initializer: function() {
2561 this._createEvents();
2563 this.get(HOST).on('drag:end', Y.bind(this._handleEnd, this));
2564 this.get(HOST).on('drag:start', Y.bind(this._handleStart, this));
2565 this.get(HOST).after('drag:align', Y.bind(this.align, this));
2566 this.get(HOST).after('drag:drag', Y.bind(this.drag, this));
2570 * @method _createEvents
2571 * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
2573 _createEvents: function() {
2574 var instance = this;
2581 Y.each(ev, function(v, k) {
2593 * @method _handleEnd
2594 * @description Fires on drag:end
2596 _handleEnd: function() {
2597 this._lastTickYFired = null;
2598 this._lastTickXFired = null;
2602 * @method _handleStart
2603 * @description Fires on drag:start and clears the _regionCache
2605 _handleStart: function() {
2610 * @property _regionCache
2611 * @description Store a cache of the region that we are constraining to
2617 * @method _cacheRegion
2618 * @description Get's the region and caches it, called from window.resize and when the cache is null
2620 _cacheRegion: function() {
2621 this._regionCache = this.get('constrain').get('region');
2624 * @method resetCache
2625 * @description Reset the internal region cache.
2627 resetCache: function() {
2628 this._regionCache = null;
2632 * @method _getConstraint
2633 * @description Standardizes the 'constraint' attribute
2635 _getConstraint: function() {
2636 var con = this.get('constrain'),
2637 g = this.get('gutter'),
2641 if (con instanceof Y.Node) {
2642 if (!this._regionCache) {
2643 Y.on('resize', Y.bind(this._cacheRegion, this), Y.config.win);
2644 this._cacheRegion();
2646 region = Y.clone(this._regionCache);
2647 if (!this.get('cacheRegion')) {
2650 } else if (Y.Lang.isObject(con)) {
2651 region = Y.clone(con);
2654 if (!con || !region) {
2658 region = this.get(HOST).get(DRAG_NODE).get('viewportRegion');
2661 Y.each(g, function(i, n) {
2662 if ((n == RIGHT) || (n == BOTTOM)) {
2673 * @description Get the active region: viewport, node, custom region
2674 * @param {Boolean} inc Include the node's height and width
2677 getRegion: function(inc) {
2678 var r = {}, oh = null, ow = null,
2679 host = this.get(HOST);
2681 r = this._getConstraint();
2684 oh = host.get(DRAG_NODE).get(OFFSET_HEIGHT);
2685 ow = host.get(DRAG_NODE).get(OFFSET_WIDTH);
2686 r[RIGHT] = r[RIGHT] - ow;
2687 r[BOTTOM] = r[BOTTOM] - oh;
2693 * @method _checkRegion
2694 * @description Check if xy is inside a given region, if not change to it be inside.
2695 * @param {Array} _xy The XY to check if it's in the current region, if it isn't inside the region, it will reset the xy array to be inside the region.
2696 * @return {Array} The new XY that is inside the region
2698 _checkRegion: function(_xy) {
2700 r = this.getRegion(),
2701 host = this.get(HOST),
2702 oh = host.get(DRAG_NODE).get(OFFSET_HEIGHT),
2703 ow = host.get(DRAG_NODE).get(OFFSET_WIDTH);
2705 if (oxy[1] > (r[BOTTOM] - oh)) {
2706 _xy[1] = (r[BOTTOM] - oh);
2708 if (r[TOP] > oxy[1]) {
2712 if (oxy[0] > (r[RIGHT] - ow)) {
2713 _xy[0] = (r[RIGHT] - ow);
2715 if (r[LEFT] > oxy[0]) {
2723 * @description Checks if the XY passed or the dragNode is inside the active region.
2724 * @param {Array} xy Optional XY to check, if not supplied this.get('dragNode').getXY() is used.
2725 * @return {Boolean} True if the XY is inside the region, false otherwise.
2727 inRegion: function(xy) {
2728 xy = xy || this.get(HOST).get(DRAG_NODE).getXY();
2730 var _xy = this._checkRegion([xy[0], xy[1]]),
2732 if ((xy[0] === _xy[0]) && (xy[1] === _xy[1])) {
2739 * @description Modifies the Drag.actXY method from the after drag:align event. This is where the constraining happens.
2742 var host = this.get(HOST),
2743 _xy = [host.actXY[0], host.actXY[1]],
2744 r = this.getRegion(true);
2746 if (this.get('stickX')) {
2747 _xy[1] = (host.startXY[1] - host.deltaXY[1]);
2749 if (this.get('stickY')) {
2750 _xy[0] = (host.startXY[0] - host.deltaXY[0]);
2754 _xy = this._checkRegion(_xy);
2757 _xy = this._checkTicks(_xy, r);
2763 * @description Fires after drag:drag. Handle the tickX and tickX align events.
2765 drag: function(event) {
2766 var host = this.get(HOST),
2767 xt = this.get('tickX'),
2768 yt = this.get('tickY'),
2769 _xy = [host.actXY[0], host.actXY[1]];
2771 if ((Y.Lang.isNumber(xt) || this.get(TICK_X_ARRAY)) && (this._lastTickXFired !== _xy[0])) {
2773 this._lastTickXFired = _xy[0];
2776 if ((Y.Lang.isNumber(yt) || this.get(TICK_Y_ARRAY)) && (this._lastTickYFired !== _xy[1])) {
2778 this._lastTickYFired = _xy[1];
2783 * @method _checkTicks
2784 * @description This method delegates the proper helper method for tick calculations
2785 * @param {Array} xy The XY coords for the Drag
2786 * @param {Object} r The optional region that we are bound to.
2787 * @return {Array} The calced XY coords
2789 _checkTicks: function(xy, r) {
2790 var host = this.get(HOST),
2791 lx = (host.startXY[0] - host.deltaXY[0]),
2792 ly = (host.startXY[1] - host.deltaXY[1]),
2793 xt = this.get('tickX'),
2794 yt = this.get('tickY');
2795 if (xt && !this.get(TICK_X_ARRAY)) {
2796 xy[0] = DDM._calcTicks(xy[0], lx, xt, r[LEFT], r[RIGHT]);
2798 if (yt && !this.get(TICK_Y_ARRAY)) {
2799 xy[1] = DDM._calcTicks(xy[1], ly, yt, r[TOP], r[BOTTOM]);
2801 if (this.get(TICK_X_ARRAY)) {
2802 xy[0] = DDM._calcTickArray(xy[0], this.get(TICK_X_ARRAY), r[LEFT], r[RIGHT]);
2804 if (this.get(TICK_Y_ARRAY)) {
2805 xy[1] = DDM._calcTickArray(xy[1], this.get(TICK_Y_ARRAY), r[TOP], r[BOTTOM]);
2812 * @method _tickAlignX
2813 * @description Fires when the actXY[0] reach a new value respecting the tickX gap.
2815 _tickAlignX: function() {
2816 this.fire(EV_TICK_ALIGN_X);
2820 * @method _tickAlignY
2821 * @description Fires when the actXY[1] reach a new value respecting the tickY gap.
2823 _tickAlignY: function() {
2824 this.fire(EV_TICK_ALIGN_Y);
2828 Y.namespace('Plugin');
2829 Y.extend(C, Y.Base, proto);
2830 Y.Plugin.DDConstrained = C;
2837 * @method _calcTicks
2838 * @description Helper method to calculate the tick offsets for a given position
2839 * @param {Number} pos The current X or Y position
2840 * @param {Number} start The start X or Y position
2841 * @param {Number} tick The X or Y tick increment
2842 * @param {Number} off1 The min offset that we can't pass (region)
2843 * @param {Number} off2 The max offset that we can't pass (region)
2844 * @return {Number} The new position based on the tick calculation
2846 _calcTicks: function(pos, start, tick, off1, off2) {
2847 var ix = ((pos - start) / tick),
2848 min = Math.floor(ix),
2849 max = Math.ceil(ix);
2850 if ((min !== 0) || (max !== 0)) {
2851 if ((ix >= min) && (ix <= max)) {
2852 pos = (start + (tick * min));
2855 pos = (start + (tick * (min + 1)));
2858 pos = (start + (tick * (min - 1)));
2869 * @method _calcTickArray
2870 * @description This method is used with the tickXArray and tickYArray config options
2871 * @param {Number} pos The current X or Y position
2872 * @param {Number} ticks The array containing our custom tick positions.
2873 * @param {Number} off1 The min offset that we can't pass (region)
2874 * @param {Number} off2 The max offset that we can't pass (region)
2875 * @return The tick position
2877 _calcTickArray: function(pos, ticks, off1, off2) {
2878 var i = 0, len = ticks.length, next = 0,
2881 if (!ticks || (ticks.length === 0)) {
2883 } else if (ticks[0] >= pos) {
2886 for (i = 0; i < len; i++) {
2888 if (ticks[next] && ticks[next] >= pos) {
2889 diff1 = pos - ticks[i];
2890 diff2 = ticks[next] - pos;
2891 ret = (diff2 > diff1) ? ticks[i] : ticks[next];
2897 ret = ticks[len - 1];
2905 return ticks[ticks.length - 1];
2912 }, '3.3.0' ,{requires:['dd-drag'], skinnable:false});
2913 YUI.add('dd-scroll', function(Y) {
2917 * Base scroller class used to create the Plugin.DDNodeScroll and Plugin.DDWinScroll.
2918 * This class should not be called on it's own, it's designed to be a plugin.
2920 * @submodule dd-scroll
2923 * Base scroller class used to create the Plugin.DDNodeScroll and Plugin.DDWinScroll.
2924 * This class should not be called on it's own, it's designed to be a plugin.
2931 var S = function() {
2932 S.superclass.constructor.apply(this, arguments);
2938 PARENT_SCROLL = 'parentScroll',
2939 WINDOW_SCROLL = 'windowScroll',
2940 SCROLL_TOP = 'scrollTop',
2941 SCROLL_LEFT = 'scrollLeft',
2942 OFFSET_WIDTH = 'offsetWidth',
2943 OFFSET_HEIGHT = 'offsetHeight';
2948 * @attribute parentScroll
2949 * @description Internal config option to hold the node that we are scrolling. Should not be set by the developer.
2954 setter: function(node) {
2963 * @description The number of pixels from the edge of the screen to turn on scrolling. Default: 30
2968 validator: Y.Lang.isNumber
2971 * @attribute scrollDelay
2972 * @description The number of milliseconds delay to pass to the auto scroller. Default: 235
2977 validator: Y.Lang.isNumber
2981 * @description The host we are plugged into.
2988 * @attribute windowScroll
2989 * @description Turn on window scroll support, default: false
2994 validator: Y.Lang.isBoolean
2997 * @attribute vertical
2998 * @description Allow vertical scrolling, default: true.
3003 validator: Y.Lang.isBoolean
3006 * @attribute horizontal
3007 * @description Allow horizontal scrolling, default: true.
3012 validator: Y.Lang.isBoolean
3016 Y.extend(S, Y.Base, {
3019 * @property _scrolling
3020 * @description Tells if we are actively scrolling or not.
3026 * @property _vpRegionCache
3027 * @description Cache of the Viewport dims.
3030 _vpRegionCache: null,
3033 * @property _dimCache
3034 * @description Cache of the dragNode dims.
3040 * @property _scrollTimer
3041 * @description Holder for the Timer object returned from Y.later.
3047 * @method _getVPRegion
3048 * @description Sets the _vpRegionCache property with an Object containing the dims from the viewport.
3050 _getVPRegion: function() {
3052 n = this.get(PARENT_SCROLL),
3053 b = this.get(BUFFER),
3054 ws = this.get(WINDOW_SCROLL),
3055 xy = ((ws) ? [] : n.getXY()),
3056 w = ((ws) ? 'winWidth' : OFFSET_WIDTH),
3057 h = ((ws) ? 'winHeight' : OFFSET_HEIGHT),
3058 t = ((ws) ? n.get(SCROLL_TOP) : xy[1]),
3059 l = ((ws) ? n.get(SCROLL_LEFT) : xy[0]);
3063 right: (n.get(w) + l) - b,
3064 bottom: (n.get(h) + t) - b,
3067 this._vpRegionCache = r;
3070 initializer: function() {
3071 var h = this.get(HOST);
3072 h.after('drag:start', Y.bind(this.start, this));
3073 h.after('drag:end', Y.bind(this.end, this));
3074 h.on('drag:align', Y.bind(this.align, this));
3076 //TODO - This doesn't work yet??
3077 Y.one('win').on('scroll', Y.bind(function() {
3078 this._vpRegionCache = null;
3083 * @method _checkWinScroll
3084 * @description Check to see if we need to fire the scroll timer. If scroll timer is running this will scroll the window.
3085 * @param {Boolean} move Should we move the window. From Y.later
3087 _checkWinScroll: function(move) {
3088 var r = this._getVPRegion(),
3089 ho = this.get(HOST),
3090 ws = this.get(WINDOW_SCROLL),
3093 b = this.get(BUFFER),
3094 win = this.get(PARENT_SCROLL),
3095 sTop = win.get(SCROLL_TOP),
3096 sLeft = win.get(SCROLL_LEFT),
3097 w = this._dimCache.w,
3098 h = this._dimCache.h,
3108 if (this.get('horizontal')) {
3109 if (left <= r.left) {
3111 nl = xy[0] - ((ws) ? b : 0);
3114 if (right >= r.right) {
3116 nl = xy[0] + ((ws) ? b : 0);
3120 if (this.get('vertical')) {
3121 if (bottom >= r.bottom) {
3123 nt = xy[1] + ((ws) ? b : 0);
3129 nt = xy[1] - ((ws) ? b : 0);
3151 ho.actXY = [nl, nt];
3152 ho._moveNode({ node: win, top: st, left: sl});
3154 this._cancelScroll();
3160 this._cancelScroll();
3166 * @method _initScroll
3167 * @description Cancel a previous scroll timer and init a new one.
3169 _initScroll: function() {
3170 this._cancelScroll();
3171 this._scrollTimer = Y.Lang.later(this.get('scrollDelay'), this, this._checkWinScroll, [true], true);
3176 * @method _cancelScroll
3177 * @description Cancel a currently running scroll timer.
3179 _cancelScroll: function() {
3180 this._scrolling = false;
3181 if (this._scrollTimer) {
3182 this._scrollTimer.cancel();
3183 delete this._scrollTimer;
3188 * @description Called from the drag:align event to determine if we need to scroll.
3190 align: function(e) {
3191 if (this._scrolling) {
3192 this._cancelScroll();
3195 if (!this._scrolling) {
3196 this._checkWinScroll();
3201 * @method _setDimCache
3202 * @description Set the cache of the dragNode dims.
3204 _setDimCache: function() {
3205 var node = this.get(HOST).get('dragNode');
3207 h: node.get(OFFSET_HEIGHT),
3208 w: node.get(OFFSET_WIDTH)
3213 * @description Called from the drag:start event
3216 this._setDimCache();
3220 * @description Called from the drag:end event
3223 this._dimCache = null;
3224 this._cancelScroll();
3228 * @description General toString method for logging
3229 * @return String name for the object
3231 toString: function() {
3232 return S.NAME + ' #' + this.get('node').get('id');
3236 Y.namespace('Plugin');
3240 * Extends the Scroll class to make the window scroll while dragging.
3241 * @class DDWindowScroll
3242 * @extends DD.Scroll
3247 WS.superclass.constructor.apply(this, arguments);
3249 WS.ATTRS = Y.merge(S.ATTRS, {
3251 * @attribute windowScroll
3252 * @description Turn on window scroll support, default: true
3257 setter: function(scroll) {
3259 this.set(PARENT_SCROLL, Y.one('win'));
3266 //Shouldn't have to do this..
3267 initializer: function() {
3268 this.set('windowScroll', this.get('windowScroll'));
3273 * @default winscroll
3277 * @description The Scroll instance will be placed on the Drag instance under the winscroll namespace.
3280 WS.NAME = WS.NS = 'winscroll';
3281 Y.Plugin.DDWinScroll = WS;
3285 * Extends the Scroll class to make a parent node scroll while dragging.
3286 * @class DDNodeScroll
3287 * @extends DD.Scroll
3292 NS.superclass.constructor.apply(this, arguments);
3295 NS.ATTRS = Y.merge(S.ATTRS, {
3298 * @description The node we want to scroll. Used to set the internal parentScroll attribute.
3303 setter: function(node) {
3304 var n = Y.one(node);
3306 if (node !== false) {
3307 Y.error('DDNodeScroll: Invalid Node Given: ' + node);
3310 this.set(PARENT_SCROLL, n);
3317 //Shouldn't have to do this..
3318 initializer: function() {
3319 this.set('node', this.get('node'));
3324 * @default nodescroll
3328 * @description The NodeScroll instance will be placed on the Drag instance under the nodescroll namespace.
3331 NS.NAME = NS.NS = 'nodescroll';
3332 Y.Plugin.DDNodeScroll = NS;
3338 }, '3.3.0' ,{requires:['dd-drag'], skinnable:false, optional:['dd-proxy']});
3339 YUI.add('dd-drop', function(Y) {
3343 * Provides the ability to create a Drop Target.
3345 * @submodule dd-drop
3348 * Provides the ability to create a Drop Target.
3357 OFFSET_HEIGHT = 'offsetHeight',
3358 OFFSET_WIDTH = 'offsetWidth',
3361 * @description Fires when a drag element is over this target.
3362 * @param {Event.Facade} event An Event Facade object with the following specific property added:
3364 * <dt>drop</dt><dd>The drop object at the time of the event.</dd>
3365 * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
3368 * @type {Event.Custom}
3370 EV_DROP_OVER = 'drop:over',
3373 * @description Fires when a drag element enters this target.
3374 * @param {Event.Facade} event An Event Facade object with the following specific property added:
3376 * <dt>drop</dt><dd>The drop object at the time of the event.</dd>
3377 * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
3380 * @type {Event.Custom}
3382 EV_DROP_ENTER = 'drop:enter',
3385 * @description Fires when a drag element exits this target.
3386 * @param {Event.Facade} event An Event Facade object
3388 * @type {Event.Custom}
3390 EV_DROP_EXIT = 'drop:exit',
3394 * @description Fires when a draggable node is dropped on this Drop Target. (Fired from dd-ddm-drop)
3395 * @param {Event.Facade} event An Event Facade object with the following specific property added:
3397 * <dt>drop</dt><dd>The best guess on what was dropped on.</dd>
3398 * <dt>drag</dt><dd>The drag object at the time of the event.</dd>
3399 * <dt>others</dt><dd>An array of all the other drop targets that was dropped on.</dd>
3402 * @type {Event.Custom}
3407 this._lazyAddAttrs = false;
3408 Drop.superclass.constructor.apply(this, arguments);
3412 Y.on('domready', Y.bind(function() {
3413 Y.later(100, this, this._createShim);
3415 DDM._regTarget(this);
3418 if (Dom.getStyle(this.el, 'position') == 'fixed') {
3419 Event.on(window, 'scroll', function() {
3420 this.activateShim();
3431 * @description Y.Node instanace to use as the element to make a Drop Target
3435 setter: function(node) {
3436 var n = Y.one(node);
3438 Y.error('DD.Drop: Invalid Node Given: ' + node);
3445 * @description Array of groups to add this drop into.
3450 setter: function(g) {
3452 Y.each(g, function(v, k) {
3453 this._groups[v] = true;
3459 * @attribute padding
3460 * @description CSS style padding to make the Drop Target bigger than the node.
3465 setter: function(p) {
3466 return DDM.cssSizestoObject(p);
3471 * @description Set to lock this drop element.
3476 setter: function(lock) {
3478 this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-locked');
3480 this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-locked');
3487 * @attribute bubbles
3488 * @description Controls the default bubble parent for this Drop instance. Default: Y.DD.DDM. Set to false to disable bubbling. Use bubbleTargets in config.
3492 setter: function(t) {
3499 * @attribute useShim
3500 * @description Use the Drop shim. Default: true
3505 setter: function(v) {
3506 Y.DD.DDM._noShim = !v;
3512 Y.extend(Drop, Y.Base, {
3515 * @property _bubbleTargets
3516 * @description The default bubbleTarget for this object. Default: Y.DD.DDM
3518 _bubbleTargets: Y.DD.DDM,
3520 * @method addToGroup
3521 * @description Add this Drop instance to a group, this should be used for on-the-fly group additions.
3522 * @param {String} g The group to add this Drop Instance to.
3526 addToGroup: function(g) {
3527 this._groups[g] = true;
3531 * @method removeFromGroup
3532 * @description Remove this Drop instance from a group, this should be used for on-the-fly group removals.
3533 * @param {String} g The group to remove this Drop Instance from.
3537 removeFromGroup: function(g) {
3538 delete this._groups[g];
3543 * @method _createEvents
3544 * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
3546 _createEvents: function() {
3555 Y.each(ev, function(v, k) {
3569 * @description Flag for determining if the target is valid in this operation.
3576 * @description The groups this target belongs to.
3582 * @description Node reference to the targets shim
3588 * @description A region object associated with this target, used for checking regions while dragging.
3593 * @property overTarget
3594 * @description This flag is tripped when a drag element is over this target.
3600 * @description Check if this target is in one of the supplied groups.
3601 * @param {Array} groups The groups to check against
3604 inGroup: function(groups) {
3605 this._valid = false;
3607 Y.each(groups, function(v, k) {
3608 if (this._groups[v]) {
3617 * @method initializer
3618 * @description Private lifecycle method
3620 initializer: function(cfg) {
3621 Y.later(100, this, this._createEvents);
3623 var node = this.get(NODE), id;
3624 if (!node.get('id')) {
3628 node.addClass(DDM.CSS_PREFIX + '-drop');
3629 //Shouldn't have to do this..
3630 this.set('groups', this.get('groups'));
3634 * @method destructor
3635 * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
3637 destructor: function() {
3638 DDM._unregTarget(this);
3639 if (this.shim && (this.shim !== this.get(NODE))) {
3640 this.shim.detachAll();
3644 this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop');
3649 * @method _deactivateShim
3650 * @description Removes classes from the target, resets some flags and sets the shims deactive position [-999, -999]
3652 _deactivateShim: function() {
3656 this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
3657 this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
3658 this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
3660 if (this.get('useShim')) {
3661 this.shim.setStyles({
3667 this.overTarget = false;
3671 * @method _activateShim
3672 * @description Activates the shim and adds some interaction CSS classes
3674 _activateShim: function() {
3675 if (!DDM.activeDrag) {
3676 return false; //Nothing is dragging, no reason to activate.
3678 if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
3681 if (this.get('lock')) {
3684 var node = this.get(NODE);
3685 //TODO Visibility Check..
3686 //if (this.inGroup(DDM.activeDrag.get('groups')) && this.get(NODE).isVisible()) {
3687 if (this.inGroup(DDM.activeDrag.get('groups'))) {
3688 node.removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
3689 node.addClass(DDM.CSS_PREFIX + '-drop-active-valid');
3690 DDM._addValid(this);
3691 this.overTarget = false;
3692 if (!this.get('useShim')) {
3693 this.shim = this.get(NODE);
3697 DDM._removeValid(this);
3698 node.removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
3699 node.addClass(DDM.CSS_PREFIX + '-drop-active-invalid');
3704 * @description Positions and sizes the shim with the raw data from the node, this can be used to programatically adjust the Targets shim for Animation..
3706 sizeShim: function() {
3707 if (!DDM.activeDrag) {
3708 return false; //Nothing is dragging, no reason to activate.
3710 if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
3713 //if (this.get('lock') || !this.get('useShim')) {
3714 if (this.get('lock')) {
3718 Y.later(100, this, this.sizeShim);
3721 var node = this.get(NODE),
3722 nh = node.get(OFFSET_HEIGHT),
3723 nw = node.get(OFFSET_WIDTH),
3725 p = this.get('padding'),
3730 nw = nw + p.left + p.right;
3731 nh = nh + p.top + p.bottom;
3732 xy[0] = xy[0] - p.left;
3733 xy[1] = xy[1] - p.top;
3736 if (DDM.activeDrag.get('dragMode') === DDM.INTERSECT) {
3737 //Intersect Mode, make the shim bigger
3738 dd = DDM.activeDrag;
3739 dH = dd.get(NODE).get(OFFSET_HEIGHT);
3740 dW = dd.get(NODE).get(OFFSET_WIDTH);
3744 xy[0] = xy[0] - (dW - dd.deltaXY[0]);
3745 xy[1] = xy[1] - (dH - dd.deltaXY[1]);
3749 if (this.get('useShim')) {
3750 //Set the style on the shim
3751 this.shim.setStyles({
3759 //Create the region to be used by intersect when a drag node is over us.
3772 * @method _createShim
3773 * @description Creates the Target shim and adds it to the DDM's playground..
3775 _createShim: function() {
3776 //No playground, defer
3778 Y.later(10, this, this._createShim);
3781 //Shim already here, cancel
3785 var s = this.get('node');
3787 if (this.get('useShim')) {
3788 s = Y.Node.create('<div id="' + this.get(NODE).get('id') + '_shim"></div>');
3790 height: this.get(NODE).get(OFFSET_HEIGHT) + 'px',
3791 width: this.get(NODE).get(OFFSET_WIDTH) + 'px',
3792 backgroundColor: 'yellow',
3798 position: 'absolute'
3801 DDM._pg.appendChild(s);
3803 s.on('mouseover', Y.bind(this._handleOverEvent, this));
3804 s.on('mouseout', Y.bind(this._handleOutEvent, this));
3812 * @method _handleOverTarget
3813 * @description This handles the over target call made from this object or from the DDM
3815 _handleTargetOver: function() {
3816 if (DDM.isOverTarget(this)) {
3817 this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-over');
3818 DDM.activeDrop = this;
3819 DDM.otherDrops[this] = this;
3820 if (this.overTarget) {
3821 DDM.activeDrag.fire('drag:over', { drop: this, drag: DDM.activeDrag });
3822 this.fire(EV_DROP_OVER, { drop: this, drag: DDM.activeDrag });
3824 //Prevent an enter before a start..
3825 if (DDM.activeDrag.get('dragging')) {
3826 this.overTarget = true;
3827 this.fire(EV_DROP_ENTER, { drop: this, drag: DDM.activeDrag });
3828 DDM.activeDrag.fire('drag:enter', { drop: this, drag: DDM.activeDrag });
3829 DDM.activeDrag.get(NODE).addClass(DDM.CSS_PREFIX + '-drag-over');
3830 //TODO - Is this needed??
3831 //DDM._handleTargetOver();
3840 * @method _handleOverEvent
3841 * @description Handles the mouseover DOM event on the Target Shim
3843 _handleOverEvent: function() {
3844 this.shim.setStyle('zIndex', '999');
3845 DDM._addActiveShim(this);
3849 * @method _handleOutEvent
3850 * @description Handles the mouseout DOM event on the Target Shim
3852 _handleOutEvent: function() {
3853 this.shim.setStyle('zIndex', '1');
3854 DDM._removeActiveShim(this);
3858 * @method _handleOut
3859 * @description Handles out of target calls/checks
3861 _handleOut: function(force) {
3862 if (!DDM.isOverTarget(this) || force) {
3863 if (this.overTarget) {
3864 this.overTarget = false;
3866 DDM._removeActiveShim(this);
3868 if (DDM.activeDrag) {
3869 this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
3870 DDM.activeDrag.get(NODE).removeClass(DDM.CSS_PREFIX + '-drag-over');
3871 this.fire(EV_DROP_EXIT);
3872 DDM.activeDrag.fire('drag:exit', { drop: this });
3873 delete DDM.otherDrops[this];
3886 }, '3.3.0' ,{requires:['dd-ddm-drop', 'dd-drag'], skinnable:false});
3887 YUI.add('dd-delegate', function(Y) {
3891 * Provides the ability to drag multiple nodes under a container element using only one Y.DD.Drag instance as a delegate.
3893 * @submodule dd-delegate
3896 * Provides the ability to drag multiple nodes under a container element using only one Y.DD.Drag instance as a delegate.
3904 var Delegate = function(o) {
3905 Delegate.superclass.constructor.apply(this, arguments);
3909 _tmpNode = Y.Node.create('<div>Temp Node</div>');
3912 Y.extend(Delegate, Y.Base, {
3915 * @property _bubbleTargets
3916 * @description The default bubbleTarget for this object. Default: Y.DD.DDM
3918 _bubbleTargets: Y.DD.DDM,
3921 * @description A reference to the temporary dd instance used under the hood.
3925 * @property _shimState
3927 * @description The state of the Y.DD.DDM._noShim property to it can be reset.
3932 * @property _handles
3933 * @description Array of event handles to be destroyed
3938 * @method _onNodeChange
3939 * @description Listens to the nodeChange event and sets the dragNode on the temp dd instance.
3940 * @param {Event} e The Event.
3942 _onNodeChange: function(e) {
3943 this.set('dragNode', e.newVal);
3947 * @method _afterDragEnd
3948 * @description Listens for the drag:end event and updates the temp dd instance.
3949 * @param {Event} e The Event.
3951 _afterDragEnd: function(e) {
3952 Y.DD.DDM._noShim = this._shimState;
3954 this.set('lastNode', this.dd.get('node'));
3955 this.get('lastNode').removeClass(Y.DD.DDM.CSS_PREFIX + '-dragging');
3957 this.dd.set('node', _tmpNode);
3961 * @method _delMouseDown
3962 * @description The callback for the Y.DD.Delegate instance used
3963 * @param {Event} e The MouseDown Event.
3965 _delMouseDown: function(e) {
3966 var tar = e.currentTarget,
3969 if (tar.test(this.get(NODES)) && !tar.test(this.get('invalid'))) {
3970 this._shimState = Y.DD.DDM._noShim;
3971 Y.DD.DDM._noShim = true;
3972 this.set('currentNode', tar);
3973 dd.set('node', tar);
3975 dd.set('dragNode', Y.DD.DDM._proxy);
3977 dd.set('dragNode', tar);
3981 dd.fire('drag:mouseDown', { ev: e });
3986 * @method _onMouseEnter
3987 * @description Sets the target shim state
3988 * @param {Event} e The MouseEnter Event
3990 _onMouseEnter: function(e) {
3991 this._shimState = Y.DD.DDM._noShim;
3992 Y.DD.DDM._noShim = true;
3996 * @method _onMouseLeave
3997 * @description Resets the target shim state
3998 * @param {Event} e The MouseLeave Event
4000 _onMouseLeave: function(e) {
4001 Y.DD.DDM._noShim = this._shimState;
4003 initializer: function(cfg) {
4005 //Create a tmp DD instance under the hood.
4006 var conf = Y.clone(this.get('dragConfig') || {}),
4007 cont = this.get(CONT);
4009 conf.node = _tmpNode.cloneNode(true);
4010 conf.bubbleTargets = this;
4012 if (this.get('handles')) {
4013 conf.handles = this.get('handles');
4016 this.dd = new Y.DD.Drag(conf);
4018 //On end drag, detach the listeners
4019 this.dd.after('drag:end', Y.bind(this._afterDragEnd, this));
4020 this.dd.on('dragNodeChange', Y.bind(this._onNodeChange, this));
4021 this.dd.after('drag:mouseup', function() {
4025 //Attach the delegate to the container
4026 this._handles.push(Y.delegate(Y.DD.Drag.START_EVENT, Y.bind(this._delMouseDown, this), cont, this.get(NODES)));
4028 this._handles.push(Y.on('mouseenter', Y.bind(this._onMouseEnter, this), cont));
4030 this._handles.push(Y.on('mouseleave', Y.bind(this._onMouseLeave, this), cont));
4032 Y.later(50, this, this.syncTargets);
4033 Y.DD.DDM.regDelegate(this);
4036 * @method syncTargets
4037 * @description Applies the Y.Plugin.Drop to all nodes matching the cont + nodes selector query.
4041 syncTargets: function() {
4042 if (!Y.Plugin.Drop || this.get('destroyed')) {
4045 var items, groups, config;
4047 if (this.get('target')) {
4048 items = Y.one(this.get(CONT)).all(this.get(NODES));
4049 groups = this.dd.get('groups');
4050 config = this.get('dragConfig');
4052 if (config && 'groups' in config) {
4053 groups = config.groups;
4056 items.each(function(i) {
4057 this.createDrop(i, groups);
4063 * @method createDrop
4064 * @description Apply the Drop plugin to this node
4065 * @param {Node} node The Node to apply the plugin to
4066 * @param {Array} groups The default groups to assign this target to.
4069 createDrop: function(node, groups) {
4076 node.plug(Y.Plugin.Drop, config);
4078 node.drop.set('groups', groups);
4081 destructor: function() {
4085 if (Y.Plugin.Drop) {
4086 var targets = Y.one(this.get(CONT)).all(this.get(NODES));
4087 targets.unplug(Y.Plugin.Drop);
4089 Y.each(this._handles, function(v) {
4097 * @attribute container
4098 * @description A selector query to get the container to listen for mousedown events on. All "nodes" should be a child of this container.
4106 * @description A selector query to get the children of the "container" to make draggable elements from.
4110 value: '.dd-draggable'
4113 * @attribute invalid
4114 * @description A selector query to test a node to see if it's an invalid item.
4118 value: 'input, select, button, a, textarea'
4121 * @attribute lastNode
4122 * @description Y.Node instance of the last item dragged.
4129 * @attribute currentNode
4130 * @description Y.Node instance of the dd node.
4137 * @attribute dragNode
4138 * @description Y.Node instance of the dd dragNode.
4146 * @description Is the mouse currently over the container
4154 * @description Should the items also be a drop target.
4161 * @attribute dragConfig
4162 * @description The default config to be used when creating the DD instance.
4169 * @attribute handles
4170 * @description The handles config option added to the temp DD instance.
4183 * @property _delegates
4184 * @description Holder for all Y.DD.Delegate instances
4190 * @method regDelegate
4191 * @description Register a Delegate with the DDM
4193 regDelegate: function(del) {
4194 this._delegates.push(del);
4198 * @method getDelegate
4199 * @description Get a delegate instance from a container node
4200 * @returns Y.DD.Delegate
4202 getDelegate: function(node) {
4205 Y.each(this._delegates, function(v) {
4206 if (node.test(v.get(CONT))) {
4215 Y.DD.Delegate = Delegate;
4219 }, '3.3.0' ,{requires:['dd-drag', 'event-mouseenter'], skinnable:false, optional:['dd-drop-plugin']});
4222 YUI.add('dd', function(Y){}, '3.3.0' ,{skinnable:false, use:['dd-ddm-base', 'dd-ddm', 'dd-ddm-drop', 'dd-drag', 'dd-proxy', 'dd-constrain', 'dd-drop', 'dd-scroll', 'dd-delegate']});