dd.js revision 500845218b5d6bae9d21550b32ea674d4287d6e7
d54d5f031b212b560ff1ce52c10079cefc92b8a4Ryan Grove * Provides the base Drag Drop Manger required for making a Node draggable.
d54d5f031b212b560ff1ce52c10079cefc92b8a4Ryan Grove * @module dd
d54d5f031b212b560ff1ce52c10079cefc92b8a4Ryan Grove * @submodule dd-ddm-base
d54d5f031b212b560ff1ce52c10079cefc92b8a4Ryan Grove * Provides the base Drag Drop Manger required for making a Node draggable.
d54d5f031b212b560ff1ce52c10079cefc92b8a4Ryan Grove * @class DDM
d54d5f031b212b560ff1ce52c10079cefc92b8a4Ryan Grove * @extends Base
d54d5f031b212b560ff1ce52c10079cefc92b8a4Ryan Grove * @constructor
d54d5f031b212b560ff1ce52c10079cefc92b8a4Ryan Grove * @namespace DD
var DDMBase = function() {
dragCursor: {
* @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
throttleTime: {
* @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.
dragMode: {
return mode;
_createPG: function() {},
_active: null,
* @param String/Number The Number value or the String for the DragMode to default all future drag instances to.
if (mode === null) {
switch (mode) {
_activateTargets: function() {},
_drags: [],
activeDrag: false,
* @description Adds a reference to the drag object to the DDM._drags array, called in the constructor of Drag.
_regDrag: function(d) {
if (!this._active) {
this._setupListeners();
_unregDrag: function(d) {
var tmp = [];
_setupListeners: function() {
this._createPG();
this._active = true;
_start: function() {
this._startDrag();
_startDrag: function() {},
_endDrag: function() {},
_dropMove: function() {},
_end: function() {
if (this.activeDrag) {
this._endDrag();
this.activeDrag = null;
* @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
stopDrag: function() {
if (this.activeDrag) {
this._end();
if (this.activeDrag) {
this._dropMove();
* @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)
switch (x.length) {
var drag = false,
if (n instanceof Y.Node) {
drag = v;
return drag;
return n1;
* @description Return a node instance from the given node, selector string or Y.Base extended object.
getNode: function(n) {
if (n && n.get) {
n = Y.one(n);
if (s == n1) {
return n1;
* 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.
* It is also required to have Drop Targets enabled, as the viewport shim will contain the shims for the Drop Targets.
_pg: null,
_debugShim: false,
_activateTargets: function() { },
_deactivateTargets: function() {},
_startDrag: function() {
this._pg_activate();
this._activateTargets();
_endDrag: function() {
this._pg_deactivate();
this._deactivateTargets();
_pg_deactivate: function() {
_pg_activate: function() {
if (ah) {
this._pg_size();
_pg_size: function() {
if (this.activeDrag) {
_createPG: function() {
* 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.
* @description This flag turns off the use of the mouseover/mouseout shim. It should not be used unless you know what you are doing.
_noShim: false,
_activeShims: [],
_hasActiveShim: function() {
if (this._noShim) {
_addActiveShim: function(d) {
_removeActiveShim: function(d) {
s[s.length] = v;
this._activeShims = s;
* @description This method will sync the position of the shims on the Drop Targets that are currently active.
}, force);
* @description The mode that the drag operations will run in 0 for Point, 1 for Intersect, 2 for Strict
* @description In intersect mode, a Drop is targeted by "part" of the drag node being over the Target
* @description Should we only check targets that are in the viewport on drags (for performance), default: true
useHash: true,
activeDrop: null,
validDrops: [],
* @description An object literal of Other Drop Targets that we encountered during this interaction (in the case of overlapping Drop Targets)
otherDrops: {},
targets: [],
* @description Add a Drop Target to the list of Valid Targets. This list get's regenerated on each new drag operation.
* @description Removes a Drop Target from the list of Valid Targets. This list get's regenerated on each new drag operation.
var drops = [];
if (v !== drop) {
if (this._noShim) {
clearCache: function() {
this.validDrops = [];
this.otherDrops = {};
this._activeShims = [];
_activateTargets: function() {
this._noShim = true;
this.clearCache();
v._activateShim([]);
this._noShim = false;
this._handleTargetOver();
* @description This method will gather the area for all potential targets and see which has the hightest covered area and return it.
* @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.
biggest = v;
if (all) {
out = [];
if (v !== biggest) {
return biggest;
* @description This method fires the drop:hit, drag:drophit, drag:dropmiss methods and deactivates the shims..
_deactivateTargets: function() {
if (activeDrop) {
this.activeDrop = null;
v._deactivateShim([]);
_dropMove: function() {
if (this._hasActiveShim()) {
this._handleTargetOver();
_lookup: function() {
return this.validDrops;
var drops = [];
return drops;
_handleTargetOver: function() {
_regTarget: function(t) {
if (v != drop) {
vdrops = [];
if (v !== drop) {
var drop = false,
if (n instanceof Y.Node) {
drop = v;
return drop;
* @description Handles the mousedown DOM event, checks to see if you have a valid handle then starts the drag timers.
Drag = function(o) {
this._lazyAddAttrs = false;
if (!valid) {
* This property defaults to "mousedown", but when drag-gestures is loaded, it is changed to "gesturemovestart"
node: {
dragNode: {
offsetNode: {
value: true
value: false
* @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
lock: {
value: false,
if (lock) {
return lock;
* @description A payload holder to store arbitrary data about this drag object, can be used to store any value.
data: {
value: false
* @description If this is false, the drag element will not move with the cursor: default true. Can be used to "resize" the element.
move: {
value: true
* @description Use the protective shim on all drag operations: default true. Only works with dd-ddm, not dd-ddm-base.
useShim: {
value: true
* @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.
activeHandle: {
value: false
* @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.
value: true
* @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.
dragging: {
value: false
parent: {
value: false
* @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.
target: {
value: false,
return config;
* @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of this Drag instance.
dragMode: {
value: null,
groups: {
getter: function() {
if (!this._groups) {
this._groups = {};
var ret = [];
return ret;
setter: function(g) {
this._groups = {};
Y.each(g, function(v, k) {
this._groups[v] = true;
* @description Array of valid handles to add. Adding something here will set all handles, even if previously added with addHandle
handles: {
value: null,
setter: function(g) {
this._handles = {};
Y.each(g, function(v, k) {
var key = v;
this._handles = null;
* @description Controls the default bubble parent for this Drag instance. Default: Y.DD.DDM. Set to false to disable bubbling. Use bubbleTargets in config
bubbles: {
setter: function(t) {
this.addTarget(t);
haltDown: {
value: true
* @description Add this Drag instance to a group, this should be used for on-the-fly group additions.
addToGroup: function(g) {
this._groups[g] = true;
* @description Remove this Drag instance from a group, this should be used for on-the-fly group removals.
removeFromGroup: function(g) {
delete this._groups[g];
* @description This will be a reference to the Drop instance associated with this drag if the target: true config attribute is set..
target: null,
if (config === false) {
if (this.target) {
this.target = null;
config = {};
config.bubbleTargets = ('bubbleTargets' in config) ? config.bubbleTargets : Y.Object.values(this._yuievt.targets);
_groups: null,
* @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
_createEvents: function() {
queuable: false,
emitFacade: true,
bubbles: true,
queuable: false,
emitFacade: true,
bubbles: true,
queuable: false,
emitFacade: true,
bubbles: true,
queuable: false,
emitFacade: true,
bubbles: true,
var ev = [
this.publish(v, {
type: v,
emitFacade: true,
bubbles: true,
preventable: false,
queuable: false,
_ev_md: null,
* @description The getTime of the mousedown event. Not used, just here in case someone wants/needs to use it.
_startTime: null,
* @description The getTime of the mouseup event. Not used, just here in case someone wants/needs to use it.
_endTime: null,
_handles: null,
_invalids: null,
* @description A private hash of the default invalid selector strings: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true}
_dragThreshMet: null,
_fromTimeout: null,
_clickTimeout: null,
deltaXY: null,
startXY: null,
nodeXY: null,
lastXY: null,
* @description The xy that the node will be set to. Changing this will alter the position as it's dragged.
actXY: null,
realXY: null,
mouseXY: null,
region: null,
this._fixIEMouseUp();
* @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.
_fixDragStart: function(e) {
e.preventDefault();
* @description The function we use as the onselectstart handler when we start a drag in Internet Explorer
_ieSelectFix: function() {
* @description We will hold a copy of the current "onselectstart" method on this property, and reset it after we are done using it.
_ieSelectBack: null,
* @description This method copies the onselectstart listner on the document to the _ieSelectFix property
_fixIEMouseDown: function() {
* @description This method copies the _ieSelectFix property back to the onselectstart listner on the document.
_fixIEMouseUp: function() {
_defMouseDownFn: function(e) {
this._dragThreshMet = false;
this._fixIEMouseDown();
* @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.
hTest = null,
els = null,
nlist = null,
set = false;
if (this._handles) {
nlist = i;
hTest = n;
if (this._invalids) {
if (hTest) {
set = false;
set = true;
_timeoutCheck: function() {
this.start();
* @description Add a handle to a drag element. Drag only initiates when a mousedown happens on this element.
if (!this._handles) {
this._handles = {};
* @description Add a selector string to test the handle against. If the test passes the drag operation will not continue.
this.actXY = [];
this._createEvents();
_prep: function() {
this._dragThreshMet = false;
_unprep: function() {
start: function() {
this.region = {
end: function() {
if (this._clickTimeout) {
this._ev_md = null;
_defEndFn: function(e) {
this._fixIEMouseUp();
* @description Handler for preventing the drag:end event. It will reset the node back to it's start position
_prevEndFn: function(e) {
this._fixIEMouseUp();
_defAlignFn: function(e) {
this._moveNode();
this.region = {
info: {
_defDragFn: function(e) {
if (e.scroll) {
if (!this._dragThreshMet) {
this._dragThreshMet = true;
this.start();
if (this._clickTimeout) {
* @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
stopDrag: function() {
destructor: function() {
this._unprep();
this.detachAll();
if (this.target) {
P = function(config) {
P.ATTRS = {
host: {
moveOnEnd: {
hideOnEnd: {
resizeFrame: {
borderStyle: {
cloneNode: {
value: false
proto = {
_hands: null,
_init: function() {
if (!this._hands) {
this._hands = [];
v.detach();
initializer: function() {
this._init();
destructor: function() {
v.detach();
clone: function() {
c = n.cloneNode(true);
delete c._yuid;
_createFrame: function() {
p.setStyles({
b.prepend(p);
* @description If resizeProxy is set to true (default) it will resize the proxy element to match the size of the Drag Element.
* If positionProxy is set to true (default) it will position the proxy element in the same location as the Drag Element.
if (ah) {
d.setStyles({
d.setStyles({
* 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.
* 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.
proto = null,
C = function(config) {
this._lazyAddAttrs = false;
* @description The Constrained instance will be placed on the Drag instance under the con namespace.
C.ATTRS = {
host: {
stickX: {
value: false
stickY: {
value: false
* @description The X tick offset the drag node should snap to on each drag move. False for no ticks. Default: false
tickX: {
value: false
* @description The Y tick offset the drag node should snap to on each drag move. False for no ticks. Default: false
tickY: {
value: false
tickXArray: {
value: false
tickYArray: {
value: false
* @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)
gutter: {
* '{Region Object}': An Object Literal containing a valid region (top, right, bottom, left) of page positions
constrain: {
if (node) {
return con;
* @description An Object Literal containing a valid region (top, right, bottom, left) of page positions to constrain the drag node to.
setter: function(r) {
setter: function(n) {
setter: function(n) {
cacheRegion: {
value: true
proto = {
_lastTickXFired: null,
_lastTickYFired: null,
initializer: function() {
this._createEvents();
* @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
_createEvents: function() {
var instance = this;
var ev = [
this.publish(v, {
type: v,
emitFacade: true,
bubbles: true,
queuable: false,
_handleEnd: function() {
this._lastTickYFired = null;
this._lastTickXFired = null;
_handleStart: function() {
this.resetCache();
_regionCache: null,
_cacheRegion: function() {
resetCache: function() {
this._regionCache = null;
_getConstraint: function() {
if (con) {
if (!this._regionCache) {
this._cacheRegion();
this.resetCache();
Y.each(g, function(i, n) {
region[n] -= i;
region[n] += i;
return region;
r = this._getConstraint();
if (inc) {
* @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.
r = this.getRegion(),
return _xy;
inside = false;
inside = true;
return inside;
* @description Modifies the Drag.actXY method from the after drag:align event. This is where the constraining happens.
align: function() {
r = this.getRegion(true);
this._tickAlignX();
this._tickAlignY();
return xy;
_tickAlignX: function() {
_tickAlignY: function() {
return pos;
return pos;
if (ticks[i]) {
return ret;
S.ATTRS = {
* @description Internal config option to hold the node that we are scrolling. Should not be set by the developer.
parentScroll: {
value: false,
if (node) {
return node;
buffer: {
scrollDelay: {
host: {
value: null
windowScroll: {
value: false,
vertical: {
value: true,
horizontal: {
value: true,
_scrolling: null,
_vpRegionCache: null,
_dimCache: null,
_scrollTimer: null,
* @description Sets the _vpRegionCache property with an Object containing the dims from the viewport.
_getVPRegion: function() {
top: t + b,
left: l + b
this._vpRegionCache = r;
initializer: function() {
this._vpRegionCache = null;
* @description Check to see if we need to fire the scroll timer. If scroll timer is running this will scroll the window.
var r = this._getVPRegion(),
scroll = false,
w = this._dimCache.w,
h = this._dimCache.h,
scroll = true;
scroll = true;
scroll = true;
scroll = true;
if (move) {
this._cancelScroll();
if (scroll) {
this._initScroll();
this._cancelScroll();
_initScroll: function() {
this._cancelScroll();
this._scrollTimer = Y.Lang.later(this.get('scrollDelay'), this, this._checkWinScroll, [true], true);
_cancelScroll: function() {
this._scrolling = false;
if (this._scrollTimer) {
delete this._scrollTimer;
align: function(e) {
if (this._scrolling) {
this._cancelScroll();
e.preventDefault();
if (!this._scrolling) {
this._checkWinScroll();
_setDimCache: function() {
this._dimCache = {
start: function() {
this._setDimCache();
this._dimCache = null;
this._cancelScroll();
toString: function() {
WS = function() {
windowScroll: {
value: true,
if (scroll) {
return scroll;
initializer: function() {
* @description The Scroll instance will be placed on the Drag instance under the winscroll namespace.
NS = function() {
node: {
value: false,
if (node !== false) {
initializer: function() {
* @description The NodeScroll instance will be placed on the Drag instance under the nodescroll namespace.
Drop = function() {
this._lazyAddAttrs = false;
node: {
groups: {
setter: function(g) {
this._groups = {};
Y.each(g, function(v, k) {
this._groups[v] = true;
padding: {
setter: function(p) {
lock: {
value: false,
if (lock) {
return lock;
* @description Controls the default bubble parent for this Drop instance. Default: Y.DD.DDM. Set to false to disable bubbling. Use bubbleTargets in config.
bubbles: {
setter: function(t) {
this.addTarget(t);
useShim: {
value: true,
setter: function(v) {
* @description Add this Drop instance to a group, this should be used for on-the-fly group additions.
addToGroup: function(g) {
this._groups[g] = true;
* @description Remove this Drop instance from a group, this should be used for on-the-fly group removals.
removeFromGroup: function(g) {
delete this._groups[g];
* @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
_createEvents: function() {
var ev = [
this.publish(v, {
type: v,
emitFacade: true,
preventable: false,
bubbles: true,
queuable: false,
_valid: null,
_groups: null,
shim: null,
* @description A region object associated with this target, used for checking regions while dragging.
region: null,
overTarget: null,
this._valid = false;
var ret = false;
if (this._groups[v]) {
ret = true;
this._valid = true;
return ret;
destructor: function() {
this.shim = null;
this.detachAll();
* @description Removes classes from the target, resets some flags and sets the shims deactive position [-999, -999]
_deactivateShim: function() {
if (!this.shim) {
this.overTarget = false;
_activateShim: function() {
this.overTarget = false;
this.sizeShim();
* @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..
sizeShim: function() {
if (!this.shim) {
this.region = {
_createShim: function() {
if (this.shim) {
s.setStyles({
this.shim = s;
_handleTargetOver: function() {
if (this.overTarget) {
this.overTarget = true;
this._handleOut();
_handleOverEvent: function() {
_handleOutEvent: function() {
if (this.overTarget) {
this.overTarget = false;
if (!force) {
* Provides the ability to drag multiple nodes under a container element using only one Y.DD.Drag instance as a delegate.
* Provides the ability to drag multiple nodes under a container element using only one Y.DD.Drag instance as a delegate.
var Delegate = function(o) {
dd: null,
_shimState: null,
_handles: null,
_onNodeChange: function(e) {
_afterDragEnd: function(e) {
_delMouseDown: function(e) {
_onMouseEnter: function(e) {
_onMouseLeave: function(e) {
this._handles = [];
this._handles.push(Y.delegate(Y.DD.Drag.START_EVENT, Y.bind(this._delMouseDown, this), cont, this.get(NODES)));
syncTargets: function() {
var config = {
useShim: false,
bubbleTargets: this
return node;
destructor: function() {
if (this.dd) {
v.detach();
ATTRS: {
* @description A selector query to get the container to listen for mousedown events on. All "nodes" should be a child of this container.
container: {
* @description A selector query to get the children of the "container" to make draggable elements from.
nodes: {
invalid: {
lastNode: {
currentNode: {
dragNode: {
over: {
value: false
target: {
value: false
dragConfig: {
value: null
handles: {
value: null
_delegates: [],
var del = null;
del = v;
return del;
}, '@VERSION@' ,{requires:['dd-drag', 'event-mouseenter'], skinnable:false, optional:['dd-drop-plugin']});