2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
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.
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.
62 setter: function(mode) {
63 this._setDragMode(mode);
70 Y.extend(DDMBase, Y.Base, {
73 * @description flag set when we activate our first drag, so DDM can start listening for events.
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
84 _setDragMode: function(mode) {
86 mode = Y.DD.DDM.get('dragMode');
102 * @property CSS_PREFIX
103 * @description The PREFIX to attach to all DD CSS class names
106 CSS_PREFIX: 'yui-dd',
107 _activateTargets: function() {},
111 * @description Holder for all registered drag elements.
116 * @property activeDrag
117 * @description A reference to the currently active draggable object.
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
127 _regDrag: function(d) {
128 if (this.getDrag(d.get('node'))) {
133 this._setupListeners();
141 * @description Remove this drag object from the DDM._drags array.
142 * @param {Drag} d The drag object.
144 _unregDrag: function(d) {
146 Y.each(this._drags, function(n, i) {
155 * @method _setupListeners
156 * @description Add the document listeners.
158 _setupListeners: function() {
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));
168 * @description Internal method used by Drag to signal the start of a drag operation
171 this.fire('ddm:start');
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
183 _startDrag: function() {},
187 * @description Factory method to be overwritten by other DDM's
189 _endDrag: function() {},
190 _dropMove: function() {},
194 * @description Internal method used by Drag to signal the end of a drag operation
197 if (this.activeDrag) {
199 this.fire('ddm:end');
200 this.activeDrag.end.call(this.activeDrag);
201 this.activeDrag = null;
206 * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
210 stopDrag: function() {
211 if (this.activeDrag) {
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
222 _move: function(ev) {
223 if (this.activeDrag) {
224 this.activeDrag._move.call(this.activeDrag, ev);
229 * //TODO Private, rename??...
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.
236 cssSizestoObject: function(gutter) {
237 var x = gutter.split(' ');
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;
246 top : parseInt(x[0],10),
247 right : parseInt(x[1],10),
248 bottom: parseInt(x[2],10),
249 left : parseInt(x[3],10)
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
258 getDrag: function(node) {
261 if (n instanceof Y.Node) {
262 Y.each(this._drags, function(v, k) {
263 if (n.compareTo(v.get('node'))) {
273 Y.DD.DDM = new DDMBase();
277 * @description Fires from the DDM before all drag events fire.
278 * @type {Event.Custom}
282 * @description Fires from the DDM after the DDM finishes, before the drag end events.
283 * @type {Event.Custom}
289 }, '3.0.0' ,{requires:['node', 'base'], skinnable:false});
290 YUI.add('dd-ddm', function(Y) {
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.
305 * @description The shim placed over the screen to track the mousemove event.
311 * @property _debugShim
312 * @description Set this to true to set the shims opacity to .5 for debugging it, default: false.
316 _activateTargets: function() {},
317 _deactivateTargets: function() {},
318 _startDrag: function() {
319 if (this.activeDrag.get('useShim')) {
321 this._activateTargets();
324 _endDrag: function() {
325 this._pg_deactivate();
326 this._deactivateTargets();
330 * @method _pg_deactivate
331 * @description Deactivates the shim
333 _pg_deactivate: function() {
334 this._pg.setStyle('display', 'none');
338 * @method _pg_activate
339 * @description Activates the shim
341 _pg_activate: function() {
342 var ah = this.activeDrag.get('activeHandle'), cur = 'auto';
344 cur = ah.getStyle('cursor');
347 cur = this.get('dragCursor');
355 opacity: ((this._debugShim) ? '.5' : '0'),
362 * @description Sizes the shim on: activatation, window:scroll, window:resize
364 _pg_size: function() {
365 if (this.activeDrag) {
366 var b = Y.get('body'),
367 h = b.get('docHeight'),
368 w = b.get('docWidth');
378 * @description Creates the shim and adds it's listeners to it.
380 _createPG: function() {
381 var pg = Y.Node.create('<div></div>'),
386 position: 'absolute',
389 backgroundColor: 'red',
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'));
402 this._pg.on('mouseup', Y.bind(this._end, this));
403 this._pg.on('mousemove', Y.bind(this._move, this));
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));
411 Y.on('domready', Y.bind(Y.DD.DDM._createPG, Y.DD.DDM));
417 }, '3.0.0' ,{requires:['dd-ddm-base', 'event-resize'], skinnable:false});
418 YUI.add('dd-ddm-drop', function(Y) {
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.
424 * @submodule dd-ddm-drop
429 //TODO CSS class name for the bestMatch..
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.
440 * @property _activeShims
441 * @description Placeholder for all active shims on the page
447 * @method _hasActiveShim
448 * @description This method checks the _activeShims Object to see if there is a shim active.
451 _hasActiveShim: function() {
455 return this._activeShims.length;
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.
463 _addActiveShim: function(d) {
464 this._activeShims[this._activeShims.length] = d;
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.
472 _removeActiveShim: function(d) {
474 Y.each(this._activeShims, function(v, k) {
475 if (v._yuid !== d._yuid) {
480 this._activeShims = s;
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.
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) {
498 * @description The mode that the drag operations will run in 0 for Point, 1 for Intersect, 2 for Strict
505 * @description In point mode, a Drop is targeted by the cursor being over the Target
511 * @property INTERSECT
512 * @description In intersect mode, a Drop is targeted by "part" of the drag node being over the Target
519 * @description In strict mode, a Drop is targeted by the "entire" drag node being over the Target
525 * @description Should we only check targets that are in the viewport on drags (for performance), default: true
530 * @property activeDrop
531 * @description A reference to the active Drop Target
536 * @property validDrops
537 * @description An array of the valid Drop Targets for this interaction.
540 //TODO Change array/object literals to be in sync..
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)
550 * @description All of the Targets
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
562 _addValid: function(drop) {
563 this.validDrops[this.validDrops.length] = drop;
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
574 _removeValid: function(drop) {
576 Y.each(this.validDrops, function(v, k) {
578 drops[drops.length] = v;
582 this.validDrops = drops;
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
591 isOverTarget: function(drop) {
592 if (this.activeDrag && drop) {
593 var xy = this.activeDrag.mouseXY, r, dMode = this.activeDrag.get('dragMode'),
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);
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;
605 return drop.shim.intersect({
610 }, drop.region).inRegion;
625 * @description Clears the cache data used for this interaction.
627 clearCache: function() {
628 this.validDrops = [];
629 this.otherDrops = {};
630 this._activeShims = [];
634 * @method _activateTargets
635 * @description Clear the cache and activate the shims of all the targets
637 _activateTargets: function() {
639 Y.each(this.targets, function(v, k) {
640 v._activateShim.apply(v, []);
642 this._handleTargetOver();
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}
652 getBestMatch: function(drops, all) {
653 var biggest = null, area = 0, out;
655 Y.each(drops, function(v, k) {
656 var inter = this.activeDrag.get('dragNode').intersect(v.get('node'));
657 v.region.area = inter.area;
659 if (inter.inRegion) {
660 if (inter.area > area) {
668 //TODO Sort the others in numeric order by area covered..
669 Y.each(drops, function(v, k) {
674 return [biggest, out];
681 * @method _deactivateTargets
682 * @description This method fires the drop:hit, drag:drophit, drag:dropmiss methods and deactivates the shims..
684 _deactivateTargets: function() {
686 activeDrag = this.activeDrag,
687 activeDrop = this.activeDrop;
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];
696 tmp = this.getBestMatch(this.otherDrops, true);
700 activeDrag.get('node').removeClass(this.CSS_PREFIX + '-drag-over');
702 activeDrop.fire('drop:hit', { drag: activeDrag, drop: activeDrop, others: other });
703 activeDrag.fire('drag:drophit', { drag: activeDrag, drop: activeDrop, others: other });
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] });
711 this.activeDrop = null;
713 Y.each(this.targets, function(v, k) {
714 v._deactivateShim.apply(v, []);
720 * @description This method is called when the move method is called on the Drag Object.
722 _dropMove: function() {
723 if (this._hasActiveShim()) {
724 this._handleTargetOver();
726 Y.each(this.otherDrops, function(v, k) {
727 v._handleOut.apply(v, []);
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.
737 _lookup: function() {
738 if (!this.useHash || this._noShim) {
739 return this.validDrops;
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;
753 * @method _handleTargetOver
754 * @description This method execs _handleTargetOver on all valid Drop Targets
756 _handleTargetOver: function() {
757 var drops = this._lookup();
758 Y.each(drops, function(v, k) {
759 v._handleTargetOver.call(v);
765 * @description Add the passed in Target to the targets collection
766 * @param {Object} t The Target to add to the targets collection
768 _regTarget: function(t) {
769 this.targets[this.targets.length] = t;
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
777 _unregTarget: function(drop) {
778 var targets = [], vdrops;
779 Y.each(this.targets, function(v, k) {
781 targets[targets.length] = v;
784 this.targets = targets;
787 Y.each(this.validDrops, function(v, k) {
789 vdrops[vdrops.length] = v;
793 this.validDrops = vdrops;
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
801 getDrop: function(node) {
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'))) {
821 }, '3.0.0' ,{requires:['dd-ddm'], skinnable:false});
822 YUI.add('dd-drag', function(Y) {
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.
831 * This class provides the ability to drag a 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',
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.
853 * @type {Event.Custom}
855 EV_MOUSE_DOWN = 'drag:mouseDown',
857 * @event drag:afterMouseDown
858 * @description Fires after the mousedown event has been cleared.
859 * @param {Event.Facade} ev The mousedown event.
861 * @type {Event.Custom}
863 EV_AFTER_MOUSE_DOWN = 'drag:afterMouseDown',
865 * @event drag:removeHandle
866 * @description Fires after a handle is removed.
868 * @type {Event.Custom}
870 EV_REMOVE_HANDLE = 'drag:removeHandle',
872 * @event drag:addHandle
873 * @description Fires after a handle is added.
875 * @type {Event.Custom}
877 EV_ADD_HANDLE = 'drag:addHandle',
879 * @event drag:removeInvalid
880 * @description Fires after an invalid selector is removed.
882 * @type {Event.Custom}
884 EV_REMOVE_INVALID = 'drag:removeInvalid',
886 * @event drag:addInvalid
887 * @description Fires after an invalid selector is added.
889 * @type {Event.Custom}
891 EV_ADD_INVALID = 'drag:addInvalid',
894 * @description Fires at the start of a drag operation.
896 * @type {Event.Custom}
898 EV_START = 'drag:start',
901 * @description Fires at the end of a drag operation.
903 * @type {Event.Custom}
908 * @description Fires every mousemove during a drag operation.
910 * @type {Event.Custom}
912 EV_DRAG = 'drag:drag',
915 * @preventable _defAlignFn
916 * @description Fires when this node is aligned.
918 * @type {Event.Custom}
920 EV_ALIGN = 'drag:align',
923 * @description Fires when this node is over a Drop Target. (Fired from dd-drop)
925 * @type {Event.Custom}
929 * @description Fires when this node enters a Drop Target. (Fired from dd-drop)
931 * @type {Event.Custom}
935 * @description Fires when this node exits a Drop Target. (Fired from dd-drop)
937 * @type {Event.Custom}
940 * @event drag:drophit
941 * @description Fires when this node is dropped on a valid Drop Target. (Fired from dd-ddm-drop)
943 * @type {Event.Custom}
946 * @event drag:dropmiss
947 * @description Fires when this node is dropped on an invalid Drop Target. (Fired from dd-ddm-drop)
949 * @type {Event.Custom}
953 this._lazyAddAttrs = false;
954 Drag.superclass.constructor.apply(this, arguments);
956 var valid = DDM._regDrag(this);
958 Y.error('Failed to register node, already in use: ' + o.node);
967 * @description Y.Node instanace to use as the element to initiate a drag operation
971 setter: function(node) {
974 Y.error('DD.Drag: Invalid Node Given: ' + node);
982 * @attribute dragNode
983 * @description Y.Node instanace to use as the draggable element, defaults to node
987 setter: function(node) {
988 var n = Y.Node.get(node);
990 Y.error('DD.Drag: Invalid dragNode Given: ' + node);
996 * @attribute offsetNode
997 * @description Offset the drag element by the difference in cursor position: default true
1004 * @attribute clickPixelThresh
1005 * @description The number of pixels to move to start a drag operation, default is 3.
1009 value: DDM.get('clickPixelThresh')
1012 * @attribute clickTimeThresh
1013 * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
1017 value: DDM.get('clickTimeThresh')
1021 * @description Set to lock this drag element so that it can't be dragged: default false.
1026 setter: function(lock) {
1028 this.get(NODE).addClass(DDM.CSS_PREFIX + '-locked');
1030 this.get(NODE).removeClass(DDM.CSS_PREFIX + '-locked');
1037 * @description A payload holder to store arbitrary data about this drag object, can be used to store any value.
1045 * @description If this is false, the drag element will not move with the cursor: default true. Can be used to "resize" the element.
1052 * @attribute useShim
1053 * @description Use the protective shim on all drag operations: default true. Only works with dd-ddm, not dd-ddm-base.
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.
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.
1072 primaryButtonOnly: {
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.
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.
1093 setter: function(config) {
1094 this._handleTarget(config);
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.
1105 setter: function(mode) {
1106 return DDM._setDragMode(mode);
1111 * @description Array of groups to add this drag into.
1116 getter: function() {
1117 if (!this._groups) {
1121 Y.each(this._groups, function(v, k) {
1122 ret[ret.length] = k;
1126 setter: function(g) {
1128 Y.each(g, function(v, k) {
1129 this._groups[v] = true;
1135 * @attribute handles
1136 * @description Array of valid handles to add. Adding something here will set all handles, even if previously added with addHandle
1141 setter: function(g) {
1144 Y.each(g, function(v, k) {
1145 this._handles[v] = true;
1148 this._handles = null;
1154 * @attribute bubbles
1155 * @description Controls the default bubble parent for this Drag instance. Default: Y.DD.DDM. Set to false to disable bubbling.
1164 Y.extend(Drag, Y.Base, {
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.
1172 addToGroup: function(g) {
1173 this._groups[g] = true;
1174 DDM._activateTargets();
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.
1184 removeFromGroup: function(g) {
1185 delete this._groups[g];
1186 DDM._activateTargets();
1191 * @description This will be a reference to the Drop instance associated with this drag if the target: true config attribute is set..
1197 * @method _handleTarget
1198 * @description Attribute handler for the target config attribute.
1199 * @param {Boolean/Object}
1200 * @return {Boolean/Object}
1202 _handleTarget: function(config) {
1204 if (config === false) {
1206 DDM._unregTarget(this.target);
1211 if (!Y.Lang.isObject(config)) {
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);
1226 * @description Storage Array for the groups this drag belongs to.
1232 * @method _createEvents
1233 * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
1235 _createEvents: function() {
1237 this.publish(EV_MOUSE_DOWN, {
1238 defaultFn: this._defMouseDownFn,
1245 this.publish(EV_ALIGN, {
1246 defaultFn: this._defAlignFn,
1253 this.publish(EV_DRAG, {
1254 defaultFn: this._defDragFn,
1261 this.publish(EV_END, {
1262 preventedFn: this._prevEndFn,
1270 EV_AFTER_MOUSE_DOWN,
1283 Y.each(ev, function(v, k) {
1294 if (this.get('bubbles')) {
1295 this.addTarget(this.get('bubbles'));
1303 * @description A private reference to the mousedown DOM event
1304 * @type {Event.Facade}
1309 * @property _startTime
1310 * @description The getTime of the mousedown event. Not used, just here in case someone wants/needs to use it.
1316 * @property _endTime
1317 * @description The getTime of the mouseup event. Not used, just here in case someone wants/needs to use it.
1323 * @property _handles
1324 * @description A private hash of the valid drag handles
1330 * @property _invalids
1331 * @description A private hash of the invalid selector strings
1337 * @property _invalidsDefault
1338 * @description A private hash of the default invalid selector strings: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true}
1341 _invalidsDefault: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true },
1344 * @property _dragThreshMet
1345 * @description Private flag to see if the drag threshhold was met
1348 _dragThreshMet: null,
1351 * @property _fromTimeout
1352 * @description Flag to determine if the drag operation came from a timeout
1358 * @property _clickTimeout
1359 * @description Holder for the setTimeout call
1362 _clickTimeout: null,
1365 * @description The offset of the mouse position to the element's position
1371 * @description The initial mouse position
1377 * @description The initial element position
1383 * @description The position of the element as it's moving (for offset calculations)
1389 * @description The xy that the node will be set to. Changing this will alter the position as it's dragged.
1395 * @description The real xy position of the node.
1401 * @description The XY coords of the mousemove
1407 * @description A region object associated with this drag, used for checking regions while dragging.
1413 * @method _handleMouseUp
1414 * @description Handler for the mouseup DOM event
1415 * @param {Event.Facade}
1417 _handleMouseUp: function(ev) {
1418 this._fixIEMouseUp();
1419 if (DDM.activeDrag) {
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.
1428 _fixDragStart: function(e) {
1433 * @method _ieSelectFix
1434 * @description The function we use as the onselectstart handler when we start a drag in Internet Explorer
1436 _ieSelectFix: function() {
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.
1444 _ieSelectBack: null,
1447 * @method _fixIEMouseDown
1448 * @description This method copies the onselectstart listner on the document to the _ieSelectFix property
1450 _fixIEMouseDown: function() {
1452 this._ieSelectBack = Y.config.doc.body.onselectstart;
1453 Y.config.doc.body.onselectstart = this._ieSelectFix;
1458 * @method _fixIEMouseUp
1459 * @description This method copies the _ieSelectFix property back to the onselectstart listner on the document.
1461 _fixIEMouseUp: function() {
1463 Y.config.doc.body.onselectstart = this._ieSelectBack;
1468 * @method _handleMouseDownEvent
1469 * @description Handler for the mousedown DOM event
1470 * @param {Event.Facade}
1472 _handleMouseDownEvent: function(ev) {
1473 this.fire(EV_MOUSE_DOWN, { ev: ev });
1477 * @method _defMouseDownFn
1478 * @description Handler for the mousedown DOM event
1479 * @param {Event.Facade}
1481 _defMouseDownFn: function(e) {
1483 this._dragThreshMet = false;
1486 if (this.get('primaryButtonOnly') && ev.button > 1) {
1489 if (this.validClick(ev)) {
1490 this._fixIEMouseDown();
1492 this._setStartPosition([ev.pageX, ev.pageY]);
1494 DDM.activeDrag = this;
1496 this._clickTimeout = Y.later(this.get('clickTimeThresh'), this, this._timeoutCheck);
1498 this.fire(EV_AFTER_MOUSE_DOWN, { ev: ev });
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}
1506 validClick: function(ev) {
1507 var r = false, n = 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) {
1524 if (n.contains(tar) || n.compareTo(tar)) {
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 + ' *')) {
1542 els = ev.currentTarget.queryAll(hTest);
1544 els.each(function(n, i) {
1545 if ((n.contains(tar) || n.compareTo(tar)) && !set) {
1547 this.set('activeHandle', n);
1551 this.set('activeHandle', this.get(NODE));
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.
1562 _setStartPosition: function(xy) {
1565 this.nodeXY = this.lastXY = this.realXY = this.get(NODE).getXY();
1567 if (this.get('offsetNode')) {
1568 this.deltaXY = [(this.startXY[0] - this.nodeXY[0]), (this.startXY[1] - this.nodeXY[1])];
1570 this.deltaXY = [0, 0];
1575 * @method _timeoutCheck
1576 * @description The method passed to setTimeout to determine if the clickTimeThreshold was met.
1578 _timeoutCheck: function() {
1579 if (!this.get('lock') && !this._dragThreshMet) {
1580 this._fromTimeout = this._dragThreshMet = true;
1582 this._alignNode([this._ev_md.pageX, this._ev_md.pageY], true);
1586 * @method removeHandle
1587 * @description Remove a Selector added by addHandle
1588 * @param {String} str The selector for the handle to be removed.
1592 removeHandle: function(str) {
1593 if (this._handles[str]) {
1594 delete this._handles[str];
1595 this.fire(EV_REMOVE_HANDLE, { handle: str });
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.
1606 addHandle: function(str) {
1607 if (!this._handles) {
1610 if (Y.Lang.isString(str)) {
1611 this._handles[str] = true;
1612 this.fire(EV_ADD_HANDLE, { handle: str });
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.
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 });
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.
1638 addInvalid: function(str) {
1639 if (Y.Lang.isString(str)) {
1640 this._invalids[str] = true;
1641 this.fire(EV_ADD_INVALID, { handle: str });
1647 * @method initializer
1648 * @description Internal init handler
1650 initializer: function() {
1651 this.get(NODE).dd = this;
1653 if (!this.get(NODE).get('id')) {
1654 var id = Y.stamp(this.get(NODE));
1655 this.get(NODE).set('id', id);
1660 this._invalids = Y.clone(this._invalidsDefault, true);
1662 this._createEvents();
1664 if (!this.get(DRAG_NODE)) {
1665 this.set(DRAG_NODE, this.get(NODE));
1669 //Don't prep the DD instance until all plugins are loaded.
1670 this.on('initializedChange', Y.bind(this._prep, this));
1672 //Shouldn't have to do this..
1673 this.set('groups', this.get('groups'));
1678 * @description Attach event listners and add classname
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));
1691 * @description Detach event listeners and remove classname
1693 _unprep: function() {
1694 var node = this.get(NODE);
1695 node.removeClass(DDM.CSS_PREFIX + '-draggable');
1700 * @description Starts the drag operation
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();
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
1716 var xy = this.nodeXY;
1728 this.set(DRAGGING, true);
1734 * @description Ends the drag operation
1739 this._endTime = (new Date()).getTime();
1740 if (this._clickTimeout) {
1741 this._clickTimeout.cancel();
1743 this._dragThreshMet = false;
1744 this._fromTimeout = false;
1745 if (!this.get('lock') && this.get(DRAGGING)) {
1747 pageX: this.lastXY[0],
1748 pageY: this.lastXY[1],
1749 startTime: this._startTime,
1750 endTime: this._endTime
1753 this.get(NODE).removeClass(DDM.CSS_PREFIX + '-dragging');
1754 this.set(DRAGGING, false);
1755 this.deltaXY = [0, 0];
1761 * @method _prevEndFn
1762 * @description Handler for preventing the drag:end event. It will reset the node back to it's start position
1764 _prevEndFn: function(e) {
1766 this.get(DRAG_NODE).setXY(this.nodeXY);
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.
1774 _align: function(xy) {
1775 this.fire(EV_ALIGN, {pageX: xy[0], pageY: xy[1] });
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.
1783 _defAlignFn: function(e) {
1784 this.actXY = [e.pageX - this.deltaXY[0], e.pageY - this.deltaXY[1]];
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.
1792 _alignNode: function(eXY) {
1799 * @description This method performs the actual element move.
1801 _moveNode: function(scroll) {
1802 //if (!this.get(DRAGGING)) {
1805 var diffXY = [], diffXY2 = [], startXY = this.nodeXY, xy = this.actXY;
1807 diffXY[0] = (xy[0] - this.lastXY[0]);
1808 diffXY[1] = (xy[1] - this.lastXY[1]);
1810 diffXY2[0] = (xy[0] - this.nodeXY[0]);
1811 diffXY2[1] = (xy[1] - this.nodeXY[1]);
1819 right: xy[0] + this.get(DRAG_NODE).get(OFFSET_WIDTH),
1820 bottom: xy[1] + this.get(DRAG_NODE).get(OFFSET_HEIGHT),
1824 this.fire(EV_DRAG, {
1840 * @method _defDragFn
1841 * @description Default function for drag:drag. Fired from _moveNode.
1842 * @param {Event.Facade} ev The drag:drag event
1844 _defDragFn: function(e) {
1845 if (this.get('move')) {
1847 e.scroll.node.set('scrollTop', e.scroll.top);
1848 e.scroll.node.set('scrollLeft', e.scroll.left);
1850 this.get(DRAG_NODE).setXY([e.pageX, e.pageY]);
1851 this.realXY = [e.pageX, e.pageY];
1857 * @description Fired from DragDropMgr (DDM) on mousemove.
1858 * @param {Event.Facade} ev The mousemove DOM event
1860 _move: function(ev) {
1861 if (this.get('lock')) {
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;
1871 this._alignNode([ev.pageX, ev.pageY]);
1874 if (this._clickTimeout) {
1875 this._clickTimeout.cancel();
1877 this._alignNode([ev.pageX, ev.pageY]);
1883 * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
1887 stopDrag: function() {
1888 if (this.get(DRAGGING)) {
1895 * @method destructor
1896 * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
1898 destructor: function() {
1902 this.target.destroy();
1904 DDM._unregDrag(this);
1914 }, '3.0.0' ,{requires:['dd-ddm-base'], skinnable:false});
1915 YUI.add('dd-proxy', function(Y) {
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.
1921 * @submodule dd-proxy
1924 * This plugin for dd-drag is for creating a proxy drag node, instead of dragging the original node.
1932 DRAG_NODE = 'dragNode',
1936 var P = function(config) {
1937 P.superclass.constructor.apply(this, arguments);
1943 * @description The Proxy instance will be placed on the Drag instance under the proxy namespace.
1952 * @attribute moveOnEnd
1953 * @description Move the original node at the end of the drag. Default: true
1960 * @attribute hideOnEnd
1961 * @description Hide the drag node at the end of the drag. Default: true
1968 * @attribute resizeFrame
1969 * @description Make the Proxy node assume the size of the original node. Default: true
1976 * @attribute positionProxy
1977 * @description Make the Proxy node appear in the same place as the original node. Default: true
1984 * @attribute borderStyle
1985 * @description The default border style for the border of the proxy. Default: 1px solid #808080
1989 value: '1px solid #808080'
1997 * @description Holds the event handles for setting the proxy
2003 * @description Handler for the proxy config attribute
2007 Y.on('domready', Y.bind(this._init, this));
2013 var i, h, h1, host = this.get(HOST), dnode = host.get(DRAG_NODE);
2014 if (dnode.compareTo(host.get(NODE))) {
2016 host.set(DRAG_NODE, DDM._proxy);
2019 Y.each(this._hands, function(v) {
2022 h = DDM.on('ddm:start', Y.bind(function() {
2023 if (DDM.activeDrag === host) {
2024 DDM._setFrame(host);
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);
2032 if (this.get('hideOnEnd')) {
2033 host.get(DRAG_NODE).setStyle('display', 'none');
2037 this._hands = [h, h1];
2039 initializer: function() {
2042 destructor: function() {
2043 var host = this.get(HOST);
2044 Y.each(this._hands, function(v) {
2047 host.set(DRAG_NODE, host.get(NODE));
2051 Y.namespace('Plugin');
2052 Y.extend(P, Y.Base, proto);
2053 Y.Plugin.DDProxy = P;
2055 //Add a couple of methods to the DDM
2061 * @method _createFrame
2062 * @description Create the proxy element if it doesn't already exist and set the DD.DDM._proxy value
2064 _createFrame: function() {
2068 var p = Y.Node.create('<div></div>'),
2069 b = Y.Node.get('body');
2072 position: 'absolute',
2079 b.insertBefore(p, b.get('firstChild'));
2080 p.set('id', Y.stamp(p));
2081 p.addClass(DDM.CSS_PREFIX + '-proxy');
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.
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'
2102 ah = DDM.activeDrag.get('activeHandle');
2104 cur = ah.getStyle('cursor');
2106 if (cur == 'auto') {
2107 cur = DDM.get('dragCursor');
2112 visibility: 'hidden',
2115 border: drag.proxy.get('borderStyle')
2120 if (drag.proxy.get('positionProxy')) {
2121 d.setXY(drag.nodeXY);
2123 d.setStyle('visibility', 'visible');
2127 //Create the frame when DOM is ready
2128 Y.on('domready', Y.bind(DDM._createFrame, DDM));
2132 }, '3.0.0' ,{requires:['dd-ddm', 'dd-drag'], skinnable:false});
2133 YUI.add('dd-constrain', function(Y) {
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.
2139 * @submodule dd-constrain
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
2149 var DRAG_NODE = 'dragNode',
2150 OFFSET_HEIGHT = 'offsetHeight',
2151 OFFSET_WIDTH = 'offsetWidth',
2153 CON_2_REGION = 'constrain2region',
2154 CON_2_NODE = 'constrain2node',
2155 TICK_X_ARRAY = 'tickXArray',
2156 TICK_Y_ARRAY = 'tickYArray',
2164 var C = function(config) {
2165 C.superclass.constructor.apply(this, arguments);
2168 C.NAME = 'DragConstrained';
2171 * @description The Constrained instance will be placed on the Drag instance under the con namespace.
2181 * @description Stick the drag movement to the X-Axis. Default: false
2189 * @description Stick the drag movement to the Y-Axis
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
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
2212 * @attribute tickXArray
2213 * @description An array of page coordinates to use as X ticks for drag movement.
2220 * @attribute tickYArray
2221 * @description An array of page coordinates to use as Y ticks for drag movement.
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.
2234 getter: function(r) {
2235 if (Y.Lang.isObject(r)) {
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])) {
2252 } else if (r !== false) {
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)
2265 setter: function(gutter) {
2266 return Y.DD.DDM.cssSizestoObject(gutter);
2270 * @attribute constrain2node
2271 * @description Will attempt to constrain the drag node to the boundaries of this node.
2276 setter: function(n) {
2277 if (!this.get(CON_2_REGION)) {
2278 var node = Y.Node.get(n);
2282 } else if (this.get(CON_2_REGION) !== false) {
2288 * @attribute constrain2view
2289 * @description Will attempt to constrain the drag node to the boundaries of the viewport region.
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));
2304 * @method _handleStart
2305 * @description Fires on drag:start and clears the _regionCache
2307 _handleStart: function() {
2308 this._regionCache = null;
2312 * @property _regionCache
2313 * @description Store a cache of the region that we are constraining to
2319 * @method _cacheRegion
2320 * @description Get's the region and caches it, called from window.resize and when the cache is null
2322 _cacheRegion: function() {
2323 this._regionCache = this.get(CON_2_NODE).get('region');
2327 * @description Get the active region: viewport, node, custom region
2328 * @param {Boolean} inc Include the node's height and width
2331 getRegion: function(inc) {
2332 var r = {}, oh = null, ow = null,
2333 g = this.get('gutter'),
2334 host = this.get(HOST);
2336 if (this.get(CON_2_NODE)) {
2337 if (!this._regionCache) {
2338 Y.on('resize', Y.bind(this._cacheRegion, this), window);
2339 this._cacheRegion();
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');
2350 Y.each(g, function(i, n) {
2351 if ((n == RIGHT) || (n == BOTTOM)) {
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;
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
2372 _checkRegion: function(_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);
2379 if (oxy[1] > (r[BOTTOM] - oh)) {
2380 _xy[1] = (r[BOTTOM] - oh);
2382 if (r[TOP] > oxy[1]) {
2386 if (oxy[0] > (r[RIGHT] - ow)) {
2387 _xy[0] = (r[RIGHT] - ow);
2389 if (r[LEFT] > oxy[0]) {
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.
2401 inRegion: function(xy) {
2402 xy = xy || this.get(HOST).get(DRAG_NODE).getXY();
2404 var _xy = this._checkRegion([xy[0], xy[1]]),
2406 if ((xy[0] === _xy[0]) && (xy[1] === _xy[1])) {
2413 * @description Modifies the Drag.actXY method from the after drag:align event. This is where the constraining happens.
2416 var host = this.get(HOST),
2418 r = this.getRegion(true);
2420 if (this.get('stickX')) {
2421 _xy[1] = (host.startXY[1] - host.deltaXY[1]);
2423 if (this.get('stickY')) {
2424 _xy[0] = (host.startXY[0] - host.deltaXY[0]);
2428 _xy = this._checkRegion(_xy);
2431 _xy = this._checkTicks(_xy, r);
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
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]);
2451 if (yt && !this.get(TICK_Y_ARRAY)) {
2452 xy[1] = DDM._calcTicks(xy[1], ly, yt, r[TOP], r[BOTTOM]);
2454 if (this.get(TICK_X_ARRAY)) {
2455 xy[0] = DDM._calcTickArray(xy[0], this.get(TICK_X_ARRAY), r[LEFT], r[RIGHT]);
2457 if (this.get(TICK_Y_ARRAY)) {
2458 xy[1] = DDM._calcTickArray(xy[1], this.get(TICK_Y_ARRAY), r[TOP], r[BOTTOM]);
2465 Y.namespace('Plugin');
2466 Y.extend(C, Y.Base, proto);
2467 Y.Plugin.DDConstrained = C;
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
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));
2492 pos = (start + (tick * (min + 1)));
2495 pos = (start + (tick * (min - 1)));
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
2514 _calcTickArray: function(pos, ticks, off1, off2) {
2515 var i = 0, len = ticks.length, next = 0,
2518 if (!ticks || (ticks.length === 0)) {
2520 } else if (ticks[0] >= pos) {
2523 for (i = 0; i < len; i++) {
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];
2534 ret = ticks[len - 1];
2542 return ticks[ticks.length - 1];
2550 }, '3.0.0' ,{requires:['dd-drag'], skinnable:false});
2551 YUI.add('dd-scroll', function(Y) {
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.
2557 * @submodule dd-scroll
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.
2568 var S = function() {
2569 S.superclass.constructor.apply(this, arguments);
2574 PARENT_SCROLL = 'parentScroll',
2575 WINDOW_SCROLL = 'windowScroll',
2576 SCROLL_TOP = 'scrollTop',
2577 SCROLL_LEFT = 'scrollLeft',
2578 OFFSET_WIDTH = 'offsetWidth',
2579 OFFSET_HEIGHT = 'offsetHeight';
2584 * @attribute parentScroll
2585 * @description Internal config option to hold the node that we are scrolling. Should not be set by the developer.
2590 setter: function(node) {
2599 * @description The number of pixels from the edge of the screen to turn on scrolling. Default: 30
2606 * @attribute scrollDelay
2607 * @description The number of milliseconds delay to pass to the auto scroller. Default: 235
2615 * @description The host we are plugged into.
2622 * @attribute windowScroll
2623 * @description Turn on window scroll support, default: false
2630 * @attribute vertical
2631 * @description Allow vertical scrolling, default: true.
2638 * @attribute horizontal
2639 * @description Allow horizontal scrolling, default: true.
2647 Y.extend(S, Y.Base, {
2650 * @property _scrolling
2651 * @description Tells if we are actively scrolling or not.
2657 * @property _vpRegionCache
2658 * @description Cache of the Viewport dims.
2661 _vpRegionCache: null,
2664 * @property _dimCache
2665 * @description Cache of the dragNode dims.
2671 * @property _scrollTimer
2672 * @description Holder for the Timer object returned from Y.later.
2678 * @method _getVPRegion
2679 * @description Sets the _vpRegionCache property with an Object containing the dims from the viewport.
2681 _getVPRegion: function() {
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]);
2695 right: (n.get(w) + l) - b,
2696 bottom: (n.get(h) + t) - b,
2699 this._vpRegionCache = r;
2701 // r = this._vpRegionCache;
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));
2711 //TODO - This doesn't work yet??
2712 Y.get(window).on('scroll', Y.bind(function() {
2713 this._vpRegionCache = null;
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
2722 _checkWinScroll: function(move) {
2723 var r = this._getVPRegion(),
2724 ho = this.get(HOST),
2725 ws = this.get(WINDOW_SCROLL),
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,
2743 if (this.get('horizontal')) {
2744 if (left <= r.left) {
2746 nl = xy[0] - ((ws) ? b : 0);
2749 if (right >= r.right) {
2751 nl = xy[0] + ((ws) ? b : 0);
2755 if (this.get('vertical')) {
2756 if (bottom >= r.bottom) {
2758 nt = xy[1] + ((ws) ? b : 0);
2764 nt = xy[1] - ((ws) ? b : 0);
2786 ho.actXY = [nl, nt];
2787 ho._moveNode({ node: win, top: st, left: sl});
2789 this._cancelScroll();
2795 this._cancelScroll();
2801 * @method _initScroll
2802 * @description Cancel a previous scroll timer and init a new one.
2804 _initScroll: function() {
2805 this._cancelScroll();
2806 this._scrollTimer = Y.Lang.later(this.get('scrollDelay'), this, this._checkWinScroll, [true], true);
2811 * @method _cancelScroll
2812 * @description Cancel a currently running scroll timer.
2814 _cancelScroll: function() {
2815 this._scrolling = false;
2816 if (this._scrollTimer) {
2817 this._scrollTimer.cancel();
2818 delete this._scrollTimer;
2823 * @description Called from the drag:align event to determine if we need to scroll.
2825 align: function(e) {
2826 if (this._scrolling) {
2827 this._cancelScroll();
2830 if (!this._scrolling) {
2831 this._checkWinScroll();
2836 * @method _setDimCache
2837 * @description Set the cache of the dragNode dims.
2839 _setDimCache: function() {
2840 var node = this.get(HOST).get('dragNode');
2842 h: node.get(OFFSET_HEIGHT),
2843 w: node.get(OFFSET_WIDTH)
2848 * @description Called from the drag:start event
2851 this._setDimCache();
2855 * @description Called from the drag:end event
2858 this._dimCache = null;
2859 this._cancelScroll();
2863 * @description General toString method for logging
2864 * @return String name for the object
2866 toString: function() {
2867 return S.NAME + ' #' + this.get('node').get('id');
2871 Y.namespace('Plugin');
2875 * Extends the Scroll class to make the window scroll while dragging.
2876 * @class DDWindowScroll
2877 * @extends DD.Scroll
2881 var WS = function() {
2882 WS.superclass.constructor.apply(this, arguments);
2884 WS.ATTRS = Y.merge(S.ATTRS, {
2886 * @attribute windowScroll
2887 * @description Turn on window scroll support, default: true
2892 setter: function(scroll) {
2894 this.set(PARENT_SCROLL, Y.get(window));
2901 //Shouldn't have to do this..
2902 initializer: function() {
2903 this.set('windowScroll', this.get('windowScroll'));
2906 WS.NAME = WS.NS = 'winscroll';
2907 Y.Plugin.DDWinScroll = WS;
2911 * Extends the Scroll class to make a parent node scroll while dragging.
2912 * @class DDNodeScroll
2913 * @extends DD.Scroll
2917 var NS = function() {
2918 NS.superclass.constructor.apply(this, arguments);
2921 NS.ATTRS = Y.merge(S.ATTRS, {
2924 * @description The node we want to scroll. Used to set the internal parentScroll attribute.
2929 setter: function(node) {
2930 var n = Y.get(node);
2932 if (node !== false) {
2933 Y.error('DDNodeScroll: Invalid Node Given: ' + node);
2937 this.set(PARENT_SCROLL, n);
2944 //Shouldn't have to do this..
2945 initializer: function() {
2946 this.set('node', this.get('node'));
2949 NS.NAME = NS.NS = 'nodescroll';
2950 Y.Plugin.DDNodeScroll = NS;
2956 }, '3.0.0' ,{skinnable:false, requires:['dd-drag'], optional:['dd-proxy']});
2957 YUI.add('dd-plugin', function(Y) {
2961 * This is a simple Drag plugin that can be attached to a Node via the plug method.
2963 * @submodule dd-plugin
2966 * This is a simple Drag plugin that can be attached to a Node via the plug method.
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);
2981 * @description dd-plugin
2984 Drag.NAME = "dd-plugin";
2988 * @description The Drag instance will be placed on the Node instance under the dd namespace. It can be accessed via Node.dd;
2994 Y.extend(Drag, Y.DD.Drag);
2995 Y.namespace('Plugin');
2996 Y.Plugin.Drag = Drag;
3002 }, '3.0.0' ,{skinnable:false, requires:['dd-drag'], optional:['dd-constrain', 'dd-proxy']});
3003 YUI.add('dd-drop', function(Y) {
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.
3009 * @submodule dd-drop
3012 * This class provides the ability to create a Drop Target.
3021 OFFSET_HEIGHT = 'offsetHeight',
3022 OFFSET_WIDTH = 'offsetWidth',
3025 * @description Fires when a drag element is over this target.
3027 * @type {Event.Custom}
3029 EV_DROP_OVER = 'drop:over',
3032 * @description Fires when a drag element enters this target.
3034 * @type {Event.Custom}
3036 EV_DROP_ENTER = 'drop:enter',
3039 * @description Fires when a drag element exits this target.
3041 * @type {Event.Custom}
3043 EV_DROP_EXIT = 'drop:exit',
3047 * @description Fires when a draggable node is dropped on this Drop Target. (Fired from dd-ddm-drop)
3049 * @type {Event.Custom}
3054 this._lazyAddAttrs = false;
3055 Drop.superclass.constructor.apply(this, arguments);
3059 Y.on('domready', Y.bind(function() {
3060 Y.later(100, this, this._createShim);
3062 DDM._regTarget(this);
3065 if (Dom.getStyle(this.el, 'position') == 'fixed') {
3066 Event.on(window, 'scroll', function() {
3067 this.activateShim();
3078 * @description Y.Node instanace to use as the element to make a Drop Target
3082 setter: function(node) {
3083 var n = Y.Node.get(node);
3085 Y.error('DD.Drop: Invalid Node Given: ' + node);
3092 * @description Array of groups to add this drop into.
3097 setter: function(g) {
3099 Y.each(g, function(v, k) {
3100 this._groups[v] = true;
3106 * @attribute padding
3107 * @description CSS style padding to make the Drop Target bigger than the node.
3112 setter: function(p) {
3113 return DDM.cssSizestoObject(p);
3118 * @description Set to lock this drop element.
3123 setter: function(lock) {
3125 this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-locked');
3127 this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-locked');
3133 * @attribute bubbles
3134 * @description Controls the default bubble parent for this Drop instance. Default: Y.DD.DDM. Set to false to disable bubbling.
3143 Y.extend(Drop, Y.Base, {
3146 * @method _createEvents
3147 * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
3149 _createEvents: function() {
3158 Y.each(ev, function(v, k) {
3169 if (this.get('bubbles')) {
3170 this.addTarget(this.get('bubbles'));
3177 * @description Flag for determining if the target is valid in this operation.
3184 * @description The groups this target belongs to.
3190 * @description Node reference to the targets shim
3196 * @description A region object associated with this target, used for checking regions while dragging.
3201 * @property overTarget
3202 * @description This flag is tripped when a drag element is over this target.
3208 * @description Check if this target is in one of the supplied groups.
3209 * @param {Array} groups The groups to check against
3212 inGroup: function(groups) {
3213 this._valid = false;
3215 Y.each(groups, function(v, k) {
3216 if (this._groups[v]) {
3225 * @method initializer
3226 * @description Private lifecycle method
3228 initializer: function() {
3229 //this._createEvents();
3230 Y.later(100, this, this._createEvents);
3232 var node = this.get(NODE), id;
3233 if (!node.get('id')) {
3237 node.addClass(DDM.CSS_PREFIX + '-drop');
3238 //Shouldn't have to do this..
3239 this.set('groups', this.get('groups'));
3243 * @method destructor
3244 * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
3246 destructor: function() {
3247 DDM._unregTarget(this);
3249 this.shim.detachAll();
3250 this.shim.get('parentNode').removeChild(this.shim);
3253 this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop');
3258 * @method _deactivateShim
3259 * @description Removes classes from the target, resets some flags and sets the shims deactive position [-999, -999]
3261 _deactivateShim: function() {
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({
3273 this.overTarget = false;
3277 * @method _activateShim
3278 * @description Activates the shim and adds some interaction CSS classes
3280 _activateShim: function() {
3281 if (!DDM.activeDrag) {
3282 return false; //Nothing is dragging, no reason to activate.
3284 if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
3287 if (this.get('lock')) {
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;
3300 DDM._removeValid(this);
3301 node.removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
3302 node.addClass(DDM.CSS_PREFIX + '-drop-active-invalid');
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..
3309 sizeShim: function() {
3310 if (!DDM.activeDrag) {
3311 return false; //Nothing is dragging, no reason to activate.
3313 if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
3316 if (this.get('lock')) {
3320 Y.later(100, this, this.sizeShim);
3323 var node = this.get(NODE),
3324 nh = node.get(OFFSET_HEIGHT),
3325 nw = node.get(OFFSET_WIDTH),
3327 p = this.get('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;
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);
3346 xy[0] = xy[0] - (dW - dd.deltaXY[0]);
3347 xy[1] = xy[1] - (dH - dd.deltaXY[1]);
3351 //Set the style on the shim
3352 this.shim.setStyles({
3359 //Create the region to be used by intersect when a drag node is over us.
3372 * @method _createShim
3373 * @description Creates the Target shim and adds it to the DDM's playground..
3375 _createShim: function() {
3376 //No playground, defer
3378 Y.later(10, this, this._createShim);
3381 //Shim already here, cancel
3385 var s = Y.Node.create('<div id="' + this.get(NODE).get('id') + '_shim"></div>');
3388 height: this.get(NODE).get(OFFSET_HEIGHT) + 'px',
3389 width: this.get(NODE).get(OFFSET_WIDTH) + 'px',
3390 backgroundColor: 'yellow',
3396 position: 'absolute'
3398 DDM._pg.appendChild(s);
3401 s.on('mouseover', Y.bind(this._handleOverEvent, this));
3402 s.on('mouseout', Y.bind(this._handleOutEvent, this));
3406 * @method _handleOverTarget
3407 * @description This handles the over target call made from this object or from the DDM
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 });
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();
3431 * @method _handleOverEvent
3432 * @description Handles the mouseover DOM event on the Target Shim
3434 _handleOverEvent: function() {
3435 this.shim.setStyle('zIndex', '999');
3436 DDM._addActiveShim(this);
3440 * @method _handleOutEvent
3441 * @description Handles the mouseout DOM event on the Target Shim
3443 _handleOutEvent: function() {
3444 this.shim.setStyle('zIndex', '1');
3445 DDM._removeActiveShim(this);
3449 * @method _handleOut
3450 * @description Handles out of target calls/checks
3452 _handleOut: function(force) {
3453 if (!DDM.isOverTarget(this) || force) {
3454 if (this.overTarget) {
3455 this.overTarget = false;
3457 DDM._removeActiveShim(this);
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;
3480 }, '3.0.0' ,{requires:['dd-ddm-drop', 'dd-drag'], skinnable:false});
3481 YUI.add('dd-drop-plugin', function(Y) {
3485 * This is a simple Drop plugin that can be attached to a Node via the plug method.
3487 * @submodule dd-drop-plugin
3490 * This is a simple Drop plugin that can be attached to a Node via the plug method.
3498 var Drop = function(config) {
3499 config.node = config.host;
3500 Drop.superclass.constructor.apply(this, arguments);
3505 * @description dd-drop-plugin
3508 Drop.NAME = "dd-drop-plugin";
3511 * @description The Drop instance will be placed on the Node instance under the drop namespace. It can be accessed via Node.drop;
3517 Y.extend(Drop, Y.DD.Drop);
3518 Y.namespace('Plugin');
3519 Y.Plugin.Drop = Drop;
3525 }, '3.0.0' ,{requires:['dd-drop'], skinnable:false});
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});