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

Merge pull request #3462 from node-red/junctions

Add Junctions
This commit is contained in:
Nick O'Leary 2022-03-14 18:34:09 +00:00 committed by GitHub
commit 64d1a82920
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 491 additions and 47 deletions

View File

@ -106,6 +106,23 @@ RED.history = (function() {
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) {
inverseEv.groups = [];
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) {
inverseEv.links = [];
for (i=0;i<ev.links.length;i++) {

View File

@ -38,6 +38,9 @@ RED.nodes = (function() {
var groups = {};
var groupsByZ = {};
var junctions = {};
var junctionsByZ = {};
var initialLoad;
var dirty = false;
@ -819,6 +822,7 @@ RED.nodes = (function() {
var removedNodes = [];
var removedLinks = [];
var removedGroups = [];
var removedJunctions = [];
if (ws) {
delete workspaces[id];
delete linkTabMap[id];
@ -827,7 +831,14 @@ RED.nodes = (function() {
var node;
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) {
if (configNodes.hasOwnProperty(i)) {
@ -842,6 +853,10 @@ RED.nodes = (function() {
var result = removeNode(removedNodes[i].id);
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.
// - start with the top-most groups
@ -861,7 +876,7 @@ RED.nodes = (function() {
allNodes.removeTab(id);
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) {
@ -1118,7 +1133,7 @@ RED.nodes = (function() {
delete node.env;
}
}
if (n._def.category != "config") {
if (n._def.category != "config" || n.type === 'junction') {
node.x = n.x;
node.y = n.y;
if (exportDimensions) {
@ -1381,6 +1396,11 @@ RED.nodes = (function() {
nns.push(convertNode(groups[i], opts));
}
}
for (i in junctions) {
if (junctions.hasOwnProperty(i)) {
nns.push(convertNode(junctions[i], opts));
}
}
for (i in configNodes) {
if (configNodes.hasOwnProperty(i)) {
nns.push(convertNode(configNodes[i], opts));
@ -1462,6 +1482,7 @@ RED.nodes = (function() {
tabs: {},
subflows: {},
groups: {},
junctions: {},
configs: {},
nodes: {},
all: [],
@ -1477,6 +1498,8 @@ RED.nodes = (function() {
imported.subflows[n.id] = n;
} else if (n.type === "group") {
imported.groups[n.id] = n;
} else if (n.type === "junction") {
imported.junctions[n.id] = n;
} else if (n.hasOwnProperty("x") && n.hasOwnProperty("y")) {
imported.nodes[n.id] = n;
} else {
@ -1485,7 +1508,7 @@ RED.nodes = (function() {
var nodeZ = n.z || "__global__";
imported.zMap[nodeZ] = imported.zMap[nodeZ] || [];
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;
}
})
@ -1651,7 +1674,7 @@ RED.nodes = (function() {
if (!options.generateIds) {
if (!options.importMap[id]) {
// 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) {
existingNodes.push({existing:existing, imported:n});
}
@ -1705,6 +1728,7 @@ RED.nodes = (function() {
n.type != "tab" &&
n.type != "subflow" &&
n.type != "group" &&
n.type != 'junction' &&
!registry.getNodeType(n.type) &&
n.type.substring(0,8) != "subflow:" &&
unknownTypes.indexOf(n.type)==-1) {
@ -1777,6 +1801,7 @@ RED.nodes = (function() {
var new_nodes = [];
var new_links = [];
var new_groups = [];
var new_junctions = [];
var new_group_set = new Set();
var nid;
var def;
@ -1968,12 +1993,15 @@ RED.nodes = (function() {
changed:false,
_config:{}
}
if (n.type !== "group") {
if (n.type !== "group" && n.type !== 'junction') {
node.wires = n.wires||[];
node.inputLabels = n.inputLabels;
node.outputLabels = n.outputLabels;
node.icon = n.icon;
}
if (n.type === 'junction') {
node.wires = n.wires||[];
}
if (n.hasOwnProperty('l')) {
node.l = n.l;
}
@ -2042,6 +2070,15 @@ RED.nodes = (function() {
node.outputs = subflow.out.length;
node.inputs = subflow.in.length;
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 {
if (!node._def) {
if (node.x && node.y) {
@ -2125,7 +2162,9 @@ RED.nodes = (function() {
node_map[n.id] = node;
// If an 'unknown' config node, it will not have been caught by the
// 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);
} else if (node.type === "group") {
new_groups.push(node);
@ -2136,8 +2175,12 @@ RED.nodes = (function() {
}
// 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++) {
if (i<new_nodes.length) {
n = new_nodes[i];
} else {
n = new_junctions[i - new_nodes.length]
}
if (n.wires) {
for (var w1=0;w1<n.wires.length;w1++) {
var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]];
@ -2276,6 +2319,12 @@ RED.nodes = (function() {
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.
for (i=0;i<new_nodes.length;i++) {
var node = new_nodes[i];
@ -2306,6 +2355,7 @@ RED.nodes = (function() {
nodes:new_nodes,
links:new_links,
groups:new_groups,
junctions: new_junctions,
workspaces:new_workspaces,
subflows:new_subflows,
missingWorkspace: missingWorkspace,
@ -2461,6 +2511,30 @@ RED.nodes = (function() {
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) {
var helpContent = "";
var helpElement = $("script[data-help-name='"+type+"']");
@ -2734,6 +2808,11 @@ RED.nodes = (function() {
group: function(id) { return groups[id] },
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) {
allNodes.eachNode(cb);
},

View File

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

View File

@ -352,8 +352,10 @@ RED.group = (function() {
}
if (n.type === 'group') {
RED.events.emit("groups:change",n)
} else {
} else if (n.type !== 'junction') {
RED.events.emit("nodes:change",n)
} else {
RED.events.emit("junctions:change",n)
}
})
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);
if (n.type === 'group') {
RED.events.emit("groups:change",n)
} else {
} else if (n.type !== 'junction') {
RED.events.emit("nodes:change",n)
} else {
RED.events.emit("junctions:change",n)
}
}
}
@ -583,8 +587,10 @@ RED.group = (function() {
}
if (n.type === 'group') {
RED.events.emit("groups:change",n)
} else {
} else if (n.type !== 'junction') {
RED.events.emit("nodes:change",n)
} else {
RED.events.emit("junctions:change",n)
}
}
markDirty(group);

View File

@ -29,5 +29,6 @@ RED.state = {
GROUP_DRAGGING: 12,
GROUP_RESIZE: 13,
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];
if (node.type === "subflow" && node.direction) {
// ignore subflow virtual ports
} else if (node.type !== 'group'){
} else if (node.type !== 'group' && node.type !== 'junction'){
showNodeTypeHelp(node.type);
}
}

View File

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

View File

@ -171,17 +171,21 @@ RED.typeSearch = (function() {
var div = $('<div>',{class:"red-ui-search-result"}).appendTo(container);
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(div);
if (object.type === "junction") {
nodeDiv.addClass("red-ui-palette-icon-junction");
} else {
var colour = RED.utils.getNodeColor(object.type,def);
var icon_url = RED.utils.getNodeIcon(def);
nodeDiv.css('backgroundColor',colour);
}
var icon_url = RED.utils.getNodeIcon(def);
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, false);
if (def.inputs > 0) {
if (object.type !== "junction" && def.inputs > 0) {
$('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
}
if (def.outputs > 0) {
if (object.type !== "junction" && def.outputs > 0) {
$('<div/>',{class:"red-ui-search-result-node-port red-ui-search-result-node-output"}).appendTo(nodeDiv);
}
@ -313,8 +317,8 @@ RED.typeSearch = (function() {
return !filter ||
(
(!filter.type || type === filter.type) &&
(!filter.input || def.inputs > 0) &&
(!filter.output || def.outputs > 0)
(!filter.input || type === 'junction' || def.inputs > 0) &&
(!filter.output || type === 'junction' || def.outputs > 0)
)
}
function refreshTypeList(opts) {
@ -323,7 +327,7 @@ RED.typeSearch = (function() {
searchInput.searchBox('value','').focus();
selected = -1;
var common = [
'inject','debug','function','change','switch'
'inject','debug','function','change','switch','junction'
].filter(function(t) { return applyFilter(opts.filter,t,RED.nodes.getType(t)); });
var recentlyUsed = Object.keys(typesUsed);
@ -348,6 +352,9 @@ RED.typeSearch = (function() {
var index = 0;
for(i=0;i<common.length;i++) {
var itemDef = RED.nodes.getType(common[i]);
if (common[i] === 'junction') {
itemDef = { inputs:1, outputs: 1, label: 'junction', type: 'junction'}
}
if (itemDef) {
item = {
type: common[i],

View File

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

View File

@ -24,6 +24,7 @@
* |- <g> "groupLayer"
* |- <g> "groupSelectLayer"
* |- <g> "linkLayer"
* |- <g> "junctionLayer"
* |- <g> "dragGroupLayer"
* |- <g> "nodeLayer"
*/
@ -56,6 +57,7 @@ RED.view = (function() {
var activeSubflow = null;
var activeNodes = [];
var activeLinks = [];
var activeJunctions = [];
var activeFlowLinks = [];
var activeLinkNodes = {};
var activeGroup = null;
@ -111,6 +113,7 @@ RED.view = (function() {
var eventLayer;
var gridLayer;
var linkLayer;
var junctionLayer;
var dragGroupLayer;
var groupSelectLayer;
var nodeLayer;
@ -199,6 +202,11 @@ RED.view = (function() {
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");
outer = d3.select("#red-ui-workspace-chart")
@ -373,6 +381,7 @@ RED.view = (function() {
groupSelectLayer = eventLayer.append("g");
linkLayer = eventLayer.append("g");
dragGroupLayer = eventLayer.append("g");
junctionLayer = eventLayer.append("g");
nodeLayer = eventLayer.append("g");
drag_lines = [];
@ -785,7 +794,7 @@ RED.view = (function() {
source:{z:activeWorkspace},
target:{z:activeWorkspace}
});
activeJunctions = RED.nodes.junctions(activeWorkspace) || [];
activeGroups = RED.nodes.groups(activeWorkspace)||[];
activeGroups.forEach(function(g, i) {
g._index = i;
@ -800,6 +809,7 @@ RED.view = (function() {
} else {
activeNodes = [];
activeLinks = [];
activeJunctions = [];
activeGroups = [];
}
@ -970,9 +980,9 @@ RED.view = (function() {
.attr("class","nr-ui-view-lasso");
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();
mouse_mode = RED.state.SLICING;
mouse_mode = (d3.event.metaKey || d3.event.ctrlKey)?RED.state.SLICING : RED.state.SLICING_JUNCTION;
point = d3.mouse(this);
slicePath = eventLayer.append("path").attr("class","nr-ui-view-slice").attr("d",`M${point[0]} ${point[1]}`)
slicePathLast = point;
@ -1107,16 +1117,38 @@ RED.view = (function() {
keepAdding = false;
resetMouseVars();
}
var nn;
var historyEvent;
if (type === 'junction') {
nn = {
_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
}
historyEvent = {
t:'add',
junctions:[nn]
}
} else {
var result = createNode(type);
if (!result) {
return;
}
nn = result.node;
historyEvent = result.historyEvent;
}
if (keepAdding) {
mouse_mode = RED.state.QUICK_JOINING;
}
var nn = result.node;
var historyEvent = result.historyEvent;
nn.x = point[0];
nn.y = point[1];
var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label");
@ -1225,8 +1257,11 @@ RED.view = (function() {
}
}
}
if (nn.type === 'junction') {
RED.nodes.addJunction(nn);
} else {
RED.nodes.add(nn);
}
RED.editor.validateNode(nn);
if (targetGroup) {
@ -1373,7 +1408,7 @@ RED.view = (function() {
.attr("height",h)
;
return;
} else if (mouse_mode === RED.state.SLICING) {
} else if (mouse_mode === RED.state.SLICING || mouse_mode === RED.state.SLICING_JUNCTION) {
if (slicePath) {
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) {
@ -1739,6 +1774,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 +1826,92 @@ RED.view = (function() {
} else if (mouse_mode == RED.state.DEFAULT && mousedown_link == null && !d3.event.ctrlKey && !d3.event.metaKey ) {
clearSelection();
updateSelection();
} else if (slicePath) {
} else if (mouse_mode == RED.state.SLICING) {
deleteSelection();
slicePath.remove();
slicePath = null;
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 (movingSet.length() > 0) {
@ -1943,7 +2068,7 @@ RED.view = (function() {
clearSelection();
RED.history.pop();
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) {
slicePath.remove();
slicePath = null;
@ -2012,6 +2137,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) {
activeSubflow.in.forEach(function(n) {
if (!n.selected) {
@ -2211,6 +2344,7 @@ RED.view = (function() {
nodes: [],
links: [],
groups: [],
junctions: [],
workspaces: [],
subflows: []
}
@ -2231,6 +2365,7 @@ RED.view = (function() {
historyEvent.nodes = historyEvent.nodes.concat(subEvent.nodes);
historyEvent.links = historyEvent.links.concat(subEvent.links);
historyEvent.groups = historyEvent.groups.concat(subEvent.groups);
historyEvent.junctions = historyEvent.junctions.concat(subEvent.junctions);
}
RED.history.push(historyEvent);
RED.nodes.dirty(true);
@ -2243,6 +2378,7 @@ RED.view = (function() {
var removedNodes = [];
var removedLinks = [];
var removedGroups = [];
var removedJunctions = [];
var removedSubflowOutputs = [];
var removedSubflowInputs = [];
var removedSubflowStatus;
@ -2287,7 +2423,7 @@ RED.view = (function() {
for (var i=0;i<movingSet.length();i++) {
node = movingSet.get(i).n;
node.selected = false;
if (node.type !== "group" && node.type !== "subflow") {
if (node.type !== "group" && node.type !== "subflow" && node.type !== 'junction') {
if (node.x < 0) {
node.x = 25
}
@ -2305,6 +2441,10 @@ RED.view = (function() {
RED.group.markDirty(group);
}
}
} else if (node.type === 'junction') {
var result = RED.nodes.removeJunction(node)
removedJunctions.push(node);
removedLinks = removedLinks.concat(result.links);
} else {
if (node.direction === "out") {
removedSubflowOutputs.push(node);
@ -2349,7 +2489,7 @@ RED.view = (function() {
subflowInstances = instances.instances;
}
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);
}
}
@ -2396,6 +2536,7 @@ RED.view = (function() {
nodes:removedNodes,
links:removedLinks,
groups: removedGroups,
junctions: removedJunctions,
subflowOutputs:removedSubflowOutputs,
subflowInputs:removedSubflowInputs,
subflow: {
@ -2455,6 +2596,7 @@ RED.view = (function() {
var nns = [];
var nodeCount = 0;
var groupCount = 0;
var junctionCount = 0;
var handled = {};
for (var n=0;n<nodes.length;n++) {
var node = nodes[n];
@ -2467,6 +2609,8 @@ RED.view = (function() {
if (node.type != "subflow") {
if (node.type === "group") {
groupCount++;
} else if (node.type === 'junction') {
junctionCount++;
} else {
nodeCount++;
}
@ -2649,6 +2793,7 @@ RED.view = (function() {
evt.preventDefault();
}
function portMouseUp(d,portType,portIndex,evt) {
if (RED.view.DEBUG) { console.warn("portMouseUp", mouse_mode,d,portType,portIndex); }
evt = evt || d3.event;
@ -3031,6 +3176,52 @@ RED.view = (function() {
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) {
mouse_mode = RED.state.MOVING;
// Called when movingSet should be prepared to be dragged
@ -3223,8 +3414,6 @@ RED.view = (function() {
)
lastClickNode = mousedown_node;
var i;
if (!d.selected && d.g /*&& !RED.nodes.group(d.g).selected*/) {
var nodeGroup = RED.nodes.group(d.g);
@ -3497,6 +3686,11 @@ RED.view = (function() {
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 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) {
if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
@ -4631,6 +4825,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(
activeLinks,
function(d) {
@ -4657,6 +4907,27 @@ RED.view = (function() {
selectedLinks.add(d)
l.classed("red-ui-flow-link-splice",true)
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 +4954,9 @@ RED.view = (function() {
var numOutputs = d.source.outputs || 1;
var sourcePort = d.sourcePort || 0;
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.x2 = d.target.x-d.target.w/2;
d.x2 = d.target.x-(d.target.w/2||0);
d.y2 = d.target.y;
// return "M "+d.x1+" "+d.y1+
@ -5144,6 +5415,7 @@ RED.view = (function() {
var new_nodes = result.nodes;
var new_links = result.links;
var new_groups = result.groups;
var new_junctions = result.junctions;
var new_workspaces = result.workspaces;
var new_subflows = result.subflows;
var removedNodes = result.removedNodes;
@ -5153,6 +5425,7 @@ RED.view = (function() {
}
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_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; });
clearSelection();
@ -5186,9 +5459,11 @@ RED.view = (function() {
node.n.moved = true;
node.n.x -= dx - mouse_position[0];
node.n.y -= dy - mouse_position[1];
if (node.n.type !== 'junction') {
node.n.w = node_width;
node.n.h = node_height;
node.n.resize = true;
}
node.dx = node.n.x - mouse_position[0];
node.dy = node.n.y - mouse_position[1];
if (node.n.type === "group") {
@ -5235,6 +5510,7 @@ RED.view = (function() {
nodes:new_node_ids,
links:new_links,
groups:new_groups,
junctions: new_junctions,
workspaces:new_workspaces,
subflows:new_subflows,
dirty:RED.nodes.dirty()
@ -5282,6 +5558,7 @@ RED.view = (function() {
}
})
var newGroupCount = new_groups.length;
var newJunctionCount = new_junctions.length;
if (new_workspaces.length > 0) {
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;
@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-palette-icon-flow,
&.red-ui-palette-icon-group,
&.red-ui-palette-icon-junction,
&.red-ui-palette-icon-selection {
background: none;
border-color: transparent;
@ -268,6 +269,7 @@
&.red-ui-palette-icon-flow,
&.red-ui-palette-icon-group,
&.red-ui-palette-icon-junction,
&.red-ui-palette-icon-selection {
background: none;
border-color: transparent;
@ -303,6 +305,7 @@
&.red-ui-palette-icon-flow {
margin-top: -2px;
}
&.red-ui-palette-icon-junction .red-ui-palette-icon-fa,
&.red-ui-palette-icon-group .red-ui-palette-icon-fa {
font-size: 14px;
}

View File

@ -66,8 +66,9 @@
border-left-width: 3px;
border-right-width: 3px;
.red-ui-palette-icon-fa {
font-size: 11px;
position: relative;
top: -2.5px;
top: -3px;
left: 0px;
}
}

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);
}