1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Move external drag/drop to clipboard module

This commit is contained in:
Nick O'Leary 2015-03-14 22:53:31 +00:00
parent 1c2be579d9
commit 42730b8fce
3 changed files with 368 additions and 369 deletions

View File

@ -499,282 +499,277 @@ RED.nodes = (function() {
} }
function importNodes(newNodesObj,createNewIds) { function importNodes(newNodesObj,createNewIds) {
try { var i;
var i; var n;
var n; var newNodes;
var newNodes; if (typeof newNodesObj === "string") {
if (typeof newNodesObj === "string") { if (newNodesObj === "") {
if (newNodesObj === "") { return;
return; }
} try {
newNodes = JSON.parse(newNodesObj); newNodes = JSON.parse(newNodesObj);
} catch(err) {
var e = new Error("Invalid flow: "+err.message);
e.code = "NODE_RED";
throw e;
}
} else {
newNodes = newNodesObj;
}
if (!$.isArray(newNodes)) {
newNodes = [newNodes];
}
var unknownTypes = [];
for (i=0;i<newNodes.length;i++) {
n = newNodes[i];
// TODO: remove workspace in next release+1
if (n.type != "workspace" &&
n.type != "tab" &&
n.type != "subflow" &&
!registry.getNodeType(n.type) &&
n.type.substring(0,8) != "subflow:") {
// TODO: get this UI thing out of here! (see below as well)
if (unknownTypes.indexOf(n.type)==-1) {
unknownTypes.push(n.type);
}
//if (n.x == null && n.y == null) {
// // config node - remove it
// newNodes.splice(i,1);
// i--;
//}
}
}
if (unknownTypes.length > 0) {
var typeList = "<ul><li>"+unknownTypes.join("</li><li>")+"</li></ul>";
var type = "type"+(unknownTypes.length > 1?"s":"");
RED.notify("<strong>Imported unrecognised "+type+":</strong>"+typeList,"error",false,10000);
//"DO NOT DEPLOY while in this state.<br/>Either, add missing types to Node-RED, restart and then reload page,<br/>or delete unknown "+n.name+", rewire as required, and then deploy.","error");
}
var activeWorkspace = RED.workspaces.active();
var activeSubflow = getSubflow(activeWorkspace);
if (activeSubflow) {
for (i=0;i<newNodes.length;i++) {
var m = /^subflow:(.+)$/.exec(newNodes[i].type);
if (m) {
var subflowId = m[1];
var err;
if (subflowId === activeSubflow.id) {
err = new Error("Cannot add subflow to itself");
}
if (subflowContains(m[1],activeSubflow.id)) {
err = new Error("Cannot add subflow - circular reference detected");
}
if (err) {
// TODO: standardise error codes
err.code = "NODE_RED";
throw err;
}
}
}
}
var new_workspaces = [];
var workspace_map = {};
var new_subflows = [];
var subflow_map = {};
var nid;
var def;
for (i=0;i<newNodes.length;i++) {
n = newNodes[i];
// TODO: remove workspace in next release+1
if (n.type === "workspace" || n.type === "tab") {
if (n.type === "workspace") {
n.type = "tab";
}
if (defaultWorkspace == null) {
defaultWorkspace = n;
}
if (createNewIds) {
nid = getID();
workspace_map[n.id] = nid;
n.id = nid;
}
addWorkspace(n);
RED.workspaces.add(n);
new_workspaces.push(n);
} else if (n.type === "subflow") {
subflow_map[n.id] = n;
if (createNewIds) {
nid = getID();
n.id = nid;
}
// TODO: handle createNewIds - map old to new subflow ids
n.in.forEach(function(input,i) {
input.type = "subflow";
input.direction = "in";
input.z = n.id;
input.i = i;
input.id = getID();
});
n.out.forEach(function(output,i) {
output.type = "subflow";
output.direction = "out";
output.z = n.id;
output.i = i;
output.id = getID();
});
new_subflows.push(n);
addSubflow(n);
} else { } else {
newNodes = newNodesObj; def = registry.getNodeType(n.type);
} if (def && def.category == "config") {
if (!RED.nodes.node(n.id)) {
if (!$.isArray(newNodes)) { var configNode = {id:n.id,type:n.type,users:[]};
newNodes = [newNodes]; for (var d in def.defaults) {
} if (def.defaults.hasOwnProperty(d)) {
var unknownTypes = []; configNode[d] = n[d];
for (i=0;i<newNodes.length;i++) {
n = newNodes[i];
// TODO: remove workspace in next release+1
if (n.type != "workspace" &&
n.type != "tab" &&
n.type != "subflow" &&
!registry.getNodeType(n.type) &&
n.type.substring(0,8) != "subflow:") {
// TODO: get this UI thing out of here! (see below as well)
if (unknownTypes.indexOf(n.type)==-1) {
unknownTypes.push(n.type);
}
//if (n.x == null && n.y == null) {
// // config node - remove it
// newNodes.splice(i,1);
// i--;
//}
}
}
if (unknownTypes.length > 0) {
var typeList = "<ul><li>"+unknownTypes.join("</li><li>")+"</li></ul>";
var type = "type"+(unknownTypes.length > 1?"s":"");
RED.notify("<strong>Imported unrecognised "+type+":</strong>"+typeList,"error",false,10000);
//"DO NOT DEPLOY while in this state.<br/>Either, add missing types to Node-RED, restart and then reload page,<br/>or delete unknown "+n.name+", rewire as required, and then deploy.","error");
}
var activeWorkspace = RED.workspaces.active();
var activeSubflow = getSubflow(activeWorkspace);
if (activeSubflow) {
for (i=0;i<newNodes.length;i++) {
var m = /^subflow:(.+)$/.exec(newNodes[i].type);
if (m) {
var subflowId = m[1];
var err;
if (subflowId === activeSubflow.id) {
err = new Error("Cannot add subflow to itself");
}
if (subflowContains(m[1],activeSubflow.id)) {
err = new Error("Cannot add subflow - circular reference detected");
}
if (err) {
// TODO: standardise error codes
err.code = "NODE_RED";
throw err;
}
}
}
}
var new_workspaces = [];
var workspace_map = {};
var new_subflows = [];
var subflow_map = {};
var nid;
var def;
for (i=0;i<newNodes.length;i++) {
n = newNodes[i];
// TODO: remove workspace in next release+1
if (n.type === "workspace" || n.type === "tab") {
if (n.type === "workspace") {
n.type = "tab";
}
if (defaultWorkspace == null) {
defaultWorkspace = n;
}
if (createNewIds) {
nid = getID();
workspace_map[n.id] = nid;
n.id = nid;
}
addWorkspace(n);
RED.workspaces.add(n);
new_workspaces.push(n);
} else if (n.type === "subflow") {
subflow_map[n.id] = n;
if (createNewIds) {
nid = getID();
n.id = nid;
}
// TODO: handle createNewIds - map old to new subflow ids
n.in.forEach(function(input,i) {
input.type = "subflow";
input.direction = "in";
input.z = n.id;
input.i = i;
input.id = getID();
});
n.out.forEach(function(output,i) {
output.type = "subflow";
output.direction = "out";
output.z = n.id;
output.i = i;
output.id = getID();
});
new_subflows.push(n);
addSubflow(n);
} else {
def = registry.getNodeType(n.type);
if (def && def.category == "config") {
if (!RED.nodes.node(n.id)) {
var configNode = {id:n.id,type:n.type,users:[]};
for (var d in def.defaults) {
if (def.defaults.hasOwnProperty(d)) {
configNode[d] = n[d];
}
} }
configNode.label = def.label;
configNode._def = def;
RED.nodes.add(configNode);
} }
configNode.label = def.label;
configNode._def = def;
RED.nodes.add(configNode);
} }
} }
} }
if (defaultWorkspace == null) { }
defaultWorkspace = { type:"tab", id:getID(), label:"Sheet 1" }; if (defaultWorkspace == null) {
addWorkspace(defaultWorkspace); defaultWorkspace = { type:"tab", id:getID(), label:"Sheet 1" };
RED.workspaces.add(defaultWorkspace); addWorkspace(defaultWorkspace);
new_workspaces.push(defaultWorkspace); RED.workspaces.add(defaultWorkspace);
activeWorkspace = RED.workspaces.active(); new_workspaces.push(defaultWorkspace);
} activeWorkspace = RED.workspaces.active();
}
var node_map = {}; var node_map = {};
var new_nodes = []; var new_nodes = [];
var new_links = []; var new_links = [];
for (i=0;i<newNodes.length;i++) { for (i=0;i<newNodes.length;i++) {
n = newNodes[i]; n = newNodes[i];
// TODO: remove workspace in next release+1 // TODO: remove workspace in next release+1
if (n.type !== "workspace" && n.type !== "tab" && n.type !== "subflow") { if (n.type !== "workspace" && n.type !== "tab" && n.type !== "subflow") {
def = registry.getNodeType(n.type); def = registry.getNodeType(n.type);
if (!def || def.category != "config") { if (!def || def.category != "config") {
var node = {x:n.x,y:n.y,z:n.z,type:0,wires:n.wires,changed:false}; var node = {x:n.x,y:n.y,z:n.z,type:0,wires:n.wires,changed:false};
if (createNewIds) { if (createNewIds) {
if (subflow_map[node.z]) { if (subflow_map[node.z]) {
node.z = subflow_map[node.z].id; node.z = subflow_map[node.z].id;
} else {
node.z = workspace_map[node.z];
if (!workspaces[node.z]) {
node.z = activeWorkspace;
}
}
node.id = getID();
} else { } else {
node.id = n.id; node.z = workspace_map[node.z];
if (node.z == null || (!workspaces[node.z] && !subflow_map[node.z])) { if (!workspaces[node.z]) {
node.z = activeWorkspace; node.z = activeWorkspace;
} }
} }
node.type = n.type; node.id = getID();
node._def = def; } else {
if (n.type.substring(0,7) === "subflow") { node.id = n.id;
var parentId = n.type.split(":")[1]; if (node.z == null || (!workspaces[node.z] && !subflow_map[node.z])) {
var subflow = subflow_map[parentId]||getSubflow(parentId); node.z = activeWorkspace;
if (createNewIds) { }
parentId = subflow.id; }
node.type = "subflow:"+parentId; node.type = n.type;
node._def = registry.getNodeType(node.type); node._def = def;
delete node.i; if (n.type.substring(0,7) === "subflow") {
} var parentId = n.type.split(":")[1];
node.name = n.name; var subflow = subflow_map[parentId]||getSubflow(parentId);
node.outputs = subflow.out.length; if (createNewIds) {
node.inputs = subflow.in.length; parentId = subflow.id;
} else { node.type = "subflow:"+parentId;
if (!node._def) { node._def = registry.getNodeType(node.type);
if (node.x && node.y) { delete node.i;
node._def = { }
color:"#fee", node.name = n.name;
defaults: {}, node.outputs = subflow.out.length;
label: "unknown: "+n.type, node.inputs = subflow.in.length;
labelStyle: "node_label_italic", } else {
outputs: n.outputs||n.wires.length if (!node._def) {
} if (node.x && node.y) {
} else { node._def = {
node._def = { color:"#fee",
category:"config" defaults: {},
}; label: "unknown: "+n.type,
node.users = []; labelStyle: "node_label_italic",
} outputs: n.outputs||n.wires.length
var orig = {}; }
for (var p in n) { } else {
if (n.hasOwnProperty(p) && p!="x" && p!="y" && p!="z" && p!="id" && p!="wires") { node._def = {
orig[p] = n[p]; category:"config"
} };
} node.users = [];
node._orig = orig; }
node.name = n.type; var orig = {};
node.type = "unknown"; for (var p in n) {
} if (n.hasOwnProperty(p) && p!="x" && p!="y" && p!="z" && p!="id" && p!="wires") {
if (node._def.category != "config") { orig[p] = n[p];
node.inputs = n.inputs||node._def.inputs; }
node.outputs = n.outputs||node._def.outputs; }
for (var d2 in node._def.defaults) { node._orig = orig;
if (node._def.defaults.hasOwnProperty(d2)) { node.name = n.type;
node[d2] = n[d2]; node.type = "unknown";
}
}
}
} }
addNode(node);
RED.editor.validateNode(node);
node_map[n.id] = node;
if (node._def.category != "config") { if (node._def.category != "config") {
new_nodes.push(node); node.inputs = n.inputs||node._def.inputs;
node.outputs = n.outputs||node._def.outputs;
for (var d2 in node._def.defaults) {
if (node._def.defaults.hasOwnProperty(d2)) {
node[d2] = n[d2];
}
}
} }
} }
addNode(node);
RED.editor.validateNode(node);
node_map[n.id] = node;
if (node._def.category != "config") {
new_nodes.push(node);
}
} }
} }
for (i=0;i<new_nodes.length;i++) { }
n = new_nodes[i]; for (i=0;i<new_nodes.length;i++) {
for (var w1=0;w1<n.wires.length;w1++) { n = new_nodes[i];
var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]]; for (var w1=0;w1<n.wires.length;w1++) {
for (var w2=0;w2<wires.length;w2++) { var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]];
if (wires[w2] in node_map) { for (var w2=0;w2<wires.length;w2++) {
var link = {source:n,sourcePort:w1,target:node_map[wires[w2]]}; if (wires[w2] in node_map) {
addLink(link); var link = {source:n,sourcePort:w1,target:node_map[wires[w2]]};
new_links.push(link); addLink(link);
} new_links.push(link);
} }
} }
delete n.wires;
} }
for (i=0;i<new_subflows.length;i++) { delete n.wires;
n = new_subflows[i]; }
n.in.forEach(function(input) { for (i=0;i<new_subflows.length;i++) {
input.wires.forEach(function(wire) { n = new_subflows[i];
var link = {source:input, sourcePort:0, target:node_map[wire.id]}; n.in.forEach(function(input) {
addLink(link); input.wires.forEach(function(wire) {
new_links.push(link); var link = {source:input, sourcePort:0, target:node_map[wire.id]};
}); addLink(link);
delete input.wires; new_links.push(link);
}); });
n.out.forEach(function(output) { delete input.wires;
output.wires.forEach(function(wire) { });
var link; n.out.forEach(function(output) {
if (wire.id == n.id) { output.wires.forEach(function(wire) {
link = {source:n.in[wire.port], sourcePort:wire.port,target:output}; var link;
} else { if (wire.id == n.id) {
link = {source:node_map[wire.id], sourcePort:wire.port,target:output}; link = {source:n.in[wire.port], sourcePort:wire.port,target:output};
} } else {
addLink(link); link = {source:node_map[wire.id], sourcePort:wire.port,target:output};
new_links.push(link); }
}); addLink(link);
delete output.wires; new_links.push(link);
}); });
} delete output.wires;
});
return [new_nodes,new_links,new_workspaces,new_subflows];
} catch(error) {
if (error.code != "NODE_RED") {
console.log(error.stack);
RED.notify("<strong>Error</strong>: "+error,"error");
} else {
RED.notify("<strong>Error</strong>: "+error.message,"error");
}
return null;
} }
return [new_nodes,new_links,new_workspaces,new_subflows];
} }
return { return {

View File

@ -120,6 +120,11 @@ RED.clipboard = (function() {
} }
} }
function hideDropTarget() {
$("#dropTarget").hide();
RED.keyboard.remove(/* ESCAPE */ 27);
}
return { return {
init: function() { init: function() {
RED.view.on("selection-changed",function(selection) { RED.view.on("selection-changed",function(selection) {
@ -135,6 +140,32 @@ RED.clipboard = (function() {
}); });
RED.keyboard.add(/* e */ 69,{ctrl:true},function(){exportNodes();d3.event.preventDefault();}); RED.keyboard.add(/* e */ 69,{ctrl:true},function(){exportNodes();d3.event.preventDefault();});
RED.keyboard.add(/* i */ 73,{ctrl:true},function(){importNodes();d3.event.preventDefault();}); RED.keyboard.add(/* i */ 73,{ctrl:true},function(){importNodes();d3.event.preventDefault();});
$('#chart').on("dragenter",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
$("#dropTarget").css({display:'table'});
RED.keyboard.add(/* ESCAPE */ 27,hideDropTarget);
}
});
$('#dropTarget').on("dragover",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
event.preventDefault();
}
})
.on("dragleave",function(event) {
hideDropTarget();
})
.on("drop",function(event) {
var data = event.originalEvent.dataTransfer.getData("text/plain");
hideDropTarget();
RED.view.importNodes(data);
event.preventDefault();
});
}, },
import: importNodes, import: importNodes,
export: exportNodes export: exportNodes

