mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
@@ -12,6 +12,26 @@
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-mode"><span data-i18n="link.outMode"></span></label>
|
||||
<select id="node-input-mode" style="width: 70%">
|
||||
<option value="link" selected data-i18n="link.sendToAll"></option>
|
||||
<option value="return" data-i18n="link.returnToCaller"></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="node-input-link-rows" style="position:relative; height: 30px; text-align: right;"><div style="display:inline-block"><input type="text" id="node-input-link-target-filter"></div></div>
|
||||
<div class="form-row node-input-link-row node-input-link-rows"></div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-template-name="link call">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-timeout"><span data-i18n="exec.label.timeout"></span></label>
|
||||
<input type="text" id="node-input-timeout" placeholder="30" style="width: 70px; margin-right: 5px;"><span data-i18n="inject.seconds"></span>
|
||||
</div>
|
||||
<div style="position:relative; height: 30px; text-align: right;"><div style="display:inline-block"><input type="text" id="node-input-link-target-filter"></div></div>
|
||||
<div class="form-row node-input-link-row"></div>
|
||||
</script>
|
||||
@@ -48,7 +68,6 @@
|
||||
}
|
||||
});
|
||||
var candidateNodes = RED.nodes.filterNodes({type:targetType});
|
||||
|
||||
var search = $("#node-input-link-target-filter").searchBox({
|
||||
style: "compact",
|
||||
delay: 300,
|
||||
@@ -104,7 +123,8 @@
|
||||
node: n,
|
||||
label: n.name||n.id,
|
||||
selected: isChecked,
|
||||
checkbox: true
|
||||
checkbox: node.type !== "link call",
|
||||
radio: node.type === "link call"
|
||||
})
|
||||
}
|
||||
});
|
||||
@@ -129,15 +149,22 @@
|
||||
function onEditSave(node) {
|
||||
var flows = treeList.treeList('data');
|
||||
node.links = [];
|
||||
flows.forEach(function(f) {
|
||||
f.children.forEach(function(n) {
|
||||
if (n.selected) {
|
||||
node.links.push(n.id);
|
||||
}
|
||||
if (node.type !== "link out" || $("node-input-mode").val() === 'link') {
|
||||
flows.forEach(function(f) {
|
||||
f.children.forEach(function(n) {
|
||||
if (n.selected) {
|
||||
node.links.push(n.id);
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
node.oldLinks.sort();
|
||||
node.links.sort();
|
||||
|
||||
if (node.type === "link call") {
|
||||
return
|
||||
}
|
||||
|
||||
var nodeMap = {};
|
||||
var length = Math.max(node.oldLinks.length,node.links.length);
|
||||
for (var i=0;i<length;i++) {
|
||||
@@ -195,6 +222,7 @@
|
||||
outputLabels: function(i) {
|
||||
return this.name||this._("link.linkIn");
|
||||
},
|
||||
showLabel: false,
|
||||
label: function() {
|
||||
return this.name||this._("link.linkIn");
|
||||
},
|
||||
@@ -211,25 +239,32 @@
|
||||
oneditresize: resizeNodeList
|
||||
});
|
||||
|
||||
RED.nodes.registerType('link out',{
|
||||
RED.nodes.registerType('link call',{
|
||||
category: 'common',
|
||||
color:"#ddd",//"#87D8CF",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
links: { value: [], type:"link in[]"}
|
||||
links: { value: [], type:"link in[]"},
|
||||
timeout: { value: "30", validate:RED.validators.number(true) }
|
||||
},
|
||||
align:"right",
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
icon: "link-out.svg",
|
||||
inputs: 1,
|
||||
outputs: 1,
|
||||
icon: "link-call.svg",
|
||||
inputLabels: function(i) {
|
||||
return this.name||this._("link.linkOut");
|
||||
return this.name||this._("link.linkCall");
|
||||
},
|
||||
label: function() {
|
||||
return this.name||this._("link.linkOut");
|
||||
if (this.name) {
|
||||
return this.name;
|
||||
}
|
||||
if (this.links.length > 0) {
|
||||
var targetNode = RED.nodes.node(this.links[0]);
|
||||
return targetNode && (targetNode.name || targetNode.id);
|
||||
}
|
||||
return this._("link.linkCall");
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
return (this.name || this.links.length > 0)?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
onEditPrepare(this,"link in");
|
||||
@@ -237,8 +272,56 @@
|
||||
oneditsave: function() {
|
||||
onEditSave(this);
|
||||
},
|
||||
oneditresize: resizeNodeList
|
||||
});
|
||||
|
||||
|
||||
RED.nodes.registerType('link out',{
|
||||
category: 'common',
|
||||
color:"#ddd",//"#87D8CF",
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
mode: { value: "link" },// link || return
|
||||
links: { value: [], type:"link in[]"}
|
||||
},
|
||||
align:"right",
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
icon: function() {
|
||||
if (this.mode === "return") {
|
||||
return "link-return.svg";
|
||||
} else {
|
||||
return "link-out.svg";
|
||||
}
|
||||
},
|
||||
inputLabels: function(i) {
|
||||
return this.name||(this.mode === "return" ?this._("link.linkOutReturn"):this._("link.linkOut"));
|
||||
},
|
||||
showLabel: false,
|
||||
label: function() {
|
||||
return this.name||(this.mode === "return" ?this._("link.linkOutReturn"):this._("link.linkOut"));
|
||||
},
|
||||
labelStyle: function() {
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
onEditPrepare(this,"link in");
|
||||
$("#node-input-mode").on("change", function() {
|
||||
$(".node-input-link-rows").toggle(this.value === "link")
|
||||
})
|
||||
if (!this.mode) {
|
||||
$("#node-input-mode").val('link').trigger("change");
|
||||
}
|
||||
|
||||
},
|
||||
oneditsave: function() {
|
||||
onEditSave(this);
|
||||
},
|
||||
onadd: onAdd,
|
||||
oneditresize: resizeNodeList
|
||||
});
|
||||
|
||||
|
||||
|
||||
})();
|
||||
</script>
|
||||
|
@@ -17,6 +17,8 @@
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
|
||||
const crypto = require("crypto");
|
||||
|
||||
function LinkInNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var node = this;
|
||||
@@ -40,13 +42,91 @@ module.exports = function(RED) {
|
||||
function LinkOutNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
var node = this;
|
||||
var mode = n.mode || "link";
|
||||
|
||||
var event = "node:"+n.id;
|
||||
this.on("input", function(msg, send, done) {
|
||||
msg._event = event;
|
||||
RED.events.emit(event,msg)
|
||||
send(msg);
|
||||
done();
|
||||
|
||||
if (mode === "return") {
|
||||
if (Array.isArray(msg._linkSource) && msg._linkSource.length > 0) {
|
||||
var messageEvent = msg._linkSource.pop();
|
||||
var returnNode = RED.nodes.getNode(messageEvent.node);
|
||||
if (returnNode && returnNode.returnLinkMessage) {
|
||||
returnNode.returnLinkMessage(messageEvent.id, msg);
|
||||
} else {
|
||||
node.warn(RED._("link.error.missingReturn"))
|
||||
}
|
||||
} else {
|
||||
node.warn(RED._("link.error.missingReturn"))
|
||||
}
|
||||
done();
|
||||
} else if (mode === "link") {
|
||||
send(msg);
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("link out",LinkOutNode);
|
||||
|
||||
|
||||
function LinkCallNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
const node = this;
|
||||
const target = n.links[0];
|
||||
const messageEvents = {};
|
||||
let timeout = parseFloat(n.timeout || "30")*1000;
|
||||
if (isNaN(timeout)) {
|
||||
timeout = 30000;
|
||||
}
|
||||
|
||||
this.on("input", function(msg, send, done) {
|
||||
msg._linkSource = msg._linkSource || [];
|
||||
const messageEvent = {
|
||||
id: crypto.randomBytes(14).toString('hex'),
|
||||
node: node.id,
|
||||
}
|
||||
messageEvents[messageEvent.id] = {
|
||||
msg: RED.util.cloneMessage(msg),
|
||||
send,
|
||||
done,
|
||||
ts: setTimeout(function() {
|
||||
timeoutMessage(messageEvent.id)
|
||||
}, timeout )
|
||||
};
|
||||
msg._linkSource.push(messageEvent);
|
||||
var targetNode = RED.nodes.getNode(target);
|
||||
if (targetNode) {
|
||||
targetNode.receive(msg);
|
||||
}
|
||||
});
|
||||
|
||||
this.returnLinkMessage = function(eventId, msg) {
|
||||
if (Array.isArray(msg._linkSource) && msg._linkSource.length === 0) {
|
||||
delete msg._linkSource;
|
||||
}
|
||||
const messageEvent = messageEvents[eventId];
|
||||
if (messageEvent) {
|
||||
clearTimeout(messageEvent.ts);
|
||||
delete messageEvents[eventId];
|
||||
messageEvent.send(msg);
|
||||
messageEvent.done();
|
||||
} else {
|
||||
node.send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
function timeoutMessage(eventId) {
|
||||
const messageEvent = messageEvents[eventId];
|
||||
if (messageEvent) {
|
||||
delete messageEvents[eventId];
|
||||
node.error("timeout",messageEvent.msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
RED.nodes.registerType("link call",LinkCallNode);
|
||||
|
||||
|
||||
}
|
||||
|
5
packages/node_modules/@node-red/nodes/icons/link-call.svg
vendored
Normal file
5
packages/node_modules/@node-red/nodes/icons/link-call.svg
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg width="10.583mm" height="15.875mm" version="1.1" viewBox="0 0 10.583 15.875" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m8.2021 2.3812-4.8922 0.53612 1.604 0.92604-1.0395 1.8004c0.73719-0.37402 1.6437-0.38227 2.4095 0.059892 0.76511 0.44174 1.2118 1.2301 1.2577 2.055l1.0384-1.7986 1.604 0.92604zm-2.3813 4.1244c-0.77016-0.44465-1.7402-0.18474-2.1848 0.58542-0.44465 0.77016-0.185 1.7406 0.58516 2.1853 0.77016 0.44465 1.7422 0.18533 2.1869-0.58483 0.44465-0.77016 0.18295-1.7412-0.58721-2.1858zm-3.3193 1.5159-1.8211 3.1542 3.6662 2.1167 1.82-3.1524c-0.73731 0.37266-1.6431 0.37961-2.4082-0.062129-0.76585-0.44216-1.2122-1.2309-1.2569-2.0563z" fill="#fff"/>
|
||||
</svg>
|
After Width: | Height: | Size: 771 B |
5
packages/node_modules/@node-red/nodes/icons/link-return.svg
vendored
Normal file
5
packages/node_modules/@node-red/nodes/icons/link-return.svg
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg width="10.583mm" height="15.875mm" version="1.1" viewBox="0 0 10.583 15.875" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m2.6623 13.292 4.8922-0.53612-1.604-0.92604 1.0395-1.8004c-0.73719 0.37402-1.6437 0.38227-2.4095-0.059892-0.76511-0.44174-1.2118-1.2301-1.2577-2.055l-1.0384 1.7986-1.604-0.92604zm2.3813-4.1244c0.77016 0.44465 1.7402 0.18474 2.1848-0.58542 0.44465-0.77016 0.185-1.7406-0.58516-2.1853-0.77016-0.44465-1.7422-0.18533-2.1869 0.58483-0.44465 0.77016-0.18295 1.7412 0.58721 2.1858zm3.3193-1.5159 1.8211-3.1542-3.6662-2.1167-1.82 3.1524c0.73731-0.37266 1.6431-0.37961 2.4082 0.062129 0.76585 0.44216 1.2122 1.2309 1.2569 2.0563z" fill="#fff"/>
|
||||
</svg>
|
After Width: | Height: | Size: 769 B |
@@ -28,10 +28,23 @@
|
||||
<script type="text/html" data-help-name="link out">
|
||||
<p>Create virtual wires between flows.</p>
|
||||
<h3>Details</h3>
|
||||
<p>The node can be connected to any <code>link in</code> node that exists on any tab.
|
||||
Once connected, they behave as if they were wired together.</p>
|
||||
<p>The wires between link nodes are only displayed when a link node is selected.
|
||||
If there are any wires to other tabs, a virtual node is show that can be clicked
|
||||
on to jump to the appropriate tab.</p>
|
||||
<p>This node can be configured to either send messages to all <code>link in</code>
|
||||
nodes it is connected to, or to send a response back to the <code>link call</code>
|
||||
node that triggered the flow.</p>
|
||||
<p>When in 'send to all' mode, the wires between link nodes are only displayed when
|
||||
the node is selected. If there are any wires to other tabs, a virtual node
|
||||
is shown that can be clicked on to jump to the appropriate tab.</p>
|
||||
<p><b>Note: </b>Links cannot be created going into, or out of, a subflow.</p>
|
||||
</script>
|
||||
|
||||
<script type="text/html" data-help-name="link call">
|
||||
<p>Calls a flow that starts with a <code>link in</code> node and passes on the response.</p>
|
||||
<h3>Details</h3>
|
||||
<p>This node can be connected to a <code>link in</code> node that exists on any tab.
|
||||
The flow connected to that node must end with a <code>link out</code> node configured
|
||||
in 'return' mode.</p>
|
||||
<p>When this node receives a message, it is passed to the connected <code>link in</code> node.
|
||||
It then waits for a response which it then sends on.</o>
|
||||
<p>If no response is received within the configured timeout, default 30 seconds, the node
|
||||
will log an error that can be caught using the <code>catch</code> node.</p>
|
||||
</script>
|
||||
|
@@ -159,7 +159,15 @@
|
||||
},
|
||||
"link": {
|
||||
"linkIn": "link in",
|
||||
"linkOut": "link out"
|
||||
"linkOut": "link out",
|
||||
"linkCall": "link call",
|
||||
"linkOutReturn": "link return",
|
||||
"outMode": "Mode",
|
||||
"sendToAll": "Send to all connected link nodes",
|
||||
"returnToCaller": "Return to calling link node",
|
||||
"error": {
|
||||
"missingReturn": "Missing return node information"
|
||||
}
|
||||
},
|
||||
"tls": {
|
||||
"tls": "TLS configuration",
|
||||
|
Reference in New Issue
Block a user