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

Add Junctions

This commit is contained in:
Nick O'Leary 2022-02-28 12:53:11 +00:00
parent 6a5c50ff77
commit db1ad0df63
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
13 changed files with 442 additions and 31 deletions

View File

@ -106,6 +106,23 @@ RED.history = (function() {
RED.nodes.removeLink(ev.links[i]); RED.nodes.removeLink(ev.links[i]);
} }
} }
if (ev.junctions) {
inverseEv.junctions = [];
for (i=0;i<ev.junctions.length;i++) {
inverseEv.junctions.push(ev.junctions[i]);
RED.nodes.removeJunction(ev.junctions[i]);
if (ev.junctions[i].g) {
var group = RED.nodes.group(ev.junctions[i].g);
var index = group.nodes.indexOf(ev.junctions[i]);
if (index !== -1) {
group.nodes.splice(index,1);
RED.group.markDirty(group);
}
}
}
}
if (ev.groups) { if (ev.groups) {
inverseEv.groups = []; inverseEv.groups = [];
for (i = ev.groups.length - 1;i>=0;i--) { for (i = ev.groups.length - 1;i>=0;i--) {
@ -272,6 +289,21 @@ RED.history = (function() {
} }
} }
} }
if (ev.junctions) {
inverseEv.junctions = [];
for (i=0;i<ev.junctions.length;i++) {
inverseEv.junctions.push(ev.junctions[i]);
RED.nodes.addJunction(ev.junctions[i]);
if (ev.junctions[i].g) {
group = RED.nodes.group(ev.junctions[i].g);
if (group.nodes.indexOf(ev.junctions[i]) === -1) {
group.nodes.push(ev.junctions[i]);
}
RED.group.markDirty(group)
}
}
}
if (ev.links) { if (ev.links) {
inverseEv.links = []; inverseEv.links = [];
for (i=0;i<ev.links.length;i++) { for (i=0;i<ev.links.length;i++) {

View File

@ -38,6 +38,9 @@ RED.nodes = (function() {
var groups = {}; var groups = {};
var groupsByZ = {}; var groupsByZ = {};
var junctions = {};
var junctionsByZ = {};
var initialLoad; var initialLoad;
var dirty = false; var dirty = false;
@ -819,6 +822,7 @@ RED.nodes = (function() {
var removedNodes = []; var removedNodes = [];
var removedLinks = []; var removedLinks = [];
var removedGroups = []; var removedGroups = [];
var removedJunctions = [];
if (ws) { if (ws) {
delete workspaces[id]; delete workspaces[id];
delete linkTabMap[id]; delete linkTabMap[id];
@ -827,7 +831,14 @@ RED.nodes = (function() {
var node; var node;
if (allNodes.hasTab(id)) { if (allNodes.hasTab(id)) {
removedNodes = allNodes.getNodes(id).slice() removedNodes = allNodes.getNodes(id).filter(n => {
if (n.type === 'junction') {
removedJunctions.push(n)
return false
} else {
return true
}
})
} }
for (i in configNodes) { for (i in configNodes) {
if (configNodes.hasOwnProperty(i)) { if (configNodes.hasOwnProperty(i)) {
@ -842,6 +853,10 @@ RED.nodes = (function() {
var result = removeNode(removedNodes[i].id); var result = removeNode(removedNodes[i].id);
removedLinks = removedLinks.concat(result.links); removedLinks = removedLinks.concat(result.links);
} }
for (i=0;i<removedJunctions.length;i++) {
var result = removeJunction(removedJunctions[i])
removedLinks = removedLinks.concat(result.links)
}
// Must get 'removedGroups' in the right order. // Must get 'removedGroups' in the right order.
// - start with the top-most groups // - start with the top-most groups
@ -861,7 +876,7 @@ RED.nodes = (function() {
allNodes.removeTab(id); allNodes.removeTab(id);
RED.events.emit('flows:remove',ws); RED.events.emit('flows:remove',ws);
} }
return {nodes:removedNodes,links:removedLinks, groups: removedGroups}; return {nodes:removedNodes,links:removedLinks, groups: removedGroups, junctions: removedJunctions};
} }
function addSubflow(sf, createNewIds) { function addSubflow(sf, createNewIds) {
@ -1118,7 +1133,7 @@ RED.nodes = (function() {
delete node.env; delete node.env;
} }
} }
if (n._def.category != "config") { if (n._def.category != "config" || n.type === 'junction') {
node.x = n.x; node.x = n.x;
node.y = n.y; node.y = n.y;
if (exportDimensions) { if (exportDimensions) {
@ -1381,6 +1396,11 @@ RED.nodes = (function() {
nns.push(convertNode(groups[i], opts)); nns.push(convertNode(groups[i], opts));
} }
} }
for (i in junctions) {
if (junctions.hasOwnProperty(i)) {
nns.push(convertNode(junctions[i], opts));
}
}
for (i in configNodes) { for (i in configNodes) {
if (configNodes.hasOwnProperty(i)) { if (configNodes.hasOwnProperty(i)) {
nns.push(convertNode(configNodes[i], opts)); nns.push(convertNode(configNodes[i], opts));
@ -1462,6 +1482,7 @@ RED.nodes = (function() {
tabs: {}, tabs: {},
subflows: {}, subflows: {},
groups: {}, groups: {},
junctions: {},
configs: {}, configs: {},
nodes: {}, nodes: {},
all: [], all: [],
@ -1477,6 +1498,8 @@ RED.nodes = (function() {
imported.subflows[n.id] = n; imported.subflows[n.id] = n;
} else if (n.type === "group") { } else if (n.type === "group") {
imported.groups[n.id] = n; imported.groups[n.id] = n;
} else if (n.type === "junction") {
imported.junctions[n.id] = n;
} else if (n.hasOwnProperty("x") && n.hasOwnProperty("y")) { } else if (n.hasOwnProperty("x") && n.hasOwnProperty("y")) {
imported.nodes[n.id] = n; imported.nodes[n.id] = n;
} else { } else {
@ -1485,7 +1508,7 @@ RED.nodes = (function() {
var nodeZ = n.z || "__global__"; var nodeZ = n.z || "__global__";
imported.zMap[nodeZ] = imported.zMap[nodeZ] || []; imported.zMap[nodeZ] = imported.zMap[nodeZ] || [];
imported.zMap[nodeZ].push(n) imported.zMap[nodeZ].push(n)
if (allNodes.hasNode(n.id) || configNodes[n.id] || workspaces[n.id] || subflows[n.id] || groups[n.id]) { if (allNodes.hasNode(n.id) || configNodes[n.id] || workspaces[n.id] || subflows[n.id] || groups[n.id] || junctions[n.id]) {
imported.conflicted[n.id] = n; imported.conflicted[n.id] = n;
} }
}) })
@ -1651,7 +1674,7 @@ RED.nodes = (function() {
if (!options.generateIds) { if (!options.generateIds) {
if (!options.importMap[id]) { if (!options.importMap[id]) {
// No conflict resolution for this node // No conflict resolution for this node
var existing = allNodes.getNode(id) || configNodes[id] || workspaces[id] || subflows[id] || groups[id]; var existing = allNodes.getNode(id) || configNodes[id] || workspaces[id] || subflows[id] || groups[id] || junctions[id];
if (existing) { if (existing) {
existingNodes.push({existing:existing, imported:n}); existingNodes.push({existing:existing, imported:n});
} }
@ -1705,6 +1728,7 @@ RED.nodes = (function() {
n.type != "tab" && n.type != "tab" &&
n.type != "subflow" && n.type != "subflow" &&
n.type != "group" && n.type != "group" &&
n.type != 'junction' &&
!registry.getNodeType(n.type) && !registry.getNodeType(n.type) &&
n.type.substring(0,8) != "subflow:" && n.type.substring(0,8) != "subflow:" &&
unknownTypes.indexOf(n.type)==-1) { unknownTypes.indexOf(n.type)==-1) {
@ -1777,6 +1801,7 @@ RED.nodes = (function() {
var new_nodes = []; var new_nodes = [];
var new_links = []; var new_links = [];
var new_groups = []; var new_groups = [];
var new_junctions = [];
var new_group_set = new Set(); var new_group_set = new Set();
var nid; var nid;
var def; var def;
@ -1968,12 +1993,15 @@ RED.nodes = (function() {
changed:false, changed:false,
_config:{} _config:{}
} }
if (n.type !== "group") { if (n.type !== "group" && n.type !== 'junction') {
node.wires = n.wires||[]; node.wires = n.wires||[];
node.inputLabels = n.inputLabels; node.inputLabels = n.inputLabels;
node.outputLabels = n.outputLabels; node.outputLabels = n.outputLabels;
node.icon = n.icon; node.icon = n.icon;
} }
if (n.type === 'junction') {
node.wires = n.wires||[];
}
if (n.hasOwnProperty('l')) { if (n.hasOwnProperty('l')) {
node.l = n.l; node.l = n.l;
} }
@ -2042,6 +2070,15 @@ RED.nodes = (function() {
node.outputs = subflow.out.length; node.outputs = subflow.out.length;
node.inputs = subflow.in.length; node.inputs = subflow.in.length;
node.env = n.env; node.env = n.env;
} else if (n.type === 'junction') {
node._def = {defaults:{}}
node._config.x = node.x
node._config.y = node.y
node.inputs = 1
node.outputs = 1
node.w = 0;
node.h = 0;
} else { } else {
if (!node._def) { if (!node._def) {
if (node.x && node.y) { if (node.x && node.y) {
@ -2125,7 +2162,9 @@ RED.nodes = (function() {
node_map[n.id] = node; node_map[n.id] = node;
// If an 'unknown' config node, it will not have been caught by the // If an 'unknown' config node, it will not have been caught by the
// proper config node handling, so needs adding to new_nodes here // proper config node handling, so needs adding to new_nodes here
if (node.type === "unknown" || node._def.category !== "config") { if (node.type === 'junction') {
new_junctions.push(node)
} else if (node.type === "unknown" || node._def.category !== "config") {
new_nodes.push(node); new_nodes.push(node);
} else if (node.type === "group") { } else if (node.type === "group") {
new_groups.push(node); new_groups.push(node);
@ -2136,8 +2175,12 @@ RED.nodes = (function() {
} }
// Remap all wires and config node references // Remap all wires and config node references
for (i=0;i<new_nodes.length;i++) { for (i=0;i<new_nodes.length+new_junctions.length;i++) {
n = new_nodes[i]; if (i<new_nodes.length) {
n = new_nodes[i];
} else {
n = new_junctions[i - new_nodes.length]
}
if (n.wires) { if (n.wires) {
for (var w1=0;w1<n.wires.length;w1++) { for (var w1=0;w1<n.wires.length;w1++) {
var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]]; var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]];
@ -2276,6 +2319,12 @@ RED.nodes = (function() {
addGroup(n); addGroup(n);
} }
for (i=0;i<new_junctions.length;i++) {
var junction = new_junctions[i];
addJunction(junction);
}
// Now the nodes have been fully updated, add them. // Now the nodes have been fully updated, add them.
for (i=0;i<new_nodes.length;i++) { for (i=0;i<new_nodes.length;i++) {
var node = new_nodes[i]; var node = new_nodes[i];
@ -2306,6 +2355,7 @@ RED.nodes = (function() {
nodes:new_nodes, nodes:new_nodes,
links:new_links, links:new_links,
groups:new_groups, groups:new_groups,
junctions: new_junctions,
workspaces:new_workspaces, workspaces:new_workspaces,
subflows:new_subflows, subflows:new_subflows,
missingWorkspace: missingWorkspace, missingWorkspace: missingWorkspace,
@ -2461,6 +2511,30 @@ RED.nodes = (function() {
RED.events.emit("groups:remove",group); RED.events.emit("groups:remove",group);
} }
function addJunction(junction) {
junctionsByZ[junction.z] = junctionsByZ[junction.z] || []
junctionsByZ[junction.z].push(junction)
junctions[junction.id] = junction;
if (!nodeLinks[junction.id]) {
nodeLinks[junction.id] = {in:[],out:[]};
}
RED.events.emit("junctions:add", junction)
}
function removeJunction(junction) {
var i = junctionsByZ[junction.z].indexOf(junction)
junctionsByZ[junction.z].splice(i, 1)
if (junctionsByZ[junction.z].length === 0) {
delete junctionsByZ[junction.z]
}
delete junctions[junction.id]
delete nodeLinks[junction.id];
RED.events.emit("junctions:remove", junction)
var removedLinks = links.filter(function(l) { return (l.source === junction) || (l.target === junction); });
removedLinks.forEach(removeLink);
return { links: removedLinks }
}
function getNodeHelp(type) { function getNodeHelp(type) {
var helpContent = ""; var helpContent = "";
var helpElement = $("script[data-help-name='"+type+"']"); var helpElement = $("script[data-help-name='"+type+"']");
@ -2734,6 +2808,11 @@ RED.nodes = (function() {
group: function(id) { return groups[id] }, group: function(id) { return groups[id] },
groups: function(z) { return groupsByZ[z]?groupsByZ[z].slice():[] }, groups: function(z) { return groupsByZ[z]?groupsByZ[z].slice():[] },
addJunction: addJunction,
removeJunction: removeJunction,
junction: function(id) { return junctions[id] },
junctions: function(z) { return junctionsByZ[z]?junctionsByZ[z].slice():[] },
eachNode: function(cb) { eachNode: function(cb) {
allNodes.eachNode(cb); allNodes.eachNode(cb);
}, },

View File

@ -987,6 +987,7 @@ RED.clipboard = (function() {
try { try {
RED.view.importNodes(newNodes, importOptions); RED.view.importNodes(newNodes, importOptions);
} catch(error) { } catch(error) {
console.log(error.importConfig)
// Thrown for import_conflict // Thrown for import_conflict
confirmImport(error.importConfig, newNodes, importOptions); confirmImport(error.importConfig, newNodes, importOptions);
} }

View File

@ -352,8 +352,10 @@ RED.group = (function() {
} }
if (n.type === 'group') { if (n.type === 'group') {
RED.events.emit("groups:change",n) RED.events.emit("groups:change",n)
} else { } else if (n.type !== 'junction') {
RED.events.emit("nodes:change",n) RED.events.emit("nodes:change",n)
} else {
RED.events.emit("junctions:change",n)
} }
}) })
RED.nodes.removeGroup(g); RED.nodes.removeGroup(g);
@ -547,8 +549,10 @@ RED.group = (function() {
group.h = Math.max(group.h,n.y+n.h/2+25-group.y); group.h = Math.max(group.h,n.y+n.h/2+25-group.y);
if (n.type === 'group') { if (n.type === 'group') {
RED.events.emit("groups:change",n) RED.events.emit("groups:change",n)
} else { } else if (n.type !== 'junction') {
RED.events.emit("nodes:change",n) RED.events.emit("nodes:change",n)
} else {
RED.events.emit("junctions:change",n)
} }
} }
} }
@ -583,8 +587,10 @@ RED.group = (function() {
} }
if (n.type === 'group') { if (n.type === 'group') {
RED.events.emit("groups:change",n) RED.events.emit("groups:change",n)
} else { } else if (n.type !== 'junction') {
RED.events.emit("nodes:change",n) RED.events.emit("nodes:change",n)
} else {
RED.events.emit("junctions:change",n)
} }
} }
markDirty(group); markDirty(group);

View File

@ -29,5 +29,6 @@ RED.state = {
GROUP_DRAGGING: 12, GROUP_DRAGGING: 12,
GROUP_RESIZE: 13, GROUP_RESIZE: 13,
DETACHED_DRAGGING: 14, DETACHED_DRAGGING: 14,
SLICING: 15 SLICING: 15,
SLICING_JUNCTION: 16
} }

View File

@ -370,7 +370,7 @@ RED.sidebar.help = (function() {
var node = selection.nodes[0]; var node = selection.nodes[0];
if (node.type === "subflow" && node.direction) { if (node.type === "subflow" && node.direction) {
// ignore subflow virtual ports // ignore subflow virtual ports
} else if (node.type !== 'group'){ } else if (node.type !== 'group' && node.type !== 'junction'){
showNodeTypeHelp(node.type); showNodeTypeHelp(node.type);
} }
} }

View File

@ -302,8 +302,8 @@ RED.sidebar.info = (function() {
if (typeCounts.groups > 0) { if (typeCounts.groups > 0) {
$('<div>').text(RED._("clipboard.group",{count:typeCounts.groups})).appendTo(counts); $('<div>').text(RED._("clipboard.group",{count:typeCounts.groups})).appendTo(counts);
} }
} else if (node.type === 'junction') {
propertiesPanelHeaderHelp.hide();
} else { } else {
propertiesPanelHeaderHelp.show(); propertiesPanelHeaderHelp.show();

View File

@ -1028,6 +1028,8 @@ RED.utils = (function() {
return "font-awesome/fa-object-ungroup"; return "font-awesome/fa-object-ungroup";
} else if (node && node.type === 'group') { } else if (node && node.type === 'group') {
return "font-awesome/fa-object-group" return "font-awesome/fa-object-group"
} else if (node && node.type === 'junction') {
return "font-awesome/fa-circle-o"
} else if (def.category === 'config') { } else if (def.category === 'config') {
return RED.settings.apiRootUrl+"icons/node-red/cog.svg" return RED.settings.apiRootUrl+"icons/node-red/cog.svg"
} else if (node && node.type === 'tab') { } else if (node && node.type === 'tab') {
@ -1093,6 +1095,8 @@ RED.utils = (function() {
l = node.label || defaultLabel l = node.label || defaultLabel
} else if (node.type === 'group') { } else if (node.type === 'group') {
l = node.name || defaultLabel l = node.name || defaultLabel
} else if (node.type === 'junction') {
l = 'junction'
} else { } else {
l = node._def.label; l = node._def.label;
try { try {
@ -1247,6 +1251,8 @@ RED.utils = (function() {
nodeDiv.addClass("red-ui-palette-icon-selection"); nodeDiv.addClass("red-ui-palette-icon-selection");
} else if (node.type === "group") { } else if (node.type === "group") {
nodeDiv.addClass("red-ui-palette-icon-group"); nodeDiv.addClass("red-ui-palette-icon-group");
} else if (node.type === "junction") {
nodeDiv.addClass("red-ui-palette-icon-junction");
} else if (node.type === 'tab') { } else if (node.type === 'tab') {
nodeDiv.addClass("red-ui-palette-icon-flow"); nodeDiv.addClass("red-ui-palette-icon-flow");
} else { } else {

View File

@ -24,6 +24,7 @@
* |- <g> "groupLayer" * |- <g> "groupLayer"
* |- <g> "groupSelectLayer" * |- <g> "groupSelectLayer"
* |- <g> "linkLayer" * |- <g> "linkLayer"
* |- <g> "junctionLayer"
* |- <g> "dragGroupLayer" * |- <g> "dragGroupLayer"
* |- <g> "nodeLayer" * |- <g> "nodeLayer"
*/ */
@ -56,6 +57,7 @@ RED.view = (function() {
var activeSubflow = null; var activeSubflow = null;
var activeNodes = []; var activeNodes = [];
var activeLinks = []; var activeLinks = [];
var activeJunctions = [];
var activeFlowLinks = []; var activeFlowLinks = [];
var activeLinkNodes = {}; var activeLinkNodes = {};
var activeGroup = null; var activeGroup = null;
@ -111,6 +113,7 @@ RED.view = (function() {
var eventLayer; var eventLayer;
var gridLayer; var gridLayer;
var linkLayer; var linkLayer;
var junctionLayer;
var dragGroupLayer; var dragGroupLayer;
var groupSelectLayer; var groupSelectLayer;
var nodeLayer; var nodeLayer;
@ -199,6 +202,11 @@ RED.view = (function() {
function init() { function init() {
// setTimeout(function() {
// function snap(p) { return RED.view.gridSize() * Math.round(p/RED.view.gridSize())}; for (var i = 0;i<10;i++) {
// RED.nodes.addJunction({_def:{defaults:{}}, type:'junction', z:"0ccdc1d81f2729cc",id:RED.nodes.id(),x:snap(Math.floor(Math.random()*600)),y:snap(Math.floor(Math.random()*600)), w:0,h:0})
// } ; RED.view.redraw(true)
// },2000)
chart = $("#red-ui-workspace-chart"); chart = $("#red-ui-workspace-chart");
outer = d3.select("#red-ui-workspace-chart") outer = d3.select("#red-ui-workspace-chart")
@ -373,6 +381,7 @@ RED.view = (function() {
groupSelectLayer = eventLayer.append("g"); groupSelectLayer = eventLayer.append("g");
linkLayer = eventLayer.append("g"); linkLayer = eventLayer.append("g");
dragGroupLayer = eventLayer.append("g"); dragGroupLayer = eventLayer.append("g");
junctionLayer = eventLayer.append("g");
nodeLayer = eventLayer.append("g"); nodeLayer = eventLayer.append("g");
drag_lines = []; drag_lines = [];
@ -785,7 +794,7 @@ RED.view = (function() {
source:{z:activeWorkspace}, source:{z:activeWorkspace},
target:{z:activeWorkspace} target:{z:activeWorkspace}
}); });
activeJunctions = RED.nodes.junctions(activeWorkspace) || [];
activeGroups = RED.nodes.groups(activeWorkspace)||[]; activeGroups = RED.nodes.groups(activeWorkspace)||[];
activeGroups.forEach(function(g, i) { activeGroups.forEach(function(g, i) {
g._index = i; g._index = i;
@ -800,6 +809,7 @@ RED.view = (function() {
} else { } else {
activeNodes = []; activeNodes = [];
activeLinks = []; activeLinks = [];
activeJunctions = [];
activeGroups = []; activeGroups = [];
} }
@ -970,9 +980,9 @@ RED.view = (function() {
.attr("class","nr-ui-view-lasso"); .attr("class","nr-ui-view-lasso");
d3.event.preventDefault(); d3.event.preventDefault();
} }
} else if (mouse_mode === 0 && d3.event.button === 2 && (d3.event.metaKey || d3.event.ctrlKey)) { } else if (mouse_mode === 0 && d3.event.button === 2 && (d3.event.metaKey || d3.event.ctrlKey || d3.event.shiftKey)) {
clearSelection(); clearSelection();
mouse_mode = RED.state.SLICING; mouse_mode = (d3.event.metaKey || d3.event.ctrlKey)?RED.state.SLICING : RED.state.SLICING_JUNCTION;
point = d3.mouse(this); point = d3.mouse(this);
slicePath = eventLayer.append("path").attr("class","nr-ui-view-slice").attr("d",`M${point[0]} ${point[1]}`) slicePath = eventLayer.append("path").attr("class","nr-ui-view-slice").attr("d",`M${point[0]} ${point[1]}`)
slicePathLast = point; slicePathLast = point;
@ -1373,7 +1383,7 @@ RED.view = (function() {
.attr("height",h) .attr("height",h)
; ;
return; return;
} else if (mouse_mode === RED.state.SLICING) { } else if (mouse_mode === RED.state.SLICING || mouse_mode === RED.state.SLICING_JUNCTION) {
if (slicePath) { if (slicePath) {
var delta = Math.max(1,Math.abs(slicePathLast[0]-mouse_position[0]))*Math.max(1,Math.abs(slicePathLast[1]-mouse_position[1])) var delta = Math.max(1,Math.abs(slicePathLast[0]-mouse_position[0]))*Math.max(1,Math.abs(slicePathLast[1]-mouse_position[1]))
if (delta > 20) { if (delta > 20) {
@ -1739,6 +1749,15 @@ RED.view = (function() {
} }
} }
}); });
activeJunctions.forEach(function(n) {
if (!n.selected) {
if (n.x > x && n.x < x2 && n.y > y && n.y < y2) {
n.selected = true;
n.dirty = true;
movingSet.add(n);
}
}
})
@ -1782,11 +1801,92 @@ RED.view = (function() {
} else if (mouse_mode == RED.state.DEFAULT && mousedown_link == null && !d3.event.ctrlKey && !d3.event.metaKey ) { } else if (mouse_mode == RED.state.DEFAULT && mousedown_link == null && !d3.event.ctrlKey && !d3.event.metaKey ) {
clearSelection(); clearSelection();
updateSelection(); updateSelection();
} else if (slicePath) { } else if (mouse_mode == RED.state.SLICING) {
deleteSelection(); deleteSelection();
slicePath.remove(); slicePath.remove();
slicePath = null; slicePath = null;
RED.view.redraw(true); RED.view.redraw(true);
} else if (mouse_mode == RED.state.SLICING_JUNCTION) {
var removedLinks = []
var addedLinks = []
var addedJunctions = []
var groupedLinks = {}
selectedLinks.forEach(function(l) {
var sourceId = l.source.id+":"+l.sourcePort
groupedLinks[sourceId] = groupedLinks[sourceId] || []
groupedLinks[sourceId].push(l)
});
var linkGroups = Object.keys(groupedLinks)
linkGroups.forEach(function(gid) {
var links = groupedLinks[gid]
var junction = {
_def: {defaults:{}},
type: 'junction',
z: RED.workspaces.active(),
id: RED.nodes.id(),
x: 0,
y: 0,
w: 0, h: 0,
outputs: 1,
inputs: 1,
dirty: true
}
links.forEach(function(l) {
junction.x += l._sliceLocation.x
junction.y += l._sliceLocation.y
})
junction.x = Math.round(junction.x/links.length)
junction.y = Math.round(junction.y/links.length)
if (snapGrid) {
junction.x = (gridSize*Math.round(junction.x/gridSize));
junction.y = (gridSize*Math.round(junction.y/gridSize));
}
var nodeGroups = new Set()
RED.nodes.addJunction(junction)
addedJunctions.push(junction)
var newLink = {
source: links[0].source,
sourcePort: links[0].sourcePort,
target: junction
}
addedLinks.push(newLink)
RED.nodes.addLink(newLink)
links.forEach(function(l) {
removedLinks.push(l)
RED.nodes.removeLink(l)
var newLink = {
source: junction,
sourcePort: 0,
target: l.target
}
addedLinks.push(newLink)
RED.nodes.addLink(newLink)
nodeGroups.add(l.source.g || "__NONE__")
nodeGroups.add(l.target.g || "__NONE__")
})
if (nodeGroups.size === 1) {
var group = nodeGroups.values().next().value
if (group !== "__NONE__") {
RED.group.addToGroup(RED.nodes.group(group), junction)
}
}
})
slicePath.remove();
slicePath = null;
if (addedJunctions.length > 0) {
RED.history.push({
t: 'add',
links: addedLinks,
junctions: addedJunctions,
removedLinks: removedLinks
})
RED.nodes.dirty(true)
}
RED.view.redraw(true);
} }
if (mouse_mode == RED.state.MOVING_ACTIVE) { if (mouse_mode == RED.state.MOVING_ACTIVE) {
if (movingSet.length() > 0) { if (movingSet.length() > 0) {
@ -1943,7 +2043,7 @@ RED.view = (function() {
clearSelection(); clearSelection();
RED.history.pop(); RED.history.pop();
mouse_mode = 0; mouse_mode = 0;
} else if (mouse_mode === RED.state.SLICING) { } else if (mouse_mode === RED.state.SLICING || mouse_mode === RED.state.SLICING_JUNCTION) {
if (slicePath) { if (slicePath) {
slicePath.remove(); slicePath.remove();
slicePath = null; slicePath = null;
@ -2012,6 +2112,14 @@ RED.view = (function() {
} }
}); });
activeJunctions.forEach(function(n) {
if (!n.selected) {
n.selected = true;
n.dirty = true;
movingSet.add(n);
}
})
if (mouse_mode !== RED.state.SELECTING_NODE && activeSubflow) { if (mouse_mode !== RED.state.SELECTING_NODE && activeSubflow) {
activeSubflow.in.forEach(function(n) { activeSubflow.in.forEach(function(n) {
if (!n.selected) { if (!n.selected) {
@ -2211,6 +2319,7 @@ RED.view = (function() {
nodes: [], nodes: [],
links: [], links: [],
groups: [], groups: [],
junctions: [],
workspaces: [], workspaces: [],
subflows: [] subflows: []
} }
@ -2231,6 +2340,7 @@ RED.view = (function() {
historyEvent.nodes = historyEvent.nodes.concat(subEvent.nodes); historyEvent.nodes = historyEvent.nodes.concat(subEvent.nodes);
historyEvent.links = historyEvent.links.concat(subEvent.links); historyEvent.links = historyEvent.links.concat(subEvent.links);
historyEvent.groups = historyEvent.groups.concat(subEvent.groups); historyEvent.groups = historyEvent.groups.concat(subEvent.groups);
historyEvent.junctions = historyEvent.junctions.concat(subEvent.junctions);
} }
RED.history.push(historyEvent); RED.history.push(historyEvent);
RED.nodes.dirty(true); RED.nodes.dirty(true);
@ -2243,6 +2353,7 @@ RED.view = (function() {
var removedNodes = []; var removedNodes = [];
var removedLinks = []; var removedLinks = [];
var removedGroups = []; var removedGroups = [];
var removedJunctions = [];
var removedSubflowOutputs = []; var removedSubflowOutputs = [];
var removedSubflowInputs = []; var removedSubflowInputs = [];
var removedSubflowStatus; var removedSubflowStatus;
@ -2287,7 +2398,7 @@ RED.view = (function() {
for (var i=0;i<movingSet.length();i++) { for (var i=0;i<movingSet.length();i++) {
node = movingSet.get(i).n; node = movingSet.get(i).n;
node.selected = false; node.selected = false;
if (node.type !== "group" && node.type !== "subflow") { if (node.type !== "group" && node.type !== "subflow" && node.type !== 'junction') {
if (node.x < 0) { if (node.x < 0) {
node.x = 25 node.x = 25
} }
@ -2305,6 +2416,10 @@ RED.view = (function() {
RED.group.markDirty(group); RED.group.markDirty(group);
} }
} }
} else if (node.type === 'junction') {
var result = RED.nodes.removeJunction(node)
removedJunctions.push(node);
removedLinks = removedLinks.concat(result.links);
} else { } else {
if (node.direction === "out") { if (node.direction === "out") {
removedSubflowOutputs.push(node); removedSubflowOutputs.push(node);
@ -2349,7 +2464,7 @@ RED.view = (function() {
subflowInstances = instances.instances; subflowInstances = instances.instances;
} }
movingSet.clear(); movingSet.clear();
if (removedNodes.length > 0 || removedSubflowOutputs.length > 0 || removedSubflowInputs.length > 0 || removedSubflowStatus || removedGroups.length > 0) { if (removedNodes.length > 0 || removedSubflowOutputs.length > 0 || removedSubflowInputs.length > 0 || removedSubflowStatus || removedGroups.length > 0 || removedJunctions.length > 0) {
RED.nodes.dirty(true); RED.nodes.dirty(true);
} }
} }
@ -2396,6 +2511,7 @@ RED.view = (function() {
nodes:removedNodes, nodes:removedNodes,
links:removedLinks, links:removedLinks,
groups: removedGroups, groups: removedGroups,
junctions: removedJunctions,
subflowOutputs:removedSubflowOutputs, subflowOutputs:removedSubflowOutputs,
subflowInputs:removedSubflowInputs, subflowInputs:removedSubflowInputs,
subflow: { subflow: {
@ -2455,6 +2571,7 @@ RED.view = (function() {
var nns = []; var nns = [];
var nodeCount = 0; var nodeCount = 0;
var groupCount = 0; var groupCount = 0;
var junctionCount = 0;
var handled = {}; var handled = {};
for (var n=0;n<nodes.length;n++) { for (var n=0;n<nodes.length;n++) {
var node = nodes[n]; var node = nodes[n];
@ -2467,6 +2584,8 @@ RED.view = (function() {
if (node.type != "subflow") { if (node.type != "subflow") {
if (node.type === "group") { if (node.type === "group") {
groupCount++; groupCount++;
} else if (node.type === 'junction') {
junctionCount++;
} else { } else {
nodeCount++; nodeCount++;
} }
@ -2649,6 +2768,7 @@ RED.view = (function() {
evt.preventDefault(); evt.preventDefault();
} }
function portMouseUp(d,portType,portIndex,evt) { function portMouseUp(d,portType,portIndex,evt) {
if (RED.view.DEBUG) { console.warn("portMouseUp", mouse_mode,d,portType,portIndex); } if (RED.view.DEBUG) { console.warn("portMouseUp", mouse_mode,d,portType,portIndex); }
evt = evt || d3.event; evt = evt || d3.event;
@ -3031,6 +3151,52 @@ RED.view = (function() {
port.classed("red-ui-flow-port-hovered",false); port.classed("red-ui-flow-port-hovered",false);
} }
function junctionMouseOver(junction, d) {
junction.classed("red-ui-flow-junction-hovered",true);
}
function junctionMouseOut(junction, d) {
junction.classed("red-ui-flow-junction-hovered",false);
}
function junctionMouseDown(junction, d, evt) {
if (RED.view.DEBUG) { console.warn("junctionMouseDown", d); }
evt = evt || d3.event;
d3.event = evt
if (evt === 1) {
return;
}
if (mouse_mode === RED.state.SELECTING_NODE) {
evt.stopPropagation();
return;
}
if (mouse_mode == RED.state.QUICK_JOINING) {
d3.event.stopPropagation();
return;
}
// mousedown_node = d;
// mousedown_port_type = portType;
// mousedown_port_index = portIndex || 0;
if (mouse_mode !== RED.state.QUICK_JOINING && (evt.ctrlKey || evt.metaKey)) {
mouse_mode = RED.state.QUICK_JOINING;
document.body.style.cursor = "crosshair";
showDragLines([{node:d,port:0,portType: PORT_TYPE_OUTPUT}]);
$(window).on('keyup',disableQuickJoinEventHandler);
} else if (event.button != 2) {
nodeMouseDown.call(junction[0][0],d)
// clearSelection();
// movingSet.add(d);
// mousedown_node = d;
// mouse_mode = RED.state.MOVING;
// var mouse = d3.touches(junction[0][0])[0]||d3.mouse(junction[0][0]);
// mouse[0] += d.x-d.w/2;
// mouse[1] += d.y-d.h/2;
// prepareDrag(mouse);
}
evt.stopPropagation();
evt.preventDefault();
}
function prepareDrag(mouse) { function prepareDrag(mouse) {
mouse_mode = RED.state.MOVING; mouse_mode = RED.state.MOVING;
// Called when movingSet should be prepared to be dragged // Called when movingSet should be prepared to be dragged
@ -3223,8 +3389,6 @@ RED.view = (function() {
) )
lastClickNode = mousedown_node; lastClickNode = mousedown_node;
var i;
if (!d.selected && d.g /*&& !RED.nodes.group(d.g).selected*/) { if (!d.selected && d.g /*&& !RED.nodes.group(d.g).selected*/) {
var nodeGroup = RED.nodes.group(d.g); var nodeGroup = RED.nodes.group(d.g);
@ -3497,6 +3661,11 @@ RED.view = (function() {
function portMouseOverProxy(e) { portMouseOver(d3.select(this), this.__data__,this.__portType__,this.__portIndex__, e); } function portMouseOverProxy(e) { portMouseOver(d3.select(this), this.__data__,this.__portType__,this.__portIndex__, e); }
function portMouseOutProxy(e) { portMouseOut(d3.select(this), this.__data__,this.__portType__,this.__portIndex__, e); } function portMouseOutProxy(e) { portMouseOut(d3.select(this), this.__data__,this.__portType__,this.__portIndex__, e); }
function junctionMouseOverProxy(e) { junctionMouseOver(d3.select(this), this.__data__) }
function junctionMouseOutProxy(e) { junctionMouseOut(d3.select(this), this.__data__) }
function junctionMouseDownProxy(e) { junctionMouseDown(d3.select(this), this.__data__, e) }
function junctionMouseUpProxy(e) { junctionMouseUp(d3.select(this), this.__data__) }
function linkMouseDown(d) { function linkMouseDown(d) {
if (mouse_mode === RED.state.SELECTING_NODE) { if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation(); d3.event.stopPropagation();
@ -4631,6 +4800,62 @@ RED.view = (function() {
}) })
} }
var junction = junctionLayer.selectAll(".red-ui-flow-junction").data(
activeJunctions,
d => d.id
)
var junctionEnter = junction.enter().insert("svg:g").attr("class","red-ui-flow-junction")
junctionEnter.each(function(d,i) {
var junction = d3.select(this);
var contents = document.createDocumentFragment();
// d.added = true;
var junctionBack = document.createElementNS("http://www.w3.org/2000/svg","rect");
junctionBack.setAttribute("class","red-ui-flow-junction-background");
junctionBack.setAttribute("x",-5);
junctionBack.setAttribute("y",-5);
junctionBack.setAttribute("width",10);
junctionBack.setAttribute("height",10);
junctionBack.setAttribute("rx",5);
junctionBack.setAttribute("ry",5);
junctionBack.__data__ = d;
this.__junctionBack__ = junctionBack;
contents.appendChild(junctionBack);
junctionBack.addEventListener("mouseover", junctionMouseOverProxy);
junctionBack.addEventListener("mouseout", junctionMouseOutProxy);
junctionBack.addEventListener("mouseup", portMouseUpProxy);
junctionBack.addEventListener("mousedown", junctionMouseDownProxy);
// d3.select(junctionBack).on("mousedown", nodeMouseDown);
this.__portType__ = PORT_TYPE_INPUT
this.__portIndex__ = 0
// function portMouseUpProxy(e) { portMouseUp(this.__data__,this.__portType__,this.__portIndex__, e); }
junction[0][0].appendChild(contents);
})
junction.exit().remove();
junction.each(function(d) {
var junction = d3.select(this);
this.setAttribute("transform", "translate(" + (d.x) + "," + (d.y) + ")");
if (d.dirty) {
junction.classed("selected", !!d.selected)
dirtyNodes[d.id] = d;
if (d.g) {
if (!dirtyGroups[d.g]) {
var gg = d.g;
while (gg && !dirtyGroups[gg]) {
dirtyGroups[gg] = RED.nodes.group(gg);
gg = dirtyGroups[gg].g;
}
}
}
}
})
var link = linkLayer.selectAll(".red-ui-flow-link").data( var link = linkLayer.selectAll(".red-ui-flow-link").data(
activeLinks, activeLinks,
function(d) { function(d) {
@ -4657,6 +4882,27 @@ RED.view = (function() {
selectedLinks.add(d) selectedLinks.add(d)
l.classed("red-ui-flow-link-splice",true) l.classed("red-ui-flow-link-splice",true)
redraw() redraw()
} else if (mouse_mode === RED.state.SLICING_JUNCTION) {
if (!l.classed("red-ui-flow-link-splice")) {
// Find intersection point
var lineLength = pathLine.getTotalLength();
var pos;
var delta = Infinity;
for (var i = 0; i < lineLength; i++) {
var linePos = pathLine.getPointAtLength(i);
var posDeltaX = Math.abs(linePos.x-d3.event.offsetX)
var posDeltaY = Math.abs(linePos.y-d3.event.offsetY)
var posDelta = posDeltaX*posDeltaX + posDeltaY*posDeltaY
if (posDelta < delta) {
pos = linePos
delta = posDelta
}
}
d._sliceLocation = pos
selectedLinks.add(d)
l.classed("red-ui-flow-link-splice",true)
redraw()
}
} }
}) })
@ -4683,9 +4929,9 @@ RED.view = (function() {
var numOutputs = d.source.outputs || 1; var numOutputs = d.source.outputs || 1;
var sourcePort = d.sourcePort || 0; var sourcePort = d.sourcePort || 0;
var y = -((numOutputs-1)/2)*13 +13*sourcePort; var y = -((numOutputs-1)/2)*13 +13*sourcePort;
d.x1 = d.source.x+d.source.w/2; d.x1 = d.source.x+(d.source.w/2||0);
d.y1 = d.source.y+y; d.y1 = d.source.y+y;
d.x2 = d.target.x-d.target.w/2; d.x2 = d.target.x-(d.target.w/2||0);
d.y2 = d.target.y; d.y2 = d.target.y;
// return "M "+d.x1+" "+d.y1+ // return "M "+d.x1+" "+d.y1+
@ -5144,6 +5390,7 @@ RED.view = (function() {
var new_nodes = result.nodes; var new_nodes = result.nodes;
var new_links = result.links; var new_links = result.links;
var new_groups = result.groups; var new_groups = result.groups;
var new_junctions = result.junctions;
var new_workspaces = result.workspaces; var new_workspaces = result.workspaces;
var new_subflows = result.subflows; var new_subflows = result.subflows;
var removedNodes = result.removedNodes; var removedNodes = result.removedNodes;
@ -5153,6 +5400,7 @@ RED.view = (function() {
} }
var new_ms = new_nodes.filter(function(n) { return n.hasOwnProperty("x") && n.hasOwnProperty("y") && n.z == RED.workspaces.active() }); var new_ms = new_nodes.filter(function(n) { return n.hasOwnProperty("x") && n.hasOwnProperty("y") && n.z == RED.workspaces.active() });
new_ms = new_ms.concat(new_groups.filter(function(g) { return g.z === RED.workspaces.active()})) new_ms = new_ms.concat(new_groups.filter(function(g) { return g.z === RED.workspaces.active()}))
new_ms = new_ms.concat(new_junctions.filter(function(j) { return j.z === RED.workspaces.active()}))
var new_node_ids = new_nodes.map(function(n){ n.changed = true; return n.id; }); var new_node_ids = new_nodes.map(function(n){ n.changed = true; return n.id; });
clearSelection(); clearSelection();
@ -5186,9 +5434,11 @@ RED.view = (function() {
node.n.moved = true; node.n.moved = true;
node.n.x -= dx - mouse_position[0]; node.n.x -= dx - mouse_position[0];
node.n.y -= dy - mouse_position[1]; node.n.y -= dy - mouse_position[1];
node.n.w = node_width; if (node.n.type !== 'junction') {
node.n.h = node_height; node.n.w = node_width;
node.n.resize = true; node.n.h = node_height;
node.n.resize = true;
}
node.dx = node.n.x - mouse_position[0]; node.dx = node.n.x - mouse_position[0];
node.dy = node.n.y - mouse_position[1]; node.dy = node.n.y - mouse_position[1];
if (node.n.type === "group") { if (node.n.type === "group") {
@ -5235,6 +5485,7 @@ RED.view = (function() {
nodes:new_node_ids, nodes:new_node_ids,
links:new_links, links:new_links,
groups:new_groups, groups:new_groups,
junctions: new_junctions,
workspaces:new_workspaces, workspaces:new_workspaces,
subflows:new_subflows, subflows:new_subflows,
dirty:RED.nodes.dirty() dirty:RED.nodes.dirty()
@ -5282,6 +5533,7 @@ RED.view = (function() {
} }
}) })
var newGroupCount = new_groups.length; var newGroupCount = new_groups.length;
var newJunctionCount = new_junctions.length;
if (new_workspaces.length > 0) { if (new_workspaces.length > 0) {
counts.push(RED._("clipboard.flow",{count:new_workspaces.length})); counts.push(RED._("clipboard.flow",{count:new_workspaces.length}));
} }

View File

@ -379,3 +379,17 @@ g.red-ui-flow-link-unknown path.red-ui-flow-link-line {
white-space: pre; white-space: pre;
@include disable-selection; @include disable-selection;
} }
.red-ui-flow-junction-background {
stroke: $node-border;
stroke-width: 1;
fill: $node-port-background;
cursor: crosshair;
}
.red-ui-flow-junction-hovered {
stroke: $port-selected-color;
fill: $port-selected-color;
}
.red-ui-flow-junction.selected .red-ui-flow-junction-background {
stroke: $port-selected-color;
// fill: $port-selected-color;
}

View File

@ -189,6 +189,7 @@
.red-ui-search-result-node { .red-ui-search-result-node {
&.red-ui-palette-icon-flow, &.red-ui-palette-icon-flow,
&.red-ui-palette-icon-group, &.red-ui-palette-icon-group,
&.red-ui-palette-icon-junction,
&.red-ui-palette-icon-selection { &.red-ui-palette-icon-selection {
background: none; background: none;
border-color: transparent; border-color: transparent;
@ -268,6 +269,7 @@
&.red-ui-palette-icon-flow, &.red-ui-palette-icon-flow,
&.red-ui-palette-icon-group, &.red-ui-palette-icon-group,
&.red-ui-palette-icon-junction,
&.red-ui-palette-icon-selection { &.red-ui-palette-icon-selection {
background: none; background: none;
border-color: transparent; border-color: transparent;
@ -303,6 +305,7 @@
&.red-ui-palette-icon-flow { &.red-ui-palette-icon-flow {
margin-top: -2px; margin-top: -2px;
} }
&.red-ui-palette-icon-junction .red-ui-palette-icon-fa,
&.red-ui-palette-icon-group .red-ui-palette-icon-fa { &.red-ui-palette-icon-group .red-ui-palette-icon-fa {
font-size: 14px; font-size: 14px;
} }

View File

@ -0,0 +1,5 @@
<!--
05-junction.html
This file exists so that the runtime loads the Junction node into the registry,
but it is empty so it doesn't appear in the editor palette
-->

View File

@ -0,0 +1,12 @@
module.exports = function(RED) {
"use strict";
function JunctionNode(n) {
RED.nodes.createNode(this,n);
this.on("input",function(msg, send, done) {
send(msg);
done();
});
}
RED.nodes.registerType("junction",JunctionNode);
}