"use strict";
/**
* Provides `drag and drop` functionality with dropzones
*
*
* <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
* New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
*
* @example
* DD = require('drag-drop')(window);
* DD.init();
*
* @module drag-drop
* @class DD
* @since 0.0.4
*/
// Redefine the API for the events `dd`, `dd-drag` and `dd-drop`, for they have more properties:
/**
* Emitted during the drag-cycle of a draggable Element (while it is dragged).
*
* @event *:dd-drag (extended by drag-drop)
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.dragNode {HtmlElement} The HtmlElement that is being dragged (equals e.target)
* @param [e.sourceNode] {HtmlElement} The original Element. Only when a `copy` is made --> e.dragNode is being moved while
* e.sourceNode stand at its place.
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement where the mouse lies upon
* @param [e.dropTarget] {HtmlElement} The dropzone HtmlElement that will be available whenever the draggable gets over a dropzone.
* @param e.dd {Promise} Promise that gets fulfilled when dragging is ended. The fullfilled-callback has no arguments.
* @param [e.dropzone] {Promise} a Promise that will be available whenever the draggable gets over a dropzone.
* The Promise that gets fulfilled as soon as the draggable is dropped, or outside the dropzone
* Will fulfill with one argument: `onDropzone` {Boolean} when `true`, the draggable is dropped inside the dropzone, otherwise
* the draggable got outside the dropzone without beging dropped.
* @param e.ctrlKey {Boolean} Whether the Ctrl/cmd key is pressed
* @param e.isCopied {Boolean} Whether the drag is a copy-drag
* @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.
* @param [e.relativeDragNodes] {NodeList} an optional list that holds the HtmlElements that corresponds with
* the `e.relative` list, but is a list with draggable Elements.
* @since 0.0.1
*/
/**
* Emitted when drag-cycle of a draggable Element is ended.
*
* @event *:dd-drop (extended by drag-drop)
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.dragNode {HtmlElement} The HtmlElement that is being dragged (equals e.target)
* @param [e.sourceNode] {HtmlElement} The original Element. Only when a `copy` is made --> e.dragNode is being moved while
* e.sourceNode stand at its place.
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement where the mouse lies upon
* @param [e.dropTarget] {HtmlElement} The dropzone HtmlElement that will be available whenever the draggable gets over a dropzone.
* @param e.dd {Promise} Promise that gets fulfilled when dragging is ended. The fullfilled-callback has no arguments.
* @param [e.dropzone] {Promise} a Promise that will be available whenever the draggable gets over a dropzone.
* The Promise that gets fulfilled as soon as the draggable is dropped, or outside the dropzone
* Will fulfill with one argument: `onDropzone` {Boolean} when `true`, the draggable is dropped inside the dropzone, otherwise
* the draggable got outside the dropzone without beging dropped.
* @param e.ctrlKey {Boolean} Whether the Ctrl/cmd key is pressed
* @param e.isCopied {Boolean} Whether the drag is a copy-drag
* @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.
* @param [e.relativeDragNodes] {NodeList} an optional list that holds the HtmlElements that corresponds with
* the `e.relative` list, but is a list with draggable Elements.
* @since 0.0.1
*/
/**
* 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 (extended by drag-drop)
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the HtmlElement that is being dragged
* @param e.dragNode {HtmlElement} The HtmlElement that is being dragged (equals e.target)
* @param [e.sourceNode] {HtmlElement} The original Element. Only when a `copy` is made --> e.dragNode is being moved while
* e.sourceNode stand at its place.
* @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.ctrlKey {Boolean} Whether the Ctrl/cmd key is pressed
* @param e.isCopied {Boolean} Whether the drag is a copy-drag
* @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.
* @param [e.relativeDragNodes] {NodeList} an optional list that holds the HtmlElements that corresponds with
* the `e.relative` list, but is a list with draggable Elements.
* @since 0.0.1
*/
/**
* Objecthash containing all specific information about the particular drag-cycle.
* It has a structure like this:
*
* ddProps = {
* sourceNode {HtmlElement} original node (defined by drag-drop)
* 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
* };
* dropzoneSpecified {Boolean} whether the draggable has a dropzone specified (either by `dd-dropzone` or by `dd-emitter`) (defined by drag-drop)
* dragOverEv {Object} Eventhandler that watches for `mousemove` to detect dropzone-over events (defined by drag-drop)
* 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
* }]
* relativeDragNodes [HtmlElements] Array with all `copyied` Nodes corresponding to `ddProps.relatives`. Only in case of copying multiple items (defined by drag-drop)
* }
*
* @property ddProps (extended by drag-drop)
* @default {}
* @type Object
* @since 0.0.1
*/
var DRAG = 'drag',
DROP = 'drop',
NAME = '['+DRAG+'-'+DROP+']: ',
createHashMap = require('js-ext/extra/hashmap.js').createMap,
COPY = 'copy',
DROPZONE = DROP+'zone',
SOURCE = 'source',
DRAGGABLE = DRAG+'gable',
DEL_DRAGGABLE = 'del-'+DRAGGABLE,
DD_MINUS = 'dd-',
DZ_MINUS = 'dz-',
PLUGIN_DD = 'plugin-dd',
DZ_DROPZONE = DZ_MINUS+DROPZONE,
DD_DRAGGING_CLASS = DD_MINUS+DRAG+'ging',
DD_MASTER_CLASS = DD_MINUS+'master',
DD_HANDLE = DD_MINUS+'handle',
DD_SOURCE_ISCOPIED_CLASS = DD_MINUS+COPY+SOURCE,
DD_COPIED_CLASS = DD_MINUS+COPY,
DROPZONE_MOVABLE = DROPZONE+'-movable',
DD_DROPZONE_MOVABLE = DD_MINUS+DROPZONE_MOVABLE,
CONSTRAIN_ATTR = 'constrain-selector',
MOUSE = 'mouse',
DROPZONE_OVER = DROPZONE+'-over',
DROPZONE_DROP = DROPZONE+'-'+DROP,
DD_DROPZONE = DD_MINUS+DROPZONE,
NO_TRANS_CLASS = 'el-notrans', // delivered by `vdom`
DD_HIDDEN_SOURCE_CLASS = DD_MINUS+'hidden-'+SOURCE,
INVISIBLE_CLASS = 'el-invisible', // delivered by `vdom`
DD_TRANSITION_CLASS = DD_MINUS+'transition',
DD_OPACITY_CLASS = DD_MINUS+'opacity',
HIGH_Z_CLASS = DD_MINUS+'high-z',
DD_DROPACTIVE_CLASS = DROPZONE+'-awake',
DD_ABOVE_DROPZONE_CLASS = DD_MINUS+'above'+DROPZONE,
REGEXP_MOVE = /\bmove\b/i,
REGEXP_COPY = /\bcopy\b/i,
REGEXP_ALL = /\b(all|true)\b/i,
EMITTER = 'emitter',
REGEXP_EMITTER = /\bemitter=((\w|,)+)\b/,
DD_EMITTER = DD_MINUS+EMITTER,
MOVE = 'move',
DROPZONE_OUT = DROPZONE+'-out',
DD_DROP = DD_MINUS+DROP,
DD_FAKE = DD_MINUS+'fake-',
DOWN = 'down',
UP = 'up',
KEY = 'key',
MOUSEMOVE = MOUSE+MOVE,
PANMOVE = 'pan'+MOVE,
DD_FAKE_MOUSEMOVE = DD_FAKE+MOUSEMOVE,
UI = 'UI',
DROPZONE_BRACKETS = '[' + DZ_DROPZONE + ']',
EFFECT_ALLOWED = 'effect-allowed',
DD_EFFECT_ALLOWED = DD_MINUS+EFFECT_ALLOWED,
BORDER = 'border',
WIDTH = 'width',
BORDER_LEFT_WIDTH = BORDER+'-left-'+WIDTH,
BORDER_RIGHT_WIDTH = BORDER+'-right-'+WIDTH,
BORDER_TOP_WIDTH = BORDER+'-top-'+WIDTH,
BORDER_BOTTOM_WIDTH = BORDER+'-bottom-'+WIDTH,
LEFT = 'left',
TOP = 'top',
POSITION = 'position',
ABSOLUTE = 'absolute',
TRUE = 'true',
DD_MINUSDRAGGABLE = DD_MINUS+DRAGGABLE,
PLUGIN_ATTRS = [PLUGIN_DD, DD_DROPZONE, CONSTRAIN_ATTR, DD_EMITTER, DD_HANDLE, DD_EFFECT_ALLOWED, DD_DROPZONE_MOVABLE];
require('polyfill/polyfill-base.js');
require('js-ext');
require('./css/drag-drop.css');
module.exports = function (window) {
window._ITSAmodules || Object.protectedProp(window, '_ITSAmodules', createHashMap());
if (window._ITSAmodules.DragDrop) {
return window._ITSAmodules.DragDrop; // DragDrop was already created
}
var Event = require('event-dom')(window),
DragModule = require('drag')(window),
$superInit = DragModule.init,
ctrlPressed = false,
dropEffect = MOVE,
DOCUMENT = window.document,
isMobile = require('useragent')(window).isMobile,
supportHammer = !!Event.Hammer,
mobileEvents = supportHammer && isMobile,
DD;
require('vdom')(window);
require('node-plugin')(window);
require('window-ext')(window);
DD = {
/**
* Returns the allowed effects on the dragable-HtmlElement. Is determined by the attribute `dd-effect-allowed`
* Will be set to "move" when undefined.
*
* @method _allowedEffects
* @param dragableElement {HtmlElement} HtmlElement that is checked for its allowed effects
* @return {String} allowed effects: "move", "copy" or "all"
* @private
* @since 0.0.1
*/
_allowedEffects: function(dragableElement) {
console.log(NAME, '_allowedEffects');
var allowedEffects = dragableElement.getAttr(DD_EFFECT_ALLOWED);
return allowedEffects || MOVE;
},
/**
* Default function for the `*:dd-drop`-event. Overrides the definition of the `drag`-module.
*
* @method _defFnDrop (extended by drag-drop)
* @param e {Object} eventobject
* @param sourceNode {HtmlElement} the original HtmlElement
* @param dragNode {HtmlElement} the dragged HtmlElement (either original or clone)
* @param dropzoneSpecified {Boolean} whether the sourceNode had a dropzone specified
* @param relatives {Array} hash with all draggables that are being move togerther with the master draggable
* @private
* @since 0.0.1
*/
_defFnDrop: function(e, ddProps) {
console.log(NAME, '_defFnDrop: default function dd-drop. dropzoneSpecified: '+ddProps.dropzoneSpecified);
var instance = this,
sourceNode = ddProps.sourceNode,
dragNode = ddProps.dragNode,
dropzoneSpecified = ddProps.dropzoneSpecified,
relatives = ddProps.relatives,
willBeCopied,
removeClasses = function (node) {
node.removeClass([NO_TRANS_CLASS, HIGH_Z_CLASS, DD_DRAGGING_CLASS, DEL_DRAGGABLE, DD_MASTER_CLASS, DD_SOURCE_ISCOPIED_CLASS]);
};
willBeCopied = (e.dropTarget && ((ctrlPressed && instance.allowCopy(dragNode)) || instance.onlyCopy(dragNode)));
willBeCopied || (e.relativeDragNodes=null);
e.isCopied = willBeCopied;
// handle drop
if (dropzoneSpecified) {
instance._handleDrop(e, sourceNode, dragNode, relatives);
}
else {
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);
ddProps.relatives && ddProps.relatives.forEach(
function(item) {
removeClasses(item.dragNode);
}
);
}
instance.restoreDraggables = function() {/* NOOP */ return this;};
},
/**
* Default function for the `*:dropzone`-event
*
* @method _defFnOver
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_defFnOver: function(e) {
console.log(NAME, '_defFnOver: default function dropzone');
var dropzone = e.target;
dropzone.setClass(DD_DROPACTIVE_CLASS);
e.sourceNode.setClass(DD_ABOVE_DROPZONE_CLASS);
e.dragNode.setClass(DD_ABOVE_DROPZONE_CLASS);
e.dropzone.then(
function(insideDropTarget) {
dropzone.removeClass(DD_DROPACTIVE_CLASS);
e.sourceNode.removeClass(DD_ABOVE_DROPZONE_CLASS);
e.dragNode.removeClass(DD_ABOVE_DROPZONE_CLASS);
/**
* Emitted when the draggable gets out of the dropzone.
*
* @event *:dropzone-out
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the dropzone
* @param e.dragNode {HtmlElement} The HtmlElement that is being dragged
* @param e.dropzone {Promise} The Promise that gets fulfilled as soon as the draggable is dropped, or outside the dropzone
* Will fulfill with one argument: `onDropzone` {Boolean} when `true`, the draggable is dropped inside the dropzone, otherwise
* the draggable got outside the dropzone without beging dropped.
* @param e.dropTarget {HtmlElement} The dropzone HtmlElement. Equals e.target
* @param e.ctrlKey {Boolean} Whether the Ctrl/cmd key is pressed
* @param e.isCopied {Boolean} Whether the drag is a copy-drag
* @param [e.sourceNode] {HtmlElement} The original Element. Only when a `copy` is made --> e.dragNode is being moved while
* e.sourceNode stand at its place.
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating the draggable
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement of the draggable 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.
* @param [e.relativeDragNodes] {NodeList} an optional list that holds the HtmlElements that corresponds with
* the `e.relative` list, but is a list with draggable Elements.
* @since 0.1
*/
insideDropTarget || e._noDDoutEvt || Event.emit(dropzone, e.emitter+':'+DROPZONE_OUT, e);
}
);
},
/**
* Defines the definition of the `dd-drop` event: the last phase of the drag-eventcycle (dd-start, *:dd-drag, *:dd-drop)
*
* @method _defineDropEv
* @param e {Object} eventobject
* @param sourceNode {HtmlElement} the original HtmlElement
* @param dragNode {HtmlElement} the dragged HtmlElement (either original or clone)
* @param dropzoneSpecified {Boolean} whether the sourceNode had a dropzone specified
* @param x {Number} x-position in coordinaties relative to `document` (like getX())
* @param y {Number} y-position in coordinaties relative to `document` (like getX())
* @param inlineLeft {String} inline css `left` for the original sourceNode
* @param inlineTop {String} inline css `top` for the original sourceNode
* @param relatives {Array} hash with all draggables that are being move togerther with the master draggable
* @private
* @since 0.0.1
*/
_defineDropEv: function(e, ddProps) {
console.log(NAME, '_defineDropEv '+ddProps.dragNode);
var instance = this;
instance.restoreDraggables = instance._restoreDraggables.bind(instance, e, ddProps);
Event.defineEvent(e.emitter+':'+DD_DROP)
.defaultFn(instance._defFnDrop.rbind(instance, ddProps))
.forceAssign(); // need to reassign, because all arguments need to be bound again and we need to override the definition of the `drag`-module
},
/**
* Defines the definition of the `dropzone` event.
* Also sets up listeners to tricker dd-over when the mouse is above an dropzone.
*
* @method _defineOverEv
* @param e {Object} eventobject
* @param dropzones {NodeList} list with dropzonenodes
* @private
* @since 0.0.1
*/
_defineOverEv: function(e, dropzones) {
console.log(NAME, '_defineOverEv');
var instance = this,
emitterName = e.emitter,
ddProps = instance.ddProps;
Event.defineEvent(emitterName+':'+DROPZONE_OVER)
.defaultFn(instance._defFnOver.bind(instance)); // no need to reassign
return Event.after([mobileEvents ? PANMOVE : MOUSEMOVE, DD_FAKE_MOUSEMOVE], function(e2) {
var overDropzone = false,
dragNode = ddProps.dragNode;
if (typeof e2.center==='object') {
e2.clientX = e2.center.x;
e2.clientY = e2.center.y;
}
ddProps.mouseOverNode = e.target;
if (e2.clientX) {
ddProps.xMouseLast = e2.clientX + window.getScrollLeft();
ddProps.yMouseLast = e2.clientY + window.getScrollTop();
}
dropzones.forEach(
function(dropzone) {
// don't do double:
if (dropzone === e.dropTarget) {
overDropzone = true;
return;
}
var dropzoneAccept = dropzone.getAttr(DZ_DROPZONE) || '',
dropzoneMove = REGEXP_MOVE.test(dropzoneAccept),
dropzoneCopy = REGEXP_COPY.test(dropzoneAccept),
dropzoneDefDraggable = dragNode.getAttr(DD_DROPZONE),
dragOverPromise, dragOutEvent, effectAllowed, emitterAllowed, dropzoneEmitter, xMouseLast, yMouseLast, dropzoneAllowed;
// check if the mouse is inside the dropzone
// also check if the mouse is inside the dragged node: the dragged node might have been constrained
// and check if the dragged node is effectAllowed to go into the dropzone
xMouseLast = ddProps.xMouseLast;
yMouseLast = ddProps.yMouseLast;
if (dropzone.insidePos(xMouseLast, yMouseLast) && dragNode.insidePos(xMouseLast, yMouseLast)) {
effectAllowed = (!dropzoneMove && !dropzoneCopy) || (dropzoneCopy && (dropEffect===COPY)) || (dropzoneMove && (dropEffect===MOVE));
dropzoneEmitter = instance.getDropzoneEmitter(dropzoneAccept);
emitterAllowed = !dropzoneEmitter || (dropzoneEmitter.contains(emitterName));
dropzoneAllowed = !dropzoneDefDraggable || ((dropzoneDefDraggable===TRUE) || dropzone.matchesSelector(dropzoneDefDraggable));
if (dropzoneAllowed && effectAllowed && emitterAllowed) {
overDropzone = true;
e.dropTarget = dropzone;
// mouse is in area of dropzone
dragOverPromise = Promise.manage();
e.dropzone = dragOverPromise;
dragOutEvent = Event.after(
[mobileEvents ? PANMOVE : MOUSEMOVE, DD_FAKE_MOUSEMOVE],
function() {
dragOverPromise.fulfill(false);
},
function(e3) {
var effectAllowed, dropzoneAccept, dropzoneMove, dropzoneCopy;
if (e3.type===DD_FAKE_MOUSEMOVE) {
dropzoneAccept = dropzone.getAttr(DZ_DROPZONE) || '';
dropzoneMove = REGEXP_MOVE.test(dropzoneAccept);
dropzoneCopy = REGEXP_COPY.test(dropzoneAccept);
effectAllowed = (!dropzoneMove && !dropzoneCopy) || (dropzoneCopy && (dropEffect===COPY)) || (dropzoneMove && (dropEffect===MOVE));
return !effectAllowed;
}
return !dropzone.insidePos((e3.clientX || e3.center.x)+window.getScrollLeft(), (e3.clientY || e3.center.y)+window.getScrollTop());
}
);
dragOverPromise.finally(
function(insideDropzone) {
dragOutEvent.detach();
insideDropzone || (e.dropTarget=null);
}
);
ddProps.dragOverList.push(dragOverPromise);
/**
* Emitted when the draggable gets inside a dropzone.
*
* @event *:dropzone-over
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the dropzone
* @param e.dragNode {HtmlElement} The HtmlElement that is being dragged
* @param e.dropzone {Promise} The Promise that gets fulfilled as soon as the draggable is dropped, or outside the dropzone
* Will fulfill with one argument: `onDropzone` {Boolean} when `true`, the draggable is dropped inside the dropzone, otherwise
* the draggable got outside the dropzone without beging dropped.
* @param e.dropTarget {HtmlElement} The dropzone HtmlElement. Equals e.target
* @param e.ctrlKey {Boolean} Whether the Ctrl/cmd key is pressed
* @param e.isCopied {Boolean} Whether the drag is a copy-drag
* @param [e.sourceNode] {HtmlElement} The original Element. Only when a `copy` is made --> e.dragNode is being moved while
* e.sourceNode stand at its place.
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating the draggable
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement of the draggable 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.
* @param [e.relativeDragNodes] {NodeList} an optional list that holds the HtmlElements that corresponds with
* the `e.relative` list, but is a list with draggable Elements.
* @since 0.1
*/
Event.emit(dropzone, emitterName+':'+DROPZONE_OVER, e);
}
}
}
);
overDropzone || (e.dropTarget=null);
});
},
/**
* Emits a dropzone-drop event.
*
* @method _emitDropzoneDrop
* @param e {Object} eventobject to pass arround
* @private
* @since 0.0.1
*/
_emitDropzoneDrop: function(e) {
/**
* Emitted when a draggable gets dropped inside a dropzone.
*
* @event *:dropzone-drop
* @param e {Object} eventobject including:
* @param e.target {HtmlElement} the dropzone
* @param e.dragNode {HtmlElement} The HtmlElement that is being dragged
* @param e.dropzone {Promise} The Promise that gets fulfilled as soon as the draggable is dropped, or outside the dropzone
* Will fulfill with one argument: `onDropzone` {Boolean} when `true`, the draggable is dropped inside the dropzone, otherwise
* the draggable got outside the dropzone without beging dropped.
* @param e.dropTarget {HtmlElement} The dropzone HtmlElement. Equals e.target
* @param e.ctrlKey {Boolean} Whether the Ctrl/cmd key is pressed
* @param e.isCopied {Boolean} Whether the drag is a copy-drag
* @param [e.sourceNode] {HtmlElement} The original Element. Only when a `copy` is made --> e.dragNode is being moved while
* e.sourceNode stand at its place.
* @param e.currentTarget {HtmlElement} the HtmlElement that is delegating the draggable
* @param e.sourceTarget {HtmlElement} the deepest HtmlElement of the draggable 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.
* @param [e.relativeDragNodes] {NodeList} an optional list that holds the HtmlElements that corresponds with
* the `e.relative` list, but is a list with draggable Elements.
* @since 0.1
*/
Event.emit(e.dropTarget, e.emitter+':'+DROPZONE_DROP, e);
},
/**
* Sets the draggable node back to its original position
*
* @method _handleDrop
* @param e {Object} eventobject
* @param sourceNode {HtmlElement} the original HtmlElement
* @param dragNode {HtmlElement} the dragged HtmlElement (either original or clone)
* @param relatives {Array} hash with all draggables that are being move togerther with the master draggable
* @private
* @since 0.0.1
*/
_handleDrop: function(e, sourceNode, dragNode, relatives) {
console.log(NAME, '_handleDrop '+dragNode);
var instance = this,
dropzoneNode = e.dropTarget,
delegatedDragging = sourceNode.hasClass(DEL_DRAGGABLE),
constrainRectangle, borderLeft, borderTop, dragNodeX, dragNodeY, copyToDropzone, moveToDropzone,
moveInsideDropzone, isCopied, dropzoneDelegatedDraggable, dropzoneIsDelegated;
if (dropzoneNode) {
dropzoneDelegatedDraggable = dropzoneNode.getAttr(DD_MINUSDRAGGABLE);
dropzoneIsDelegated = dropzoneDelegatedDraggable && (dropzoneNode.getAttr(DD_MINUSDRAGGABLE)!=='true');
copyToDropzone = function(nodeSource, nodeDrag, shiftX, shiftY) {
if (delegatedDragging) {
if (!dropzoneIsDelegated) {
dragNode.getPlugin('dd').then(function(plugin) {
plugin.model[DRAGGABLE] = TRUE;
});
}
nodeDrag.removeClass(DEL_DRAGGABLE);
}
PLUGIN_ATTRS.forEach(function(attribute) {
var data = '_del_'+attribute,
attr = sourceNode.getData(data);
if (attr) {
if (dropzoneIsDelegated) {
nodeDrag.removeAttr(attribute);
}
else {
dragNode.getPlugin('dd').then(function(plugin) {
plugin.model[attribute] = attr;
});
}
nodeSource.getPlugin('dd').then(function(plugin) {
delete plugin.model[attribute];
});
nodeSource.removeData(data);
nodeDrag.removeData(data);
}
});
dropzoneNode.append(nodeDrag);
nodeDrag.removeClass([DD_OPACITY_CLASS, DD_TRANSITION_CLASS, HIGH_Z_CLASS, DD_DRAGGING_CLASS, NO_TRANS_CLASS, DD_MASTER_CLASS, DD_COPIED_CLASS]);
nodeSource.removeClass(DD_SOURCE_ISCOPIED_CLASS);
nodeDrag.setXY(dragNodeX+shiftX, dragNodeY+shiftY, constrainRectangle, true);
// make the new HtmlElement non-copyable: it only can be replaced inside its dropzone
if (!dropzoneIsDelegated) {
nodeDrag.getPlugin('dd').then(function(plugin) {
plugin.model[EFFECT_ALLOWED] = MOVE;
plugin.model[DROPZONE_MOVABLE] = TRUE;
});
}
};
moveToDropzone = function(nodeSource, nodeDrag, shiftX, shiftY) {
nodeSource.setInlineStyle(POSITION, ABSOLUTE);
if (delegatedDragging) {
if (!dropzoneIsDelegated) {
nodeSource.getPlugin('dd').then(function(plugin) {
plugin.model[DRAGGABLE] = TRUE;
});
}
nodeSource.removeClass(DEL_DRAGGABLE);
}
PLUGIN_ATTRS.forEach(function(attribute) {
var data = '_del_'+attribute,
attr = sourceNode.getData(data);
if (attr) {
if (dropzoneIsDelegated) {
nodeSource.removeAttr(attribute);
}
else {
nodeSource.getPlugin('dd').then(function(plugin) {
plugin.model[attribute] = attr;
});
}
nodeSource.removeData(data);
}
});
dropzoneNode.append(nodeSource);
nodeSource.setXY(dragNodeX+shiftX, dragNodeY+shiftY, constrainRectangle, true);
// make the new HtmlElement non-copyable: it only can be replaced inside its dropzone
if (!dropzoneIsDelegated) {
nodeSource.getPlugin('dd').then(function(plugin) {
plugin.model[EFFECT_ALLOWED] = MOVE;
plugin.model[DROPZONE_MOVABLE] = TRUE;
});
}
nodeSource.removeClass(DD_HIDDEN_SOURCE_CLASS);
nodeDrag.remove();
};
// reset its position, only now constrain it to the dropzondenode
// we need to specify exactly the droparea: because we don't want to compare to any
// scrollWidth/scrollHeight, but exaclty to the visible part of the dropzone
borderLeft = parseInt(dropzoneNode.getStyle(BORDER_LEFT_WIDTH), 10);
borderTop = parseInt(dropzoneNode.getStyle(BORDER_TOP_WIDTH), 10);
constrainRectangle = {
x: dropzoneNode.left + borderLeft,
y: dropzoneNode.top + borderTop,
w: dropzoneNode.offsetWidth - borderLeft - parseInt(dropzoneNode.getStyle(BORDER_RIGHT_WIDTH), 10),
h: dropzoneNode.offsetHeight - borderTop - parseInt(dropzoneNode.getStyle(BORDER_BOTTOM_WIDTH), 10)
};
isCopied = (ctrlPressed && instance.allowCopy(dragNode)) || instance.onlyCopy(dragNode);
if (isCopied) {
// backup x,y before move it into dropzone (which leads to new x,y)
dragNodeX = dragNode.left;
dragNodeY = dragNode.top;
// now move the dragNode into dropzone
relatives && relatives.forEach(
function(item) {
(dragNode!==item.dragNode) && copyToDropzone(item.sourceNode, item.dragNode, item.shiftX, item.shiftY);
}
);
copyToDropzone(sourceNode, dragNode, 0 ,0);
}
else {
dragNodeX = dragNode.left;
dragNodeY = dragNode.top;
relatives && relatives.forEach(
function(item) {
(dragNode!==item.dragNode) && moveToDropzone(item.sourceNode, item.dragNode, item.shiftX, item.shiftY);
}
);
moveToDropzone(sourceNode, dragNode, 0, 0);
}
sourceNode.removeClass(DEL_DRAGGABLE);
instance._emitDropzoneDrop(e);
}
else {
(dragNode.hasAttr(DD_DROPZONE_MOVABLE)) && (dropzoneNode=dragNode.inside(DROPZONE_BRACKETS));
if (dropzoneNode && dragNode.rectangleInside(dropzoneNode)) {
moveInsideDropzone = function(hasMatch, nodeSource, nodeDrag, shiftX, shiftY) {
hasMatch && nodeSource.setXY(nodeSource+shiftX, nodeSource+shiftY, constrainRectangle, true);
if (delegatedDragging) {
nodeSource.removeClass(DEL_DRAGGABLE);
}
PLUGIN_ATTRS.forEach(function(attribute) {
var data = '_del_'+attribute,
attr = dragNode.getData(data);
if (attr) {
if (dropzoneIsDelegated) {
nodeSource.removeAttr(attribute);
}
else {
nodeSource.getPlugin('dd').then(function(plugin) {
plugin.model[attribute] = attr;
});
}
nodeSource.removeData(data);
}
});
nodeSource.removeClass(DD_HIDDEN_SOURCE_CLASS);
nodeDrag.remove();
};
// reset its position, only now constrain it to the dropzondenode
// we need to specify exactly the droparea: because we don't want to compare to any
// scrollWidth/scrollHeight, but exaclty to the visible part of the dropzone
dropzoneDelegatedDraggable = dropzoneNode.getAttr(DD_MINUSDRAGGABLE);
dropzoneIsDelegated = dropzoneDelegatedDraggable && (dropzoneNode.getAttr(DD_MINUSDRAGGABLE)!=='true');
borderLeft = parseInt(dropzoneNode.getStyle(BORDER_LEFT_WIDTH), 10);
borderTop = parseInt(dropzoneNode.getStyle(BORDER_TOP_WIDTH), 10);
constrainRectangle = {
x: dropzoneNode.left + borderLeft,
y: dropzoneNode.top + borderTop,
w: dropzoneNode.offsetWidth - borderLeft - parseInt(dropzoneNode.getStyle(BORDER_RIGHT_WIDTH), 10),
h: dropzoneNode.offsetHeight - borderTop - parseInt(dropzoneNode.getStyle(BORDER_BOTTOM_WIDTH), 10)
};
dragNodeX = dragNode.left;
dragNodeY = dragNode.top;
relatives && relatives.forEach(
function(item) {
(sourceNode!==item.sourceNode) && moveInsideDropzone(dropzoneNode, item.sourceNode, item.dragNode, item.shiftX, item.shiftY);
}
);
moveInsideDropzone(dropzoneNode, sourceNode, dragNode, 0, 0);
}
else {
instance.restoreDraggables();
}
}
sourceNode.removeClass(DD_MASTER_CLASS);
dragNode.removeClass(DD_MASTER_CLASS);
},
/**
* Sets the draggable items back to their original place. Should only be used when you prevent the default-function of `dd-drop`,
* so you can choose to do set the draggables back conditionally.
*
* @method _restoreDraggables
* @param e {Object} eventobject
* @param sourceNode {HtmlElement} the original HtmlElement
* @param dragNode {HtmlElement} the dragged HtmlElement (either original or clone)
* @param dropzoneSpecified {Boolean} whether the sourceNode had a dropzone specified
* @param x {Number} x-position in coordinaties relative to `document` (like getX())
* @param y {Number} y-position in coordinaties relative to `document` (like getX())
* @param inlineLeft {String} inline css `left` for the original sourceNode
* @param inlineTop {String} inline css `top` for the original sourceNode
* @param relatives {Array} hash with all draggables that are being move togerther with the master draggable
* @private
* @since 0.0.1
*/
_restoreDraggables: function(e, ddProps) {
console.log(NAME, '_restoreDraggables');
var instance = this,
sourceNode = ddProps.sourceNode,
dragNode = ddProps.dragNode,
dropzoneSpecified = ddProps.dropzoneSpecified,
x = ddProps.x,
y = ddProps.y,
inlineLeft = ddProps.inlineLeft,
inlineTop = ddProps.inlineTop,
relatives = ddProps.relatives;
instance.restoreDraggables = function() {/* NOOP */ return this;};
instance._setBack(e, sourceNode, dragNode, dropzoneSpecified, x, y, inlineLeft, inlineTop, e.dropzone);
relatives && relatives.forEach(
function(item) {
(dragNode!==item.dragNode) && instance._setBack(e, item.sourceNode, item.dragNode, dropzoneSpecified, x+item.shiftX, y+item.shiftY, item.inlineLeft, item.inlineTop);
}
);
return instance;
},
/**
* Sets the draggable node back to its original position
*
* @method _setBack
* @param sourceNode {HtmlElement} the original HtmlElement
* @param dragNode {HtmlElement} the dragged HtmlElement (either original or clone)
* @param dropzoneSpecified {Boolean} whether the sourceNode had a dropzone specified
* @param x {Number} x-position in coordinaties relative to `document` (like getX())
* @param y {Number} y-position in coordinaties relative to `document` (like getX())
* @param inlineLeft {String} inline css `left` for the original sourceNode
* @param inlineTop {String} inline css `top` for the original sourceNode
* @param [emitDropzoneEvent] {Boolean} whether dropzone-event should be emitted
* @private
* @since 0.0.1
*/
_setBack: function(e, sourceNode, dragNode, dropzoneSpecified, x, y, inlineLeft, inlineTop, emitDropzoneEvent) {
console.log(NAME, '_setBack to '+x+', '+y);
var tearedDown,
winScrollTop,
winScrollLeft,
dropzones,
tearDown = function() {
// dragNode might be gone when this method is called for the second time
// therefor check its existance:
if (!tearedDown) {
tearedDown = true;
// notransRemoval || (dragNode.removeEventListener && dragNode.removeEventListener(TRANS_END, tearDown, true));
if (dropzoneSpecified) {
sourceNode.removeClass([DD_HIDDEN_SOURCE_CLASS, DEL_DRAGGABLE, DD_MASTER_CLASS, DD_SOURCE_ISCOPIED_CLASS]);
dragNode.remove();
}
else {
dragNode.removeClass([DD_TRANSITION_CLASS, HIGH_Z_CLASS, DD_DRAGGING_CLASS, DEL_DRAGGABLE, DD_MASTER_CLASS, DD_SOURCE_ISCOPIED_CLASS]);
dragNode.setInlineStyle(LEFT, inlineLeft)
.setInlineStyle(TOP, inlineTop);
}
PLUGIN_ATTRS.forEach(function(attribute) {
var data = '_del_'+attribute;
if (sourceNode.getData(data)) {
sourceNode.removeData(data);
sourceNode.getPlugin('dd').then(function(plugin) {
delete plugin.model[attribute];
});
}
});
}
};
dragNode.removeClass([NO_TRANS_CLASS, DD_DRAGGING_CLASS]);
dragNode.setClass(DD_TRANSITION_CLASS);
// transitions only work with IE10+, and that browser has addEventListener
// when it doesn't have, it doesn;t harm to leave the transitionclass on: it would work anyway
// nevertheless we will remove it with a timeout
// if (dragNode.addEventListener) {
// dragNode.addEventListener(TRANS_END, tearDown, true);
// }
// ALWAYS tearDowm after delay --> when there was no repositioning, there never will be a transition-event
// LATER(tearDown, 260);
dragNode.setXY(x, y).finally(tearDown);
// now we might need to fire a last `dropzone` event when the dragged element returns to a dropzone when it wasn't before set it back
if (emitDropzoneEvent) {
dropzones = DOCUMENT.getAll(DROPZONE_BRACKETS);
if (dropzones) {
winScrollTop = window.getScrollTop();
winScrollLeft = window.getScrollLeft();
dropzones.forEach(
function(dropzone) {
if (dropzone.insidePos(x, y) && !dropzone.insidePos(e.xMouse+winScrollLeft, e.yMouse+winScrollTop)) {
e.dropTarget = dropzone;
e._noDDoutEvt = true;
Event.emit(dropzone, e.emitter+':'+DROPZONE_OVER, e);
}
}
);
}
}
},
/**
* Sets up a `keydown` and `keyup` listener, to monitor whether a `ctrlKey` (windows) or `metaKey` (Mac)
* is pressed to support the copying of draggable items
*
* @method _setupKeyEv
* @private
* @since 0.0.1
*/
_setupKeyEv: function() {
console.log(NAME, '_setupKeyEv');
var instance = this,
changeClasses = function(sourceNode, dragNode) {
sourceNode.toggleClass(DD_HIDDEN_SOURCE_CLASS, !ctrlPressed);
sourceNode.toggleClass(DD_SOURCE_ISCOPIED_CLASS, ctrlPressed);
dragNode.toggleClass([DD_OPACITY_CLASS, DD_COPIED_CLASS], ctrlPressed);
};
Event.after([KEY+DOWN, KEY+UP], function(e) {
console.log(NAME, 'event '+e.type);
var ddProps = instance.ddProps,
sourceNode = ddProps.sourceNode,
dragNode, mouseOverNode;
ctrlPressed = e.ctrlKey || e.metaKey;
if (sourceNode && instance.allowSwitch(sourceNode)) {
dragNode = ddProps.dragNode;
mouseOverNode = ddProps.mouseOverNode;
dropEffect = ctrlPressed ? COPY : MOVE;
changeClasses(sourceNode, dragNode);
ddProps.relatives && ddProps.relatives.forEach(
function(item) {
changeClasses(item.sourceNode, item.dragNode);
}
);
// now, it could be that any droptarget should change its appearance (DD_DROPACTIVE_CLASS).
// we need to recalculate it for all targets
// we do this by emitting a DD_FAKE_MOUSEMOVE event
/**
* 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-mousemove
* @private
* @since 0.1
*/
mouseOverNode && Event.emit(mouseOverNode, UI+':'+DD_FAKE_MOUSEMOVE);
}
});
},
/**
* Cleansup the dragover subscriber and fulfills any dropzone-promise.
*
* @method _teardownOverEvent
* @param e {Object} eventobject
* @private
* @since 0.0.1
*/
_teardownOverEvent: function(e, ddProps) {
console.log('_teardownOverEvent');
var dragOverEvent = ddProps.dragOverEv,
mouseX = e.xMouse,
mouseY = e.yMouse,
winScrollTop, winScrollLeft;
if (dragOverEvent) {
dragOverEvent.detach();
winScrollTop = window.getScrollTop();
winScrollLeft = window.getScrollLeft();
ddProps.dragOverList.forEach(function(promise) {
promise.fulfill(e.dropTarget && e.dropTarget.insidePos(mouseX+winScrollLeft, mouseY+winScrollTop));
});
}
},
/**
* Returns true if the dropzone-HtmlElement accepts copy-dragables.
* Is determined by the attribute `dd-effect-allowed="copy"` or `dd-effect-allowed="all"`
*
* @method allowCopy
* @param dropzone {HtmlElement} HtmlElement that is checked for its allowed effects
* @return {Boolean} if copy-dragables are allowed
* @since 0.0.1
*/
allowCopy: function(dropzone) {
var allowedEffects = this._allowedEffects(dropzone);
console.log('allowCopy --> '+REGEXP_ALL.test(allowedEffects) || REGEXP_COPY.test(allowedEffects));
return REGEXP_ALL.test(allowedEffects) || REGEXP_COPY.test(allowedEffects);
},
/**
* Returns true if the dragable-HtmlElement allowes to switch between `copy` and `move`.
*
* @method allowSwitch
* @param dragableElement {HtmlElement} HtmlElement that is checked for its allowed effects
* @return {Boolean} if copy-dragables are allowed
* @since 0.0.1
*/
allowSwitch: function(dragableElement) {
console.log('allowSwitch --> '+REGEXP_ALL.test(this._allowedEffects(dragableElement)));
return REGEXP_ALL.test(this._allowedEffects(dragableElement));
},
/**
* Returns the emitterName that the dropzone accepts.
*
* @method getDropzoneEmitter
* @param dropzone {String} dropzone attribute of the dropzone HtmlElement
* @return {String|null} the emitterName that is accepted
* @since 0.0.1
*/
getDropzoneEmitter: function(dropzone) {
var extract = dropzone.match(REGEXP_EMITTER);
console.log('getDropzoneEmitter --> '+(extract && extract[1]));
return extract && (','+extract[1]+',');
},
/**
* Initializes dragdrop. Needs to be invoked, otherwise DD won't run.
*
* @method init (extended by drag-drop)
* @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._ddInited) {
// we will initialize `Drag` --> don;t worry if it was initialised before,
// Drag.init() will only run once
$superInit.call(instance);
instance._setupKeyEv();
instance.notify(function(e, ddProps) {
var dropzones, sourceNode,
dragNode = ddProps.dragNode,
dropzoneSpecified = ddProps.dropzoneSpecified = dragNode.hasAttr(DD_DROPZONE) || dragNode.hasAttr(DD_EMITTER) || (e.emitter!==UI),
setupDragnode = function(nodeSource, nodeDrag, shiftX, shiftY) {
if (dropEffect===COPY) {
nodeDrag.setClass([DD_OPACITY_CLASS, DD_COPIED_CLASS]);
nodeSource.setClass(DD_SOURCE_ISCOPIED_CLASS);
}
else {
nodeSource.setClass(DD_HIDDEN_SOURCE_CLASS);
}
nodeDrag.setClass(INVISIBLE_CLASS);
nodeDrag.setInlineStyle(POSITION, ABSOLUTE);
nodeSource.parentNode.append(nodeDrag, false, nodeSource);
nodeDrag.setXY(ddProps.xMouseLast+shiftX, ddProps.yMouseLast+shiftY, ddProps.constrain, true);
nodeDrag.removeClass(INVISIBLE_CLASS);
};
if (dropzoneSpecified) {
sourceNode = e.sourceNode = ddProps.sourceNode = ddProps.dragNode;
e.dragNode = ddProps.dragNode = ddProps.sourceNode.cloneNode(true);
// correct sourceNode class: reset CSS set by `drag`:
sourceNode.removeClass([NO_TRANS_CLASS, HIGH_Z_CLASS, DD_DRAGGING_CLASS]);
// also correct inline CSS style for `left` and `top` of the sourceNode:
sourceNode.setInlineStyle(LEFT, ddProps.inlineLeft);
sourceNode.setInlineStyle(TOP, ddProps.inlineTop);
dropEffect = (instance.onlyCopy(dragNode) || (ctrlPressed && instance.allowCopy(dragNode))) ? COPY : MOVE;
setupDragnode(ddProps.sourceNode, ddProps.dragNode, 0, 0);
if (ddProps.relatives) {
e.relativeDragNodes = [];
ddProps.relatives.forEach(
function(item) {
item.sourceNode = item.dragNode;
item.dragNode = item.dragNode.cloneNode(true);
setupDragnode(item.sourceNode, item.dragNode, item.shiftX, item.shiftY);
e.relativeDragNodes.push(item.dragNode);
}
);
}
dropzones = DOCUMENT.getAll(DROPZONE_BRACKETS);
if (dropzones.length>0) {
// create a custom over-event that fires exactly when the mouse is over any dropzone
// we cannot use `hover`, because that event fails when there is an absolute floated element outsize `dropzone`
// lying on top of the dropzone. -> we need to check by coördinates
ddProps.dragOverEv = instance._defineOverEv(e, dropzones);
}
}
else {
e.dragNode = ddProps.dragNode;
}
ddProps.dragDropEv = instance._defineDropEv(e, ddProps);
}, instance, true);
instance.notify(instance._teardownOverEvent, instance);
}
instance._ddInited = true;
},
/**
* Returns true if the dragable-HtmlElement accepts only copy-dragables (no moveable)
* Is determined by the attribute `dd-effect-allowed="copy"`
*
* @method onlyCopy
* @param dragableElement {HtmlElement} HtmlElement that is checked for its allowed effects
* @return {Boolean} if only copy-dragables are allowed
* @since 0.0.1
*/
onlyCopy: function(dragableElement) {
console.log('onlyCopy --> '+REGEXP_COPY.test(this._allowedEffects(dragableElement)));
return REGEXP_COPY.test(this._allowedEffects(dragableElement));
},
/**
* Sets the draggable items back to their original place. Should only be used when you prevent the default-function of `dd-drop`,
* so you can choose to do set the draggables back conditionally.
*
* @method restoreDraggables
* @private
* @chainable
* @since 0.0.1
*/
restoreDraggables: function() {/* NOOP */ return this;}
};
DragModule.merge(DD, {force: true});
window._ITSAPlugins.dd.mergePrototypes({
attrs: {
draggable: 'string',
handle: 'string',
direction: 'string',
emitter: 'string',
'effect-allowed': 'string',
'dropzone-movable': 'string',
dropzone: 'string'
}
}, true);
DOCUMENT.definePlugin('dz', null, {
attrs: {
dropzone: 'string'
},
defaults: {
dropzone: 'true'
}
});
window._ITSAmodules.DragDrop = DragModule;
return DragModule;
};