View File

@ -282,8 +282,100 @@ RED.view = (function() {
updateSelection(); updateSelection();
updateActiveNodes(); updateActiveNodes();
redraw(); redraw();
}); });
$('#btn-zoom-out').click(function() {zoomOut();});
$('#btn-zoom-zero').click(function() {zoomZero();});
$('#btn-zoom-in').click(function() {zoomIn();});
$("#chart").on('DOMMouseScroll mousewheel', function (evt) {
if ( evt.altKey ) {
evt.preventDefault();
evt.stopPropagation();
var move = -(evt.originalEvent.detail) || evt.originalEvent.wheelDelta;
if (move <= 0) { zoomOut(); }
else { zoomIn(); }
}
});
// Handle nodes dragged from the palette
$("#chart").droppable({
accept:".palette_node",
drop: function( event, ui ) {
d3.event = event;
var selected_tool = ui.draggable[0].type;
var m = /^subflow:(.+)$/.exec(selected_tool);
if (activeSubflow && m) {
var subflowId = m[1];
if (subflowId === activeSubflow.id) {
RED.notify("<strong>Error</strong>: Cannot add subflow to itself","error");
return;
}
if (RED.nodes.subflowContains(m[1],activeSubflow.id)) {
RED.notify("<strong>Error</strong>: Cannot add subflow - circular reference detected","error");
return;
}
}
var mousePos = d3.touches(this)[0]||d3.mouse(this);
mousePos[1] += this.scrollTop;
mousePos[0] += this.scrollLeft;
mousePos[1] /= scaleFactor;
mousePos[0] /= scaleFactor;
var nn = { id:(1+Math.random()*4294967295).toString(16),x: mousePos[0],y:mousePos[1],w:node_width,z:RED.workspaces.active()};
nn.type = selected_tool;
nn._def = RED.nodes.getType(nn.type);
if (!m) {
nn.inputs = nn._def.inputs || 0;
nn.outputs = nn._def.outputs;
nn.changed = true;
for (var d in nn._def.defaults) {
if (nn._def.defaults.hasOwnProperty(d)) {
nn[d] = nn._def.defaults[d].value;
}
}
if (nn._def.onadd) {
nn._def.onadd.call(nn);
}
} else {
var subflow = RED.nodes.subflow(m[1]);
nn.inputs = subflow.in.length;
nn.outputs = subflow.out.length;
}
nn.h = Math.max(node_height,(nn.outputs||0) * 15);
RED.history.push({t:'add',nodes:[nn.id],dirty:dirty});
RED.nodes.add(nn);
RED.editor.validateNode(nn);
setDirty(true);
// auto select dropped node - so info shows (if visible)
clearSelection();
nn.selected = true;
moving_set.push({n:nn});
updateActiveNodes();
updateSelection();
redraw();
if (nn._def.autoedit) {
RED.editor.edit(nn);
}
}
});
RED.keyboard.add(/* z */ 90,{ctrl:true},function(){RED.history.pop();});
RED.keyboard.add(/* a */ 65,{ctrl:true},function(){selectAll();d3.event.preventDefault();});
RED.keyboard.add(/* = */ 187,{ctrl:true},function(){zoomIn();d3.event.preventDefault();});
RED.keyboard.add(/* - */ 189,{ctrl:true},function(){zoomOut();d3.event.preventDefault();});
RED.keyboard.add(/* 0 */ 48,{ctrl:true},function(){zoomZero();d3.event.preventDefault();});
RED.keyboard.add(/* v */ 86,{ctrl:true},function(){importNodes(clipboard);d3.event.preventDefault();});
} }
function canvasMouseDown() { function canvasMouseDown() {
@ -517,90 +609,6 @@ RED.view = (function() {
redraw(); redraw();
} }
$('#btn-zoom-out').click(function() {zoomOut();});
$('#btn-zoom-zero').click(function() {zoomZero();});
$('#btn-zoom-in').click(function() {zoomIn();});
$("#chart").on('DOMMouseScroll mousewheel', function (evt) {
if ( evt.altKey ) {
evt.preventDefault();
evt.stopPropagation();
var move = -(evt.originalEvent.detail) || evt.originalEvent.wheelDelta;
if (move <= 0) { zoomOut(); }
else { zoomIn(); }
}
});
$("#chart").droppable({
accept:".palette_node",
drop: function( event, ui ) {
d3.event = event;
var selected_tool = ui.draggable[0].type;
var m = /^subflow:(.+)$/.exec(selected_tool);
if (activeSubflow && m) {
var subflowId = m[1];
if (subflowId === activeSubflow.id) {
RED.notify("<strong>Error</strong>: Cannot add subflow to itself","error");
return;
}
if (RED.nodes.subflowContains(m[1],activeSubflow.id)) {
RED.notify("<strong>Error</strong>: Cannot add subflow - circular reference detected","error");
return;
}
}
var mousePos = d3.touches(this)[0]||d3.mouse(this);
mousePos[1] += this.scrollTop;
mousePos[0] += this.scrollLeft;
mousePos[1] /= scaleFactor;
mousePos[0] /= scaleFactor;
var nn = { id:(1+Math.random()*4294967295).toString(16),x: mousePos[0],y:mousePos[1],w:node_width,z:RED.workspaces.active()};
nn.type = selected_tool;
nn._def = RED.nodes.getType(nn.type);
if (!m) {
nn.inputs = nn._def.inputs || 0;
nn.outputs = nn._def.outputs;
nn.changed = true;
for (var d in nn._def.defaults) {
if (nn._def.defaults.hasOwnProperty(d)) {
nn[d] = nn._def.defaults[d].value;
}
}
if (nn._def.onadd) {
nn._def.onadd.call(nn);
}
} else {
var subflow = RED.nodes.subflow(m[1]);
nn.inputs = subflow.in.length;
nn.outputs = subflow.out.length;
}
nn.h = Math.max(node_height,(nn.outputs||0) * 15);
RED.history.push({t:'add',nodes:[nn.id],dirty:dirty});
RED.nodes.add(nn);
RED.editor.validateNode(nn);
setDirty(true);
// auto select dropped node - so info shows (if visible)
clearSelection();
nn.selected = true;
moving_set.push({n:nn});
updateActiveNodes();
updateSelection();
redraw();
if (nn._def.autoedit) {
RED.editor.edit(nn);
}
}
});
function zoomIn() { function zoomIn() {
if (scaleFactor < 2) { if (scaleFactor < 2) {
scaleFactor += 0.1; scaleFactor += 0.1;
@ -1638,13 +1646,6 @@ RED.view = (function() {
} }
RED.keyboard.add(/* z */ 90,{ctrl:true},function(){RED.history.pop();});
RED.keyboard.add(/* a */ 65,{ctrl:true},function(){selectAll();d3.event.preventDefault();});
RED.keyboard.add(/* = */ 187,{ctrl:true},function(){zoomIn();d3.event.preventDefault();});
RED.keyboard.add(/* - */ 189,{ctrl:true},function(){zoomOut();d3.event.preventDefault();});
RED.keyboard.add(/* 0 */ 48,{ctrl:true},function(){zoomZero();d3.event.preventDefault();});
RED.keyboard.add(/* v */ 86,{ctrl:true},function(){importNodes(clipboard);d3.event.preventDefault();});
// TODO: 'dirty' should be a property of RED.nodes - with an event callback for ui hooks // TODO: 'dirty' should be a property of RED.nodes - with an event callback for ui hooks
function setDirty(d) { function setDirty(d) {
dirty = d; dirty = d;
@ -1746,34 +1747,6 @@ RED.view = (function() {
} }
} }
function hideDropTarget() {
$("#dropTarget").hide();
RED.keyboard.remove(/* ESCAPE */ 27);
}
$('#chart').on("dragenter",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
$("#dropTarget").css({display:'table'});
RED.keyboard.add(/* ESCAPE */ 27,hideDropTarget);
}
});
$('#dropTarget').on("dragover",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
event.preventDefault();
}
})
.on("dragleave",function(event) {
hideDropTarget();
})
.on("drop",function(event) {
var data = event.originalEvent.dataTransfer.getData("text/plain");
hideDropTarget();
RED.view.importNodes(data);
event.preventDefault();
});
// TODO: DRY // TODO: DRY
var eventHandler = (function() { var eventHandler = (function() {
var handlers = {}; var handlers = {};