799 lines
22 KiB
JavaScript
799 lines
22 KiB
JavaScript
|
/*!
|
||
|
* jquery.fancytree.dnd.js
|
||
|
*
|
||
|
* Drag-and-drop support (jQuery UI draggable/droppable).
|
||
|
* (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/)
|
||
|
*
|
||
|
* Copyright (c) 2008-2021, Martin Wendt (https://wwWendt.de)
|
||
|
*
|
||
|
* Released under the MIT license
|
||
|
* https://github.com/mar10/fancytree/wiki/LicenseInfo
|
||
|
*
|
||
|
* @version 2.38.2
|
||
|
* @date 2022-06-30T18:24:06Z
|
||
|
*/
|
||
|
|
||
|
(function (factory) {
|
||
|
if (typeof define === "function" && define.amd) {
|
||
|
// AMD. Register as an anonymous module.
|
||
|
define([
|
||
|
"jquery",
|
||
|
"jquery-ui/ui/widgets/draggable",
|
||
|
"jquery-ui/ui/widgets/droppable",
|
||
|
"./jquery.fancytree",
|
||
|
], factory);
|
||
|
} else if (typeof module === "object" && module.exports) {
|
||
|
// Node/CommonJS
|
||
|
require("./jquery.fancytree");
|
||
|
module.exports = factory(require("jquery"));
|
||
|
} else {
|
||
|
// Browser globals
|
||
|
factory(jQuery);
|
||
|
}
|
||
|
})(function ($) {
|
||
|
"use strict";
|
||
|
|
||
|
/******************************************************************************
|
||
|
* Private functions and variables
|
||
|
*/
|
||
|
var didRegisterDnd = false,
|
||
|
classDropAccept = "fancytree-drop-accept",
|
||
|
classDropAfter = "fancytree-drop-after",
|
||
|
classDropBefore = "fancytree-drop-before",
|
||
|
classDropOver = "fancytree-drop-over",
|
||
|
classDropReject = "fancytree-drop-reject",
|
||
|
classDropTarget = "fancytree-drop-target";
|
||
|
|
||
|
/* Convert number to string and prepend +/-; return empty string for 0.*/
|
||
|
function offsetString(n) {
|
||
|
// eslint-disable-next-line no-nested-ternary
|
||
|
return n === 0 ? "" : n > 0 ? "+" + n : "" + n;
|
||
|
}
|
||
|
|
||
|
//--- Extend ui.draggable event handling --------------------------------------
|
||
|
|
||
|
function _registerDnd() {
|
||
|
if (didRegisterDnd) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Register proxy-functions for draggable.start/drag/stop
|
||
|
|
||
|
$.ui.plugin.add("draggable", "connectToFancytree", {
|
||
|
start: function (event, ui) {
|
||
|
// 'draggable' was renamed to 'ui-draggable' since jQueryUI 1.10
|
||
|
var draggable =
|
||
|
$(this).data("ui-draggable") ||
|
||
|
$(this).data("draggable"),
|
||
|
sourceNode = ui.helper.data("ftSourceNode") || null;
|
||
|
|
||
|
if (sourceNode) {
|
||
|
// Adjust helper offset, so cursor is slightly outside top/left corner
|
||
|
draggable.offset.click.top = -2;
|
||
|
draggable.offset.click.left = +16;
|
||
|
// Trigger dragStart event
|
||
|
// TODO: when called as connectTo..., the return value is ignored(?)
|
||
|
return sourceNode.tree.ext.dnd._onDragEvent(
|
||
|
"start",
|
||
|
sourceNode,
|
||
|
null,
|
||
|
event,
|
||
|
ui,
|
||
|
draggable
|
||
|
);
|
||
|
}
|
||
|
},
|
||
|
drag: function (event, ui) {
|
||
|
var ctx,
|
||
|
isHelper,
|
||
|
logObject,
|
||
|
// 'draggable' was renamed to 'ui-draggable' since jQueryUI 1.10
|
||
|
draggable =
|
||
|
$(this).data("ui-draggable") ||
|
||
|
$(this).data("draggable"),
|
||
|
sourceNode = ui.helper.data("ftSourceNode") || null,
|
||
|
prevTargetNode = ui.helper.data("ftTargetNode") || null,
|
||
|
targetNode = $.ui.fancytree.getNode(event.target),
|
||
|
dndOpts = sourceNode && sourceNode.tree.options.dnd;
|
||
|
|
||
|
// logObject = sourceNode || prevTargetNode || $.ui.fancytree;
|
||
|
// logObject.debug("Drag event:", event, event.shiftKey);
|
||
|
if (event.target && !targetNode) {
|
||
|
// We got a drag event, but the targetNode could not be found
|
||
|
// at the event location. This may happen,
|
||
|
// 1. if the mouse jumped over the drag helper,
|
||
|
// 2. or if a non-fancytree element is dragged
|
||
|
// We ignore it:
|
||
|
isHelper =
|
||
|
$(event.target).closest(
|
||
|
"div.fancytree-drag-helper,#fancytree-drop-marker"
|
||
|
).length > 0;
|
||
|
if (isHelper) {
|
||
|
logObject =
|
||
|
sourceNode || prevTargetNode || $.ui.fancytree;
|
||
|
logObject.debug("Drag event over helper: ignored.");
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
ui.helper.data("ftTargetNode", targetNode);
|
||
|
|
||
|
if (dndOpts && dndOpts.updateHelper) {
|
||
|
ctx = sourceNode.tree._makeHookContext(sourceNode, event, {
|
||
|
otherNode: targetNode,
|
||
|
ui: ui,
|
||
|
draggable: draggable,
|
||
|
dropMarker: $("#fancytree-drop-marker"),
|
||
|
});
|
||
|
dndOpts.updateHelper.call(sourceNode.tree, sourceNode, ctx);
|
||
|
}
|
||
|
|
||
|
// Leaving a tree node
|
||
|
if (prevTargetNode && prevTargetNode !== targetNode) {
|
||
|
prevTargetNode.tree.ext.dnd._onDragEvent(
|
||
|
"leave",
|
||
|
prevTargetNode,
|
||
|
sourceNode,
|
||
|
event,
|
||
|
ui,
|
||
|
draggable
|
||
|
);
|
||
|
}
|
||
|
if (targetNode) {
|
||
|
if (!targetNode.tree.options.dnd.dragDrop) {
|
||
|
// not enabled as drop target
|
||
|
} else if (targetNode === prevTargetNode) {
|
||
|
// Moving over same node
|
||
|
targetNode.tree.ext.dnd._onDragEvent(
|
||
|
"over",
|
||
|
targetNode,
|
||
|
sourceNode,
|
||
|
event,
|
||
|
ui,
|
||
|
draggable
|
||
|
);
|
||
|
} else {
|
||
|
// Entering this node first time
|
||
|
targetNode.tree.ext.dnd._onDragEvent(
|
||
|
"enter",
|
||
|
targetNode,
|
||
|
sourceNode,
|
||
|
event,
|
||
|
ui,
|
||
|
draggable
|
||
|
);
|
||
|
targetNode.tree.ext.dnd._onDragEvent(
|
||
|
"over",
|
||
|
targetNode,
|
||
|
sourceNode,
|
||
|
event,
|
||
|
ui,
|
||
|
draggable
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
// else go ahead with standard event handling
|
||
|
},
|
||
|
stop: function (event, ui) {
|
||
|
var logObject,
|
||
|
// 'draggable' was renamed to 'ui-draggable' since jQueryUI 1.10:
|
||
|
draggable =
|
||
|
$(this).data("ui-draggable") ||
|
||
|
$(this).data("draggable"),
|
||
|
sourceNode = ui.helper.data("ftSourceNode") || null,
|
||
|
targetNode = ui.helper.data("ftTargetNode") || null,
|
||
|
dropped = event.type === "mouseup" && event.which === 1;
|
||
|
|
||
|
if (!dropped) {
|
||
|
logObject = sourceNode || targetNode || $.ui.fancytree;
|
||
|
logObject.debug("Drag was cancelled");
|
||
|
}
|
||
|
if (targetNode) {
|
||
|
if (dropped) {
|
||
|
targetNode.tree.ext.dnd._onDragEvent(
|
||
|
"drop",
|
||
|
targetNode,
|
||
|
sourceNode,
|
||
|
event,
|
||
|
ui,
|
||
|
draggable
|
||
|
);
|
||
|
}
|
||
|
targetNode.tree.ext.dnd._onDragEvent(
|
||
|
"leave",
|
||
|
targetNode,
|
||
|
sourceNode,
|
||
|
event,
|
||
|
ui,
|
||
|
draggable
|
||
|
);
|
||
|
}
|
||
|
if (sourceNode) {
|
||
|
sourceNode.tree.ext.dnd._onDragEvent(
|
||
|
"stop",
|
||
|
sourceNode,
|
||
|
null,
|
||
|
event,
|
||
|
ui,
|
||
|
draggable
|
||
|
);
|
||
|
}
|
||
|
},
|
||
|
});
|
||
|
|
||
|
didRegisterDnd = true;
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
* Drag and drop support
|
||
|
*/
|
||
|
function _initDragAndDrop(tree) {
|
||
|
var dnd = tree.options.dnd || null,
|
||
|
glyph = tree.options.glyph || null;
|
||
|
|
||
|
// Register 'connectToFancytree' option with ui.draggable
|
||
|
if (dnd) {
|
||
|
_registerDnd();
|
||
|
}
|
||
|
// Attach ui.draggable to this Fancytree instance
|
||
|
if (dnd && dnd.dragStart) {
|
||
|
tree.widget.element.draggable(
|
||
|
$.extend(
|
||
|
{
|
||
|
addClasses: false,
|
||
|
// DT issue 244: helper should be child of scrollParent:
|
||
|
appendTo: tree.$container,
|
||
|
// appendTo: "body",
|
||
|
containment: false,
|
||
|
// containment: "parent",
|
||
|
delay: 0,
|
||
|
distance: 4,
|
||
|
revert: false,
|
||
|
scroll: true, // to disable, also set css 'position: inherit' on ul.fancytree-container
|
||
|
scrollSpeed: 7,
|
||
|
scrollSensitivity: 10,
|
||
|
// Delegate draggable.start, drag, and stop events to our handler
|
||
|
connectToFancytree: true,
|
||
|
// Let source tree create the helper element
|
||
|
helper: function (event) {
|
||
|
var $helper,
|
||
|
$nodeTag,
|
||
|
opts,
|
||
|
sourceNode = $.ui.fancytree.getNode(
|
||
|
event.target
|
||
|
);
|
||
|
|
||
|
if (!sourceNode) {
|
||
|
// #405, DT issue 211: might happen, if dragging a table *header*
|
||
|
return "<div>ERROR?: helper requested but sourceNode not found</div>";
|
||
|
}
|
||
|
opts = sourceNode.tree.options.dnd;
|
||
|
$nodeTag = $(sourceNode.span);
|
||
|
// Only event and node argument is available
|
||
|
$helper = $(
|
||
|
"<div class='fancytree-drag-helper'><span class='fancytree-drag-helper-img' /></div>"
|
||
|
)
|
||
|
.css({ zIndex: 3, position: "relative" }) // so it appears above ext-wide selection bar
|
||
|
.append(
|
||
|
$nodeTag
|
||
|
.find("span.fancytree-title")
|
||
|
.clone()
|
||
|
);
|
||
|
|
||
|
// Attach node reference to helper object
|
||
|
$helper.data("ftSourceNode", sourceNode);
|
||
|
|
||
|
// Support glyph symbols instead of icons
|
||
|
if (glyph) {
|
||
|
$helper
|
||
|
.find(".fancytree-drag-helper-img")
|
||
|
.addClass(
|
||
|
glyph.map._addClass +
|
||
|
" " +
|
||
|
glyph.map.dragHelper
|
||
|
);
|
||
|
}
|
||
|
// Allow to modify the helper, e.g. to add multi-node-drag feedback
|
||
|
if (opts.initHelper) {
|
||
|
opts.initHelper.call(
|
||
|
sourceNode.tree,
|
||
|
sourceNode,
|
||
|
{
|
||
|
node: sourceNode,
|
||
|
tree: sourceNode.tree,
|
||
|
originalEvent: event,
|
||
|
ui: { helper: $helper },
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
// We return an unconnected element, so `draggable` will add this
|
||
|
// to the parent specified as `appendTo` option
|
||
|
return $helper;
|
||
|
},
|
||
|
start: function (event, ui) {
|
||
|
var sourceNode = ui.helper.data("ftSourceNode");
|
||
|
return !!sourceNode; // Abort dragging if no node could be found
|
||
|
},
|
||
|
},
|
||
|
tree.options.dnd.draggable
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
// Attach ui.droppable to this Fancytree instance
|
||
|
if (dnd && dnd.dragDrop) {
|
||
|
tree.widget.element.droppable(
|
||
|
$.extend(
|
||
|
{
|
||
|
addClasses: false,
|
||
|
tolerance: "intersect",
|
||
|
greedy: false,
|
||
|
/*
|
||
|
activate: function(event, ui) {
|
||
|
tree.debug("droppable - activate", event, ui, this);
|
||
|
},
|
||
|
create: function(event, ui) {
|
||
|
tree.debug("droppable - create", event, ui);
|
||
|
},
|
||
|
deactivate: function(event, ui) {
|
||
|
tree.debug("droppable - deactivate", event, ui);
|
||
|
},
|
||
|
drop: function(event, ui) {
|
||
|
tree.debug("droppable - drop", event, ui);
|
||
|
},
|
||
|
out: function(event, ui) {
|
||
|
tree.debug("droppable - out", event, ui);
|
||
|
},
|
||
|
over: function(event, ui) {
|
||
|
tree.debug("droppable - over", event, ui);
|
||
|
}
|
||
|
*/
|
||
|
},
|
||
|
tree.options.dnd.droppable
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/******************************************************************************
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
$.ui.fancytree.registerExtension({
|
||
|
name: "dnd",
|
||
|
version: "2.38.2",
|
||
|
// Default options for this extension.
|
||
|
options: {
|
||
|
// Make tree nodes accept draggables
|
||
|
autoExpandMS: 1000, // Expand nodes after n milliseconds of hovering.
|
||
|
draggable: null, // Additional options passed to jQuery draggable
|
||
|
droppable: null, // Additional options passed to jQuery droppable
|
||
|
focusOnClick: false, // Focus, although draggable cancels mousedown event (#270)
|
||
|
preventVoidMoves: true, // Prevent dropping nodes 'before self', etc.
|
||
|
preventRecursiveMoves: true, // Prevent dropping nodes on own descendants
|
||
|
smartRevert: true, // set draggable.revert = true if drop was rejected
|
||
|
dropMarkerOffsetX: -24, // absolute position offset for .fancytree-drop-marker relatively to ..fancytree-title (icon/img near a node accepting drop)
|
||
|
dropMarkerInsertOffsetX: -16, // additional offset for drop-marker with hitMode = "before"/"after"
|
||
|
// Events (drag support)
|
||
|
dragStart: null, // Callback(sourceNode, data), return true, to enable dnd
|
||
|
dragStop: null, // Callback(sourceNode, data)
|
||
|
initHelper: null, // Callback(sourceNode, data)
|
||
|
updateHelper: null, // Callback(sourceNode, data)
|
||
|
// Events (drop support)
|
||
|
dragEnter: null, // Callback(targetNode, data)
|
||
|
dragOver: null, // Callback(targetNode, data)
|
||
|
dragExpand: null, // Callback(targetNode, data), return false to prevent autoExpand
|
||
|
dragDrop: null, // Callback(targetNode, data)
|
||
|
dragLeave: null, // Callback(targetNode, data)
|
||
|
},
|
||
|
|
||
|
treeInit: function (ctx) {
|
||
|
var tree = ctx.tree;
|
||
|
this._superApply(arguments);
|
||
|
// issue #270: draggable eats mousedown events
|
||
|
if (tree.options.dnd.dragStart) {
|
||
|
tree.$container.on("mousedown", function (event) {
|
||
|
// if( !tree.hasFocus() && ctx.options.dnd.focusOnClick ) {
|
||
|
if (ctx.options.dnd.focusOnClick) {
|
||
|
// #270
|
||
|
var node = $.ui.fancytree.getNode(event);
|
||
|
if (node) {
|
||
|
node.debug(
|
||
|
"Re-enable focus that was prevented by jQuery UI draggable."
|
||
|
);
|
||
|
// node.setFocus();
|
||
|
// $(node.span).closest(":tabbable").focus();
|
||
|
// $(event.target).trigger("focus");
|
||
|
// $(event.target).closest(":tabbable").trigger("focus");
|
||
|
}
|
||
|
setTimeout(function () {
|
||
|
// #300
|
||
|
$(event.target).closest(":tabbable").focus();
|
||
|
}, 10);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
_initDragAndDrop(tree);
|
||
|
},
|
||
|
/* Display drop marker according to hitMode ('after', 'before', 'over'). */
|
||
|
_setDndStatus: function (
|
||
|
sourceNode,
|
||
|
targetNode,
|
||
|
helper,
|
||
|
hitMode,
|
||
|
accept
|
||
|
) {
|
||
|
var markerOffsetX,
|
||
|
pos,
|
||
|
markerAt = "center",
|
||
|
instData = this._local,
|
||
|
dndOpt = this.options.dnd,
|
||
|
glyphOpt = this.options.glyph,
|
||
|
$source = sourceNode ? $(sourceNode.span) : null,
|
||
|
$target = $(targetNode.span),
|
||
|
$targetTitle = $target.find("span.fancytree-title");
|
||
|
|
||
|
if (!instData.$dropMarker) {
|
||
|
instData.$dropMarker = $(
|
||
|
"<div id='fancytree-drop-marker'></div>"
|
||
|
)
|
||
|
.hide()
|
||
|
.css({ "z-index": 1000 })
|
||
|
.prependTo($(this.$div).parent());
|
||
|
// .prependTo("body");
|
||
|
|
||
|
if (glyphOpt) {
|
||
|
instData.$dropMarker.addClass(
|
||
|
glyphOpt.map._addClass + " " + glyphOpt.map.dropMarker
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
if (
|
||
|
hitMode === "after" ||
|
||
|
hitMode === "before" ||
|
||
|
hitMode === "over"
|
||
|
) {
|
||
|
markerOffsetX = dndOpt.dropMarkerOffsetX || 0;
|
||
|
switch (hitMode) {
|
||
|
case "before":
|
||
|
markerAt = "top";
|
||
|
markerOffsetX += dndOpt.dropMarkerInsertOffsetX || 0;
|
||
|
break;
|
||
|
case "after":
|
||
|
markerAt = "bottom";
|
||
|
markerOffsetX += dndOpt.dropMarkerInsertOffsetX || 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pos = {
|
||
|
my: "left" + offsetString(markerOffsetX) + " center",
|
||
|
at: "left " + markerAt,
|
||
|
of: $targetTitle,
|
||
|
};
|
||
|
if (this.options.rtl) {
|
||
|
pos.my = "right" + offsetString(-markerOffsetX) + " center";
|
||
|
pos.at = "right " + markerAt;
|
||
|
}
|
||
|
instData.$dropMarker
|
||
|
.toggleClass(classDropAfter, hitMode === "after")
|
||
|
.toggleClass(classDropOver, hitMode === "over")
|
||
|
.toggleClass(classDropBefore, hitMode === "before")
|
||
|
.toggleClass("fancytree-rtl", !!this.options.rtl)
|
||
|
.show()
|
||
|
.position($.ui.fancytree.fixPositionOptions(pos));
|
||
|
} else {
|
||
|
instData.$dropMarker.hide();
|
||
|
}
|
||
|
if ($source) {
|
||
|
$source
|
||
|
.toggleClass(classDropAccept, accept === true)
|
||
|
.toggleClass(classDropReject, accept === false);
|
||
|
}
|
||
|
$target
|
||
|
.toggleClass(
|
||
|
classDropTarget,
|
||
|
hitMode === "after" ||
|
||
|
hitMode === "before" ||
|
||
|
hitMode === "over"
|
||
|
)
|
||
|
.toggleClass(classDropAfter, hitMode === "after")
|
||
|
.toggleClass(classDropBefore, hitMode === "before")
|
||
|
.toggleClass(classDropAccept, accept === true)
|
||
|
.toggleClass(classDropReject, accept === false);
|
||
|
|
||
|
helper
|
||
|
.toggleClass(classDropAccept, accept === true)
|
||
|
.toggleClass(classDropReject, accept === false);
|
||
|
},
|
||
|
|
||
|
/*
|
||
|
* Handles drag'n'drop functionality.
|
||
|
*
|
||
|
* A standard jQuery drag-and-drop process may generate these calls:
|
||
|
*
|
||
|
* start:
|
||
|
* _onDragEvent("start", sourceNode, null, event, ui, draggable);
|
||
|
* drag:
|
||
|
* _onDragEvent("leave", prevTargetNode, sourceNode, event, ui, draggable);
|
||
|
* _onDragEvent("over", targetNode, sourceNode, event, ui, draggable);
|
||
|
* _onDragEvent("enter", targetNode, sourceNode, event, ui, draggable);
|
||
|
* stop:
|
||
|
* _onDragEvent("drop", targetNode, sourceNode, event, ui, draggable);
|
||
|
* _onDragEvent("leave", targetNode, sourceNode, event, ui, draggable);
|
||
|
* _onDragEvent("stop", sourceNode, null, event, ui, draggable);
|
||
|
*/
|
||
|
_onDragEvent: function (
|
||
|
eventName,
|
||
|
node,
|
||
|
otherNode,
|
||
|
event,
|
||
|
ui,
|
||
|
draggable
|
||
|
) {
|
||
|
// if(eventName !== "over"){
|
||
|
// this.debug("tree.ext.dnd._onDragEvent(%s, %o, %o) - %o", eventName, node, otherNode, this);
|
||
|
// }
|
||
|
var accept,
|
||
|
nodeOfs,
|
||
|
parentRect,
|
||
|
rect,
|
||
|
relPos,
|
||
|
relPos2,
|
||
|
enterResponse,
|
||
|
hitMode,
|
||
|
r,
|
||
|
opts = this.options,
|
||
|
dnd = opts.dnd,
|
||
|
ctx = this._makeHookContext(node, event, {
|
||
|
otherNode: otherNode,
|
||
|
ui: ui,
|
||
|
draggable: draggable,
|
||
|
}),
|
||
|
res = null,
|
||
|
self = this,
|
||
|
$nodeTag = $(node.span);
|
||
|
|
||
|
if (dnd.smartRevert) {
|
||
|
draggable.options.revert = "invalid";
|
||
|
}
|
||
|
|
||
|
switch (eventName) {
|
||
|
case "start":
|
||
|
if (node.isStatusNode()) {
|
||
|
res = false;
|
||
|
} else if (dnd.dragStart) {
|
||
|
res = dnd.dragStart(node, ctx);
|
||
|
}
|
||
|
if (res === false) {
|
||
|
this.debug("tree.dragStart() cancelled");
|
||
|
//draggable._clear();
|
||
|
// NOTE: the return value seems to be ignored (drag is not cancelled, when false is returned)
|
||
|
// TODO: call this._cancelDrag()?
|
||
|
ui.helper.trigger("mouseup").hide();
|
||
|
} else {
|
||
|
if (dnd.smartRevert) {
|
||
|
// #567, #593: fix revert position
|
||
|
// rect = node.li.getBoundingClientRect();
|
||
|
rect =
|
||
|
node[
|
||
|
ctx.tree.nodeContainerAttrName
|
||
|
].getBoundingClientRect();
|
||
|
parentRect = $(
|
||
|
draggable.options.appendTo
|
||
|
)[0].getBoundingClientRect();
|
||
|
draggable.originalPosition.left = Math.max(
|
||
|
0,
|
||
|
rect.left - parentRect.left
|
||
|
);
|
||
|
draggable.originalPosition.top = Math.max(
|
||
|
0,
|
||
|
rect.top - parentRect.top
|
||
|
);
|
||
|
}
|
||
|
$nodeTag.addClass("fancytree-drag-source");
|
||
|
// Register global handlers to allow cancel
|
||
|
$(document).on(
|
||
|
"keydown.fancytree-dnd,mousedown.fancytree-dnd",
|
||
|
function (event) {
|
||
|
// node.tree.debug("dnd global event", event.type, event.which);
|
||
|
if (
|
||
|
event.type === "keydown" &&
|
||
|
event.which === $.ui.keyCode.ESCAPE
|
||
|
) {
|
||
|
self.ext.dnd._cancelDrag();
|
||
|
} else if (event.type === "mousedown") {
|
||
|
self.ext.dnd._cancelDrag();
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "enter":
|
||
|
if (
|
||
|
dnd.preventRecursiveMoves &&
|
||
|
node.isDescendantOf(otherNode)
|
||
|
) {
|
||
|
r = false;
|
||
|
} else {
|
||
|
r = dnd.dragEnter ? dnd.dragEnter(node, ctx) : null;
|
||
|
}
|
||
|
if (!r) {
|
||
|
// convert null, undefined, false to false
|
||
|
res = false;
|
||
|
} else if (Array.isArray(r)) {
|
||
|
// TODO: also accept passing an object of this format directly
|
||
|
res = {
|
||
|
over: $.inArray("over", r) >= 0,
|
||
|
before: $.inArray("before", r) >= 0,
|
||
|
after: $.inArray("after", r) >= 0,
|
||
|
};
|
||
|
} else {
|
||
|
res = {
|
||
|
over: r === true || r === "over",
|
||
|
before: r === true || r === "before",
|
||
|
after: r === true || r === "after",
|
||
|
};
|
||
|
}
|
||
|
ui.helper.data("enterResponse", res);
|
||
|
// this.debug("helper.enterResponse: %o", res);
|
||
|
break;
|
||
|
|
||
|
case "over":
|
||
|
enterResponse = ui.helper.data("enterResponse");
|
||
|
hitMode = null;
|
||
|
if (enterResponse === false) {
|
||
|
// Don't call dragOver if onEnter returned false.
|
||
|
// break;
|
||
|
} else if (typeof enterResponse === "string") {
|
||
|
// Use hitMode from onEnter if provided.
|
||
|
hitMode = enterResponse;
|
||
|
} else {
|
||
|
// Calculate hitMode from relative cursor position.
|
||
|
nodeOfs = $nodeTag.offset();
|
||
|
relPos = {
|
||
|
x: event.pageX - nodeOfs.left,
|
||
|
y: event.pageY - nodeOfs.top,
|
||
|
};
|
||
|
relPos2 = {
|
||
|
x: relPos.x / $nodeTag.width(),
|
||
|
y: relPos.y / $nodeTag.height(),
|
||
|
};
|
||
|
|
||
|
if (enterResponse.after && relPos2.y > 0.75) {
|
||
|
hitMode = "after";
|
||
|
} else if (
|
||
|
!enterResponse.over &&
|
||
|
enterResponse.after &&
|
||
|
relPos2.y > 0.5
|
||
|
) {
|
||
|
hitMode = "after";
|
||
|
} else if (enterResponse.before && relPos2.y <= 0.25) {
|
||
|
hitMode = "before";
|
||
|
} else if (
|
||
|
!enterResponse.over &&
|
||
|
enterResponse.before &&
|
||
|
relPos2.y <= 0.5
|
||
|
) {
|
||
|
hitMode = "before";
|
||
|
} else if (enterResponse.over) {
|
||
|
hitMode = "over";
|
||
|
}
|
||
|
// Prevent no-ops like 'before source node'
|
||
|
// TODO: these are no-ops when moving nodes, but not in copy mode
|
||
|
if (dnd.preventVoidMoves) {
|
||
|
if (node === otherNode) {
|
||
|
this.debug(
|
||
|
" drop over source node prevented"
|
||
|
);
|
||
|
hitMode = null;
|
||
|
} else if (
|
||
|
hitMode === "before" &&
|
||
|
otherNode &&
|
||
|
node === otherNode.getNextSibling()
|
||
|
) {
|
||
|
this.debug(
|
||
|
" drop after source node prevented"
|
||
|
);
|
||
|
hitMode = null;
|
||
|
} else if (
|
||
|
hitMode === "after" &&
|
||
|
otherNode &&
|
||
|
node === otherNode.getPrevSibling()
|
||
|
) {
|
||
|
this.debug(
|
||
|
" drop before source node prevented"
|
||
|
);
|
||
|
hitMode = null;
|
||
|
} else if (
|
||
|
hitMode === "over" &&
|
||
|
otherNode &&
|
||
|
otherNode.parent === node &&
|
||
|
otherNode.isLastSibling()
|
||
|
) {
|
||
|
this.debug(
|
||
|
" drop last child over own parent prevented"
|
||
|
);
|
||
|
hitMode = null;
|
||
|
}
|
||
|
}
|
||
|
// this.debug("hitMode: %s - %s - %s", hitMode, (node.parent === otherNode), node.isLastSibling());
|
||
|
ui.helper.data("hitMode", hitMode);
|
||
|
}
|
||
|
// Auto-expand node (only when 'over' the node, not 'before', or 'after')
|
||
|
if (
|
||
|
hitMode !== "before" &&
|
||
|
hitMode !== "after" &&
|
||
|
dnd.autoExpandMS &&
|
||
|
node.hasChildren() !== false &&
|
||
|
!node.expanded &&
|
||
|
(!dnd.dragExpand || dnd.dragExpand(node, ctx) !== false)
|
||
|
) {
|
||
|
node.scheduleAction("expand", dnd.autoExpandMS);
|
||
|
}
|
||
|
if (hitMode && dnd.dragOver) {
|
||
|
// TODO: http://code.google.com/p/dynatree/source/detail?r=625
|
||
|
ctx.hitMode = hitMode;
|
||
|
res = dnd.dragOver(node, ctx);
|
||
|
}
|
||
|
accept = res !== false && hitMode !== null;
|
||
|
if (dnd.smartRevert) {
|
||
|
draggable.options.revert = !accept;
|
||
|
}
|
||
|
this._local._setDndStatus(
|
||
|
otherNode,
|
||
|
node,
|
||
|
ui.helper,
|
||
|
hitMode,
|
||
|
accept
|
||
|
);
|
||
|
break;
|
||
|
|
||
|
case "drop":
|
||
|
hitMode = ui.helper.data("hitMode");
|
||
|
if (hitMode && dnd.dragDrop) {
|
||
|
ctx.hitMode = hitMode;
|
||
|
dnd.dragDrop(node, ctx);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "leave":
|
||
|
// Cancel pending expand request
|
||
|
node.scheduleAction("cancel");
|
||
|
ui.helper.data("enterResponse", null);
|
||
|
ui.helper.data("hitMode", null);
|
||
|
this._local._setDndStatus(
|
||
|
otherNode,
|
||
|
node,
|
||
|
ui.helper,
|
||
|
"out",
|
||
|
undefined
|
||
|
);
|
||
|
if (dnd.dragLeave) {
|
||
|
dnd.dragLeave(node, ctx);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "stop":
|
||
|
$nodeTag.removeClass("fancytree-drag-source");
|
||
|
$(document).off(".fancytree-dnd");
|
||
|
if (dnd.dragStop) {
|
||
|
dnd.dragStop(node, ctx);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
$.error("Unsupported drag event: " + eventName);
|
||
|
}
|
||
|
return res;
|
||
|
},
|
||
|
|
||
|
_cancelDrag: function () {
|
||
|
var dd = $.ui.ddmanager.current;
|
||
|
if (dd) {
|
||
|
dd.cancel();
|
||
|
}
|
||
|
},
|
||
|
});
|
||
|
// Value returned by `require('jquery.fancytree..')`
|
||
|
return $.ui.fancytree;
|
||
|
}); // End of closure
|