"use strict";
/**
* Provides `drag and drop` functionality, without dropzones.
* For `dropzone`-support, you should use the module: `drag-drop`.
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* DD = require('drag')(window);
* DD.init();
*
* @module drag
* @class DD
* @since 0.0.4
*/
var NAME = '[drag]: ',
createHashMap = require('js-ext/extra/hashmap.js').createMap,
DRAG = 'drag',
DROP = 'drop',
DRAGGABLE = DRAG+'gable',
DEL_DRAGGABLE = 'del-'+DRAGGABLE,
DD_MINUS = 'dd-',
DD_DRAGGING_CLASS = DD_MINUS+DRAG+'ging',
DD_MASTER_CLASS = DD_MINUS+'master',
DD_HANDLE = DD_MINUS+'handle',
DD_DROPZONE_MOVABLE = DD_MINUS+'dropzone-movable',
CONSTRAIN_ATTR = 'constrain-selector',
MOUSE = 'mouse',
DROPZONE = 'dropzone',
NO_TRANS_CLASS = 'el-notrans', // delivered by `vdom`
HIGH_Z_CLASS = DD_MINUS+'high-z',
REGEXP_NODE_ID = /^#\S+$/,
EMITTER = 'emitter',
DD_EMITTER = DD_MINUS+EMITTER,
DD_DRAG = DD_MINUS+DRAG,
DD_DROP = DD_MINUS+DROP,
DD_FAKE = DD_MINUS+'fake-',
DOWN = 'down',
UP = 'up',
MOVE = 'move',
MOUSEUP = MOUSE+UP,
MOUSEDOWN = MOUSE+DOWN,
MOUSEMOVE = MOUSE+MOVE,
PAN = 'pan',
PANSTART = PAN+'start',
PANMOVE = PAN+MOVE,
PANEND = PAN+'end',
DD_FAKE_MOUSEUP = DD_FAKE+MOUSEUP,
UI = 'UI',
DD_EFFECT_ALLOWED = DD_MINUS+'effect-allowed',
BORDER = 'border',
WIDTH = 'width',
BORDER_LEFT_WIDTH = BORDER+'-left-'+WIDTH,
BORDER_TOP_WIDTH = BORDER+'-top-'+WIDTH,
LEFT = 'left',
TOP = 'top',
WINDOW = 'window',
TRUE = 'true',
NO_OVERFLOW = 'itsa-no-overflow',
DD_MINUSDRAGGABLE = DD_MINUS+DRAGGABLE,
DIRECTION_ATTR = DD_MINUS+'direction',
PLUGINTRUE = '[plugin-dd="true"]',
PLUGIN_ATTRS = [DD_MINUS+DROPZONE, CONSTRAIN_ATTR, DD_EMITTER, DD_HANDLE, DD_EFFECT_ALLOWED, DD_DROPZONE_MOVABLE, DIRECTION_ATTR];
require('polyfill');
require('js-ext');
require('./css/drag.css');
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.Drag) {
return window._ITSAmodules.Drag; // Drag was already created
}
var Event = require('event-dom')(window),
isMobile = require('useragent')(window).isMobile,
DOCUMENT = window.document,
bodyNode = DOCUMENT.body,
supportHammer = !!Event.Hammer,
mobileEvents = supportHammer && isMobile,
DD, noScrollOnDrag;
require('vdom')(window);
require('node-plugin')(window);
require('window-ext')(window);
noScrollOnDrag = function(e) {
if (e.target.matches(PLUGINTRUE) || e.target.inside(PLUGINTRUE)) {
e.preventDefault();
}
};
DD = {
/**
* Objecthash containing all specific information about the particular drag-cycle.
* It has a structure like this:
*
* ddProps = {
* dragNode {HtmlElement} Element that is dragged
* x {Number} absolute x-position of the draggable inside `document` when the drag starts
* y {Number} absolute y-position of the draggable inside `document` when the drag starts
* inlineLeft {String} inline css of the property `left` when drag starts
* inlineTop {String} inline css of the property `top` when drag starts
* winConstrained {Boolean} whether the draggable should be constrained to `window`
* xMouseLast {Number} absolute x-position of the mouse inside `document` when the drag starts
* yMouseLast {Number} absolute y-position of the draggable inside `document` when the drag starts
* winScrollLeft {Number} the left-scroll of window when drag starts
* winScrollTop {Number} the top-scroll of window when drag starts
* constrain = { // constrain-properties when constrained to a HtmlElement
* xOrig {Number} x-position in the document, included with left-border-width
* yOrig {Number} y-position in the document, included with top-border-width
* x {Number} xOrig corrected with scroll-left of the constrained node
* y {Number} yOrig corrected with scroll-top of the constrained node
* w {Number} scrollWidth
* h {Number} scrollHeight
* };
* relatives[{ // Array with objects that represent all draggables that come along with the master-draggable (in case of multiple items), excluded the master draggable itself
* sourceNode {HtmlElement} original node (defined by drag-drop)
* dragNode {HtmlElement} draggable node
* shiftX {Number} the amount of left-pixels that this HtmlElement differs from the dragged element
* shiftY {Number} the amount of top-pixels that this HtmlElement differs from the dragged element
* inlineLeft {String} inline css of the property `left` when drag starts
* inlineTop {String} inline css of the property `top` when drag starts
* }]
* }
*
* @property ddProps
* @default {}
* @type Object
* @since 0.0.1
*/
ddProps: {},
/**
* Internal hash with notifiers to response after each `Drag` event is set up, or teared down.
* You can use this to hook in into the drag-eventcycle: the `drop`-module uses it this way.
* Is filled by using `notify()`.
*
* @property _notifiers
* @default []
* @type Array
* @private
* @since 0.0.1
*/
_notifiers: [],
/**
* Default function for the `*:dd-drag`-event
*
* @method _defFnDrag
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_defFnDrag: function(e) {
console.log(NAME, '_defFnDrag: default function dd-drag');
var ddProps = this.ddProps,
dragNode = ddProps.dragNode,
constrainNode = ddProps.constrainNode,
winConstrained = ddProps.winConstrained,
x, y;
// is the drag is finished, there will be no ddProps.defined
// return then, to prevent any events that stayed behind
if (!ddProps.defined || !dragNode) {
return;
}
// caution: the user might have put the mouse out of the screen and released the mousebutton!
// If that is the case, the a mouseup-event should be initiated instead of draggin the element
if (e.buttons===0) {
// no more button pressed
/**
* Fired when the mouse comes back into the browser-window while dd-drag was busy yet no buttons are pressed.
* This is a correction to the fact that the mouseup-event wasn't noticed because the mouse was outside the browser.
*
* @event dd-fake-mouseup
* @private
* @since 0.1
*/
Event.emit(dragNode, DD_FAKE_MOUSEUP);
}
else {
console.log(NAME, '_defFnDrag: dragging:');
if (constrainNode) {
ddProps.constrain.x = ddProps.constrain.xOrig - constrainNode.scrollLeft;
ddProps.constrain.y = ddProps.constrain.yOrig - constrainNode.scrollTop;
}
if (ddProps.xMovable) {
x = ddProps.x+e.xMouse+(winConstrained ? ddProps.winScrollLeft : window.getScrollLeft())-e.xMouseOrigin;
}
if (ddProps.yMovable) {
y = ddProps.y+e.yMouse+(winConstrained ? ddProps.winScrollTop : window.getScrollTop())-e.yMouseOrigin;
}
dragNode.setXY(x, y, ddProps.constrain, true);
ddProps.relatives && ddProps.relatives.forEach(
function(item) {
item.dragNode.setXY(x+item.shiftX, y+item.shiftY, null, true);
}
);
ddProps.winConstrained || dragNode.forceIntoView(true);
constrainNode && dragNode.forceIntoNodeView(constrainNode);
}
},
/**
* Default function for the `*:dd-drop`-event
*
* @method _defFnDrop
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_defFnDrop: function(e) {
console.log(NAME, '_defFnDrop');
var dragNode = e.target,
removeClasses = function (node) {
node.removeClass([NO_TRANS_CLASS, HIGH_Z_CLASS, DD_DRAGGING_CLASS, DEL_DRAGGABLE, DD_MASTER_CLASS]);
};
PLUGIN_ATTRS.forEach(function(attribute) {
var data = '_del_'+attribute;
if (dragNode.getData(data)) {
dragNode.getPlugin('dd').then(function(plugin) {
delete plugin.model[attribute];
});
dragNode.removeData(data);
}
});
removeClasses(dragNode);
e.relatives && e.relatives.forEach(
function(node) {
removeClasses(node);
}
);
},
/**
* Default function for the `*:dd`-event
*
* @method _defFnStart
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_defFnStart: function(e) {
var instance = this,
customEvent;
customEvent = e.emitter + ':'+DD_DRAG;
console.log(NAME, '_defFnStart: default function UI:dd-start. Defining customEvent '+customEvent);
Event.defineEvent(customEvent).defaultFn(instance._defFnDrag.bind(instance));
DOCUMENT.getAll('.'+DD_MASTER_CLASS).removeClass(DD_MASTER_CLASS);
instance._initializeDrag(e);
},
/**
* Defines the definition of the `dd` event: the first phase of the drag-eventcycle (dd, *:dd-drag, *:dd-drop)
*
* @method _defineDDStart
* @param emitterName {String} the emitterName, which leads into the definition of event `emitterName:dd`
* @private
* @since 0.0.1
*/
_defineDDStart: function(emitterName) {
console.log(NAME, '_defineDDStart');
var instance = this;
// by using dd before dd-drag, the user can create a `before`-subscriber to dd
// and define e.emitter and/or e.relatives before going into `dd-drag`
Event.defineEvent(emitterName+':dd')
.defaultFn(instance._defFnStart.bind(instance))
.preventedFn(instance._prevFnStart.bind(instance));
},
/**
* Default function for the `*:dd-drag`-event
*
* @method _initializeDrag
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_initializeDrag: function(e) {
console.log(NAME, '_initializeDrag '+e.xMouseOrigin);
var instance = this,
dragNode = e.target,
constrain = dragNode.getAttr(CONSTRAIN_ATTR),
direction = dragNode.getAttr(DIRECTION_ATTR).toLowerCase(),
ddProps = instance.ddProps,
emitterName = e.emitter,
moveEv, x, y, byExactId, match, constrainNode, winConstrained, winScrollLeft, winScrollTop,
xOrig, yOrig;
// define ddProps --> internal object with data about the draggable instance
ddProps.dragNode = dragNode;
ddProps.xMovable = (direction==='xy') || (direction==='x');
ddProps.yMovable = (direction==='xy') || (direction==='y');
ddProps.x = x = dragNode.left;
ddProps.y = y = dragNode.top;
ddProps.inlineLeft = dragNode.getInlineStyle(LEFT);
ddProps.inlineTop = dragNode.getInlineStyle(TOP);
ddProps.winConstrained = winConstrained = (constrain===WINDOW);
ddProps.xMouseLast = x;
ddProps.yMouseLast = y;
if (constrain) {
if (winConstrained) {
ddProps.winScrollLeft = winScrollLeft = window.getScrollLeft();
ddProps.winScrollTop = winScrollTop = window.getScrollTop();
ddProps.constrain = {
x: winScrollLeft,
y: winScrollTop,
w: window.getWidth(),
h: window.getHeight()
};
// if constrained to window:
// set a class that makes overflow hidden --> this will prevent
// some browsers from scrolling the window when a pressed mouse
// gets out of the window
bodyNode.setClass(NO_OVERFLOW);
}
else {
byExactId = REGEXP_NODE_ID.test(constrain);
constrainNode = dragNode.parentNode;
while (constrainNode.matchesSelector && !match) {
match = byExactId ? (constrainNode.id===constrain.substr(1)) : constrainNode.matchesSelector(constrain);
// if there is a match, then make sure x and y fall within the region
if (match) {
ddProps.constrainNode = constrainNode;
xOrig = constrainNode.left + parseInt(constrainNode.getStyle(BORDER_LEFT_WIDTH), 10);
yOrig = constrainNode.top + parseInt(constrainNode.getStyle(BORDER_TOP_WIDTH), 10);
ddProps.constrain = {
xOrig: xOrig,
yOrig: yOrig,
x: xOrig - constrainNode.scrollLeft,
y: yOrig - constrainNode.scrollTop,
w: constrainNode.scrollWidth,
h: constrainNode.scrollHeight
};
}
else {
constrainNode = constrainNode.parentNode;
}
}
}
}
// create listener for `mousemove` and transform it into the `*:dd:drag`-event
moveEv = Event.after(mobileEvents ? PANMOVE : MOUSEMOVE, function(e2) {
if (typeof e2.center==='object') {
e2.clientX = e2.center.x;
e2.clientY = e2.center.y;
}
if (instance.ddProps.isEmpty() || !e2.clientX) {
return;
}
// move the object
e.xMouse = e2.clientX;
e.yMouse = e2.clientY;
/**
* Emitted during the drag-cycle of a draggable Element (while it is dragged).
*
* @event *:dd-drag
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement where the mouse lies upon
* @param e.dd {Promise} Promise that gets fulfilled when dragging is ended. The fullfilled-callback has no arguments.
* @param e.xMouse {Number} the current x-position in the window-view
* @param e.yMouse {Number} the current y-position in the window-view
* @param e.clientX {Number} the current x-position in the window-view
* @param e.clientY {Number} the current y-position in the window-view
* @param e.xMouseOrigin {Number} the original x-position in the document when drag started (incl. scrollOffset)
* @param e.yMouseOrigin {Number} the original y-position in the document when drag started (incl. scrollOffset)
* @param [e.relatives] {NodeList} an optional list that the user could set in a `before`-subscriber of the `dd`-event
* to inform which nodes are related to the draggable node and should be dragged as well.
* @since 0.1
*/
Event.emit(dragNode, emitterName+':'+DD_DRAG, e);
e.dd.callback();
});
// prepare dragNode class for the right CSS:
dragNode.setClass([NO_TRANS_CLASS, HIGH_Z_CLASS, DD_DRAGGING_CLASS]);
Event.onceAfter([mobileEvents ? PANEND : MOUSEUP, DD_FAKE_MOUSEUP], function(e3) {
moveEv.detach();
// set mousepos for the last time:
if (typeof e3.center==='object') {
e3.clientX = e3.center.x;
e3.clientY = e3.center.y;
}
e.xMouse = e3.clientX;
e.yMouse = e3.clientY;
// invoke all teardown notifiers:
instance._notifiers.forEach(
function(notifier) {
notifier.s || notifier.cb.call(notifier.o, e, ddProps);
}
);
if (constrain && ddProps.winConstrained) {
// if constrained to window:
// remove overflow=hidden from the bodynode
bodyNode.removeClass(NO_OVERFLOW);
}
instance.ddProps = {};
/**
* Emitted when drag-cycle of a draggable Element is ended.
*
* @event *:dd-drop
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement where the mouse lies upon
* @param e.dd {Promise} Promise that gets fulfilled when dragging is ended. The fullfilled-callback has no arguments.
* @param e.xMouse {Number} the current x-position in the window-view
* @param e.yMouse {Number} the current y-position in the window-view
* @param e.clientX {Number} the current x-position in the window-view
* @param e.clientY {Number} the current y-position in the window-view
* @param e.xMouseOrigin {Number} the original x-position in the document when drag started (incl. scrollOffset)
* @param e.yMouseOrigin {Number} the original y-position in the document when drag started (incl. scrollOffset)
* @param [e.relatives] {NodeList} an optional list that the user could set in a `before`-subscriber of the `dd`-event
* to inform which nodes are related to the draggable node and should be dragged as well.
* @since 0.1
*/
Event.emit(dragNode, emitterName+':'+DD_DROP, e);
e.dd.fulfill();
});
dragNode.setXY(ddProps.xMouseLast, ddProps.yMouseLast, ddProps.constrain, true);
if (e.relatives) {
// relatives are extra HtmlElements that should be moved aside with the main dragged element
// e.relatives is a selector, e.relativeNodes will be an array with nodes
e.relativeNodes = [];
dragNode.setClass(DD_MASTER_CLASS);
dragNode.setClass(DD_MASTER_CLASS);
ddProps.relatives = [];
e.relatives.forEach(
function(node) {
var item;
if (node !== dragNode) {
item = {
dragNode: node,
shiftX: node.left - x,
shiftY: node.top - y,
inlineLeft: node.getInlineStyle(LEFT),
inlineTop: node.getInlineStyle(TOP)
};
item.dragNode.setClass([NO_TRANS_CLASS, HIGH_Z_CLASS, DD_DRAGGING_CLASS]);
ddProps.relatives.push(item);
e.relativeNodes.push(item.dragNode);
}
}
);
}
// invoke all setup notifiers:
instance._notifiers.forEach(
function(notifier) {
notifier.s && notifier.cb.call(notifier.o, e, ddProps);
}
);
},
/**
* Prevented function for the `*:dd-start`-event
*
* @method _prevFnStart
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_prevFnStart: function(e) {
console.log(NAME, '_prevFnStart');
e.dd.reject();
},
/**
* Engine behind the drag-drop-cycle.
* Sets up a `mousedown` listener to initiate a drag-drop eventcycle. The eventcycle start whenever
* one of these events happens on a HtmlElement with the attribute `dd-draggable="true"`.
* The drag-drop eventcycle consists of the events: `dd-start`, `emitterName:dd-drag` and `emitterName:dd-drop`
*
*
* @method _setupMouseEv
* @private
* @since 0.0.1
*/
_setupMouseEv: function() {
console.log(NAME, '_setupMouseEv: setting up mousedown event');
var instance = this,
nodeTargetFn,
delegatedTargetFn;
nodeTargetFn = function(e) {
var node = e.target,
handle, availableHandles, insideHandle, emitterName;
// first check if there is a handle to determine if the drag started here:
handle = node.getAttr(DD_HANDLE);
if (handle) {
availableHandles = node.getAll(handle);
insideHandle = false;
availableHandles.some(function(handleNode) {
insideHandle = handleNode.contains(e.sourceTarget);
return insideHandle;
});
if (!insideHandle) {
return;
}
}
// initialize ddProps: have to do here, because the event might not start because it wasn't inside the handle when it should be
instance.ddProps = {
defined: true,
dragOverList: []
};
// prevent the emitter from resetting e.target to e.sourceTarget:
e._noResetSourceTarget = true;
// add `dd`-Promise to the eventobject --> this Promise will be resolved once the pointer has released.
e.dd = Promise.manage();
e.dd.catch(function(err) {
console.info(NAME+'dd rejected: '+err);
});
// define e.setOnDrag --> users
e.setOnDrag = function(callbackFn) {
e.dd.setCallback(callbackFn);
};
// store the orriginal mouseposition:
e.xMouseOrigin = e.clientX + window.getScrollLeft();
e.yMouseOrigin = e.clientY + window.getScrollTop();
//set the emitterName:
emitterName = e.target.getAttr(DD_EMITTER) || UI;
// now we can start the eventcycle by emitting emitterName:dd:
/**
* Emitted when a draggable Element's drag-cycle starts. You can use a `before`-subscriber to specify
* e.relatives, which should be a nodelist with HtmlElements, that should be dragged togehter with the master
* draggable Element.
*
* @event *:dd
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement where the mouse lies upon
* @param e.dd {Promise} Promise that gets fulfilled when dragging is ended. The fullfilled-callback has no arguments.
* @param e.xMouse {Number} the current x-position in the window-view
* @param e.yMouse {Number} the current y-position in the window-view
* @param e.clientX {Number} the current x-position in the window-view
* @param e.clientY {Number} the current y-position in the window-view
* @param e.xMouseOrigin {Number} the original x-position in the document when drag started (incl. scrollOffset)
* @param e.yMouseOrigin {Number} the original y-position in the document when drag started (incl. scrollOffset)
* @param [e.relatives] {NodeList} an optional list that the user could set in a `before`-subscriber of the `dd`-event
* to inform which nodes are related to the draggable node and should be dragged as well.
* @since 0.1
*/
instance._defineDDStart(emitterName);
Event.emit(e.target, emitterName+':dd', e);
};
delegatedTargetFn = function(e, cssSelector) {
var container = e.target,
nodelist = container.getAll(cssSelector),
foundNode;
nodelist.some(
function(node) {
(node.contains(e.sourceTarget)) && (foundNode=node);
return foundNode;
}
);
if (foundNode) {
// e.currentTarget = container;
e.target = foundNode;
// Mark the delegated node, so it has the same style as [draggable]:
foundNode.setClass(DEL_DRAGGABLE);
// We must transport the other relevant dd-attributes (and constrain-selector)
// which we will remove when finished dragging:
PLUGIN_ATTRS.forEach(function(attribute) {
var attr = container.getAttr(attribute);
if (attr && !foundNode.hasAttr(attribute)) {
foundNode.setData('_del_'+attribute, attr);
foundNode.setAttr(attribute, attr);
}
});
nodeTargetFn(e);
}
};
Event.after(mobileEvents ? PANSTART : MOUSEDOWN, function(e) {
var draggableAttr = e.target.getAttr(DD_MINUSDRAGGABLE);
if (typeof e.center==='object') {
e.clientX = e.center.x;
e.clientY = e.center.y;
}
(draggableAttr===TRUE) ? nodeTargetFn(e) : delegatedTargetFn(e, draggableAttr);
}, '['+DD_MINUSDRAGGABLE+']');
// prevent default behaviour on scrolling: otherwise mobile devices will scroll instead of drag:
// scrollPreventListener = Event.before('panstart', function(e) {e.preventDefaultContinue();});
// scrollPreventListener = Event.before('touchmove', function(e) {e.preventDefault();});
if (mobileEvents) {
DOCUMENT.addEventListener('touchstart', noScrollOnDrag);
DOCUMENT.addEventListener('touchmove', noScrollOnDrag);
}
},
/**
* Initializes dragdrop. Needs to be invoked, otherwise DD won't run.
*
* @method init
* @param dragableElement {HtmlElement} HtmlElement that is checked for its allowed effects
* @return {Boolean} if copy-dragables are allowed
* @since 0.0.1
*/
init: function() {
console.log(NAME, 'init');
var instance = this;
if (!instance._inited) {
instance._setupMouseEv(); // engine behind the dragdrop-eventcycle
Event.defineEvent('UI:'+DD_DROP)
.defaultFn(instance._defFnDrop.rbind(instance));
}
instance._inited = true;
},
/**
* Creates a notifier to response after each `Drag` event is set up, or teared down.
* You can use this to hook in into the drag-eventcycle: the `drop`-module uses it this way.
*
* @static
* @method notify
* @param callback {Function} subscriber: will be invoked after every drag-event is set up.
* Recieves 2 arguments: the `eventobject` and the internal property: `ddProps`
* @param context {Object} context of the callback
* @param setup {Boolean} wheter the callback should be invoked on setup (true) or teardown (false)
* @return {Object} handle with a method `detach()` which you can use to remove it from the `notifier-hash`
* @since 0.0.1
*/
notify: function(callback, context, setup) {
console.log(NAME, 'notify');
var notifier = {
cb: callback,
o: context,
s: setup
};
this._notifiers.push(notifier);
return {
detach: function() {
this._notifiers.remove(notifier);
}
};
}
};
// don't drag when the cursor is above an input, text, or editable element:
Event.before(
'*:'+DD_DRAG,
function(e) {
e.preventDefault();
},
function(e) {
var sourceNode= e.sourceTarget,
tagName = sourceNode.getTagName();
return (tagName==='INPUT') || (tagName==='TEXTAREA') || (sourceNode.getAttr('contenteditable')==='true');
}
);
// don't drag any native drag-drop items when they are part of dd, because they prevent they corrupt dragging:
Event.before('dragstart',
function (e) {
e.preventDefault();
},
function(e) {
return e.target.matches(PLUGINTRUE) || e.target.inside(PLUGINTRUE);
}
);
DOCUMENT.definePlugin('dd', null, {
attrs: {
draggable: 'string',
handle: 'string',
direction: 'string',
emitter: 'string'
},
defaults: {
draggable: 'true',
direction: 'xy'
}
});
window._ITSAmodules.Drag = DD;
return DD;
};