mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
commit
da114fa3a5
@ -448,12 +448,18 @@ RED.nodes = (function() {
|
|||||||
doZFilter = true;
|
doZFilter = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var objectLookup = false;
|
||||||
if (searchSet === null) {
|
if (searchSet === null) {
|
||||||
searchSet = nodes;
|
searchSet = Object.keys(nodes);
|
||||||
|
objectLookup = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (var n=0;n<searchSet.length;n++) {
|
for (var n=0;n<searchSet.length;n++) {
|
||||||
var node = searchSet[n];
|
var node = searchSet[n];
|
||||||
|
if (objectLookup) {
|
||||||
|
node = nodes[node];
|
||||||
|
}
|
||||||
if (filter.hasOwnProperty("type") && node.type !== filter.type) {
|
if (filter.hasOwnProperty("type") && node.type !== filter.type) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1118,8 +1124,8 @@ RED.nodes = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((!n._def.defaults || !n._def.defaults.hasOwnProperty("l")) && n.hasOwnProperty('l')) {
|
if ((!n._def.defaults || !n._def.defaults.hasOwnProperty("l")) && n.hasOwnProperty('l')) {
|
||||||
var isLink = /^link (in|out)$/.test(node.type);
|
var showLabel = n._def.hasOwnProperty("showLabel")?n._def.showLabel:true;
|
||||||
if (isLink == n.l) {
|
if (showLabel != n.l) {
|
||||||
node.l = n.l;
|
node.l = n.l;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
* sublabel: 'Local', // a sub-label for the item
|
* sublabel: 'Local', // a sub-label for the item
|
||||||
* icon: 'fa fa-rocket', // (optional) icon for the item
|
* icon: 'fa fa-rocket', // (optional) icon for the item
|
||||||
* checkbox: true/false, // (optional) if present, display checkbox accordingly
|
* checkbox: true/false, // (optional) if present, display checkbox accordingly
|
||||||
|
* radio: 'group-name', // (optional) if present, display radio box - using group-name to set radio group
|
||||||
* selected: true/false, // (optional) whether the item is selected or not
|
* selected: true/false, // (optional) whether the item is selected or not
|
||||||
* children: [] | function(done,item) // (optional) an array of child items, or a function
|
* children: [] | function(done,item) // (optional) an array of child items, or a function
|
||||||
* // that will call the `done` callback with an array
|
* // that will call the `done` callback with an array
|
||||||
@ -640,6 +641,41 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
selectWrapper.appendTo(label)
|
selectWrapper.appendTo(label)
|
||||||
|
} else if (item.radio) {
|
||||||
|
var selectWrapper = $('<span class="red-ui-treeList-icon"></span>');
|
||||||
|
var cb = $('<input class="red-ui-treeList-radio" type="radio">').prop('name', item.radio).prop('checked',item.selected).appendTo(selectWrapper);
|
||||||
|
cb.on('click', function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
cb.on('change', function(e) {
|
||||||
|
item.selected = this.checked;
|
||||||
|
that._selected.forEach(function(selectedItem) {
|
||||||
|
if (selectedItem.radio === item.radio) {
|
||||||
|
selectedItem.treeList.label.removeClass("selected");
|
||||||
|
selectedItem.selected = false;
|
||||||
|
that._selected.delete(selectedItem);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (item.selected) {
|
||||||
|
that._selected.add(item);
|
||||||
|
} else {
|
||||||
|
that._selected.delete(item);
|
||||||
|
}
|
||||||
|
label.toggleClass("selected",this.checked);
|
||||||
|
that._trigger("select",e,item);
|
||||||
|
})
|
||||||
|
if (!item.children) {
|
||||||
|
label.on("click", function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
cb.trigger("click");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
item.treeList.select = function(v) {
|
||||||
|
if (v !== item.selected) {
|
||||||
|
cb.trigger("click");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selectWrapper.appendTo(label)
|
||||||
} else {
|
} else {
|
||||||
label.on("click", function(e) {
|
label.on("click", function(e) {
|
||||||
if (!that.options.multi) {
|
if (!that.options.multi) {
|
||||||
|
@ -151,7 +151,7 @@ RED.editor = (function() {
|
|||||||
valid = definition[property].hasOwnProperty("required") && !definition[property].required;
|
valid = definition[property].hasOwnProperty("required") && !definition[property].required;
|
||||||
} else {
|
} else {
|
||||||
var configNode = RED.nodes.node(value);
|
var configNode = RED.nodes.node(value);
|
||||||
valid = (configNode !== null && (configNode.valid == null || configNode.valid));
|
valid = (configNode && (configNode.valid == null || configNode.valid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return valid;
|
return valid;
|
||||||
|
@ -93,17 +93,20 @@
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
var showLabel = node._def.hasOwnProperty("showLabel")?node._def.showLabel:true;
|
||||||
|
|
||||||
if (!$("#node-input-show-label").prop('checked')) {
|
if (!$("#node-input-show-label").prop('checked')) {
|
||||||
// Not checked - hide label
|
// Not checked - hide label
|
||||||
if (!/^link (in|out)$/.test(node.type)) {
|
|
||||||
// Not a link node - default state is true
|
if (showLabel) {
|
||||||
|
// Default to show label
|
||||||
if (node.l !== false) {
|
if (node.l !== false) {
|
||||||
editState.changes.l = node.l
|
editState.changes.l = node.l
|
||||||
editState.changed = true;
|
editState.changed = true;
|
||||||
}
|
}
|
||||||
node.l = false;
|
node.l = false;
|
||||||
} else {
|
} else {
|
||||||
// A link node - default state is false
|
// Node has showLabel:false (eg link nodes)
|
||||||
if (node.hasOwnProperty('l') && node.l) {
|
if (node.hasOwnProperty('l') && node.l) {
|
||||||
editState.changes.l = node.l
|
editState.changes.l = node.l
|
||||||
editState.changed = true;
|
editState.changed = true;
|
||||||
@ -112,8 +115,8 @@
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Checked - show label
|
// Checked - show label
|
||||||
if (!/^link (in|out)$/.test(node.type)) {
|
if (showLabel) {
|
||||||
// Not a link node - default state is true
|
// Default to show label
|
||||||
if (node.hasOwnProperty('l') && !node.l) {
|
if (node.hasOwnProperty('l') && !node.l) {
|
||||||
editState.changes.l = node.l
|
editState.changes.l = node.l
|
||||||
editState.changed = true;
|
editState.changed = true;
|
||||||
@ -204,8 +207,8 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!node.hasOwnProperty("l")) {
|
if (!node.hasOwnProperty("l")) {
|
||||||
// Show label if type not link
|
// Show label unless def.showLabel set to false
|
||||||
node.l = !/^link (in|out)$/.test(node._def.type);
|
node.l = node._def.hasOwnProperty("showLabel")?node._def.showLabel:true;
|
||||||
}
|
}
|
||||||
$("#node-input-show-label").prop("checked",node.l).trigger("change");
|
$("#node-input-show-label").prop("checked",node.l).trigger("change");
|
||||||
|
|
||||||
|
@ -159,15 +159,15 @@ RED.view.tools = (function() {
|
|||||||
nodes.forEach(function(n) {
|
nodes.forEach(function(n) {
|
||||||
var modified = false;
|
var modified = false;
|
||||||
var oldValue = n.l === undefined?true:n.l;
|
var oldValue = n.l === undefined?true:n.l;
|
||||||
var isLink = /^link (in|out)$/.test(n._def.type);
|
var showLabel = n._def.hasOwnProperty("showLabel")?n._def.showLabel:true;
|
||||||
|
|
||||||
if (labelShown) {
|
if (labelShown) {
|
||||||
if (n.l === false || (isLink && !n.hasOwnProperty('l'))) {
|
if (n.l === false || (!showLabel && !n.hasOwnProperty('l'))) {
|
||||||
n.l = true;
|
n.l = true;
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ((!isLink && (!n.hasOwnProperty('l') || n.l === true)) || (isLink && n.l === true) ) {
|
if ((showLabel && (!n.hasOwnProperty('l') || n.l === true)) || (!showLabel && n.l === true) ) {
|
||||||
n.l = false;
|
n.l = false;
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
|
@ -413,7 +413,7 @@ RED.view = (function() {
|
|||||||
var nn = result.node;
|
var nn = result.node;
|
||||||
|
|
||||||
var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label");
|
var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label");
|
||||||
if (showLabel !== undefined && !/^link (in|out)$/.test(nn._def.type) && !nn._def.defaults.hasOwnProperty("l")) {
|
if (showLabel !== undefined && (nn._def.hasOwnProperty("showLabel")?nn._def.showLabel:true) && !nn._def.defaults.hasOwnProperty("l")) {
|
||||||
nn.l = showLabel;
|
nn.l = showLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1088,7 +1088,7 @@ RED.view = (function() {
|
|||||||
nn.x = point[0];
|
nn.x = point[0];
|
||||||
nn.y = point[1];
|
nn.y = point[1];
|
||||||
var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label");
|
var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label");
|
||||||
if (showLabel !== undefined && !/^link (in|out)$/.test(nn._def.type) && !nn._def.defaults.hasOwnProperty("l")) {
|
if (showLabel !== undefined && (nn._def.hasOwnProperty("showLabel")?nn._def.showLabel:true) && !nn._def.defaults.hasOwnProperty("l")) {
|
||||||
nn.l = showLabel;
|
nn.l = showLabel;
|
||||||
}
|
}
|
||||||
if (quickAddLink) {
|
if (quickAddLink) {
|
||||||
@ -1991,7 +1991,7 @@ RED.view = (function() {
|
|||||||
activeLinkNodes = {};
|
activeLinkNodes = {};
|
||||||
for (var i=0;i<movingSet.length();i++) {
|
for (var i=0;i<movingSet.length();i++) {
|
||||||
var msn = movingSet.get(i);
|
var msn = movingSet.get(i);
|
||||||
if ((msn.n.type === "link out" || msn.n.type === "link in") &&
|
if (((msn.n.type === "link out" && msn.n.mode !== 'return') || msn.n.type === "link in") &&
|
||||||
(msn.n.z === activeWorkspace)) {
|
(msn.n.z === activeWorkspace)) {
|
||||||
var linkNode = msn.n;
|
var linkNode = msn.n;
|
||||||
activeLinkNodes[linkNode.id] = linkNode;
|
activeLinkNodes[linkNode.id] = linkNode;
|
||||||
@ -4140,7 +4140,7 @@ RED.view = (function() {
|
|||||||
}
|
}
|
||||||
var numOutputs = d.outputs;
|
var numOutputs = d.outputs;
|
||||||
if (isLink && d.type === "link out") {
|
if (isLink && d.type === "link out") {
|
||||||
if (showAllLinkPorts===PORT_TYPE_OUTPUT || activeLinkNodes[d.id]) {
|
if (d.mode !== "return" && (showAllLinkPorts===PORT_TYPE_OUTPUT || activeLinkNodes[d.id])) {
|
||||||
numOutputs = 1;
|
numOutputs = 1;
|
||||||
} else {
|
} else {
|
||||||
numOutputs = 0;
|
numOutputs = 0;
|
||||||
|
@ -95,7 +95,8 @@
|
|||||||
color: $list-item-color;
|
color: $list-item-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.red-ui-treeList-checkbox {
|
input.red-ui-treeList-checkbox,
|
||||||
|
input.red-ui-treeList-radio {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,26 @@
|
|||||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
<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">
|
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
||||||
</div>
|
</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 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>
|
<div class="form-row node-input-link-row"></div>
|
||||||
</script>
|
</script>
|
||||||
@ -48,7 +68,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
var candidateNodes = RED.nodes.filterNodes({type:targetType});
|
var candidateNodes = RED.nodes.filterNodes({type:targetType});
|
||||||
|
|
||||||
var search = $("#node-input-link-target-filter").searchBox({
|
var search = $("#node-input-link-target-filter").searchBox({
|
||||||
style: "compact",
|
style: "compact",
|
||||||
delay: 300,
|
delay: 300,
|
||||||
@ -104,7 +123,8 @@
|
|||||||
node: n,
|
node: n,
|
||||||
label: n.name||n.id,
|
label: n.name||n.id,
|
||||||
selected: isChecked,
|
selected: isChecked,
|
||||||
checkbox: true
|
checkbox: node.type !== "link call",
|
||||||
|
radio: node.type === "link call"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -129,6 +149,7 @@
|
|||||||
function onEditSave(node) {
|
function onEditSave(node) {
|
||||||
var flows = treeList.treeList('data');
|
var flows = treeList.treeList('data');
|
||||||
node.links = [];
|
node.links = [];
|
||||||
|
if (node.type !== "link out" || $("node-input-mode").val() === 'link') {
|
||||||
flows.forEach(function(f) {
|
flows.forEach(function(f) {
|
||||||
f.children.forEach(function(n) {
|
f.children.forEach(function(n) {
|
||||||
if (n.selected) {
|
if (n.selected) {
|
||||||
@ -136,8 +157,14 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
}
|
||||||
node.oldLinks.sort();
|
node.oldLinks.sort();
|
||||||
node.links.sort();
|
node.links.sort();
|
||||||
|
|
||||||
|
if (node.type === "link call") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var nodeMap = {};
|
var nodeMap = {};
|
||||||
var length = Math.max(node.oldLinks.length,node.links.length);
|
var length = Math.max(node.oldLinks.length,node.links.length);
|
||||||
for (var i=0;i<length;i++) {
|
for (var i=0;i<length;i++) {
|
||||||
@ -195,6 +222,7 @@
|
|||||||
outputLabels: function(i) {
|
outputLabels: function(i) {
|
||||||
return this.name||this._("link.linkIn");
|
return this.name||this._("link.linkIn");
|
||||||
},
|
},
|
||||||
|
showLabel: false,
|
||||||
label: function() {
|
label: function() {
|
||||||
return this.name||this._("link.linkIn");
|
return this.name||this._("link.linkIn");
|
||||||
},
|
},
|
||||||
@ -211,25 +239,32 @@
|
|||||||
oneditresize: resizeNodeList
|
oneditresize: resizeNodeList
|
||||||
});
|
});
|
||||||
|
|
||||||
RED.nodes.registerType('link out',{
|
RED.nodes.registerType('link call',{
|
||||||
category: 'common',
|
category: 'common',
|
||||||
color:"#ddd",//"#87D8CF",
|
color:"#ddd",//"#87D8CF",
|
||||||
defaults: {
|
defaults: {
|
||||||
name: {value:""},
|
name: {value:""},
|
||||||
links: { value: [], type:"link in[]"}
|
links: { value: [], type:"link in[]"},
|
||||||
|
timeout: { value: "30", validate:RED.validators.number(true) }
|
||||||
},
|
},
|
||||||
align:"right",
|
inputs: 1,
|
||||||
inputs:1,
|
outputs: 1,
|
||||||
outputs:0,
|
icon: "link-call.svg",
|
||||||
icon: "link-out.svg",
|
|
||||||
inputLabels: function(i) {
|
inputLabels: function(i) {
|
||||||
return this.name||this._("link.linkOut");
|
return this.name||this._("link.linkCall");
|
||||||
},
|
},
|
||||||
label: function() {
|
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() {
|
labelStyle: function() {
|
||||||
return this.name?"node_label_italic":"";
|
return (this.name || this.links.length > 0)?"node_label_italic":"";
|
||||||
},
|
},
|
||||||
oneditprepare: function() {
|
oneditprepare: function() {
|
||||||
onEditPrepare(this,"link in");
|
onEditPrepare(this,"link in");
|
||||||
@ -237,8 +272,56 @@
|
|||||||
oneditsave: function() {
|
oneditsave: function() {
|
||||||
onEditSave(this);
|
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,
|
onadd: onAdd,
|
||||||
oneditresize: resizeNodeList
|
oneditresize: resizeNodeList
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
module.exports = function(RED) {
|
module.exports = function(RED) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const crypto = require("crypto");
|
||||||
|
|
||||||
function LinkInNode(n) {
|
function LinkInNode(n) {
|
||||||
RED.nodes.createNode(this,n);
|
RED.nodes.createNode(this,n);
|
||||||
var node = this;
|
var node = this;
|
||||||
@ -40,13 +42,91 @@ module.exports = function(RED) {
|
|||||||
function LinkOutNode(n) {
|
function LinkOutNode(n) {
|
||||||
RED.nodes.createNode(this,n);
|
RED.nodes.createNode(this,n);
|
||||||
var node = this;
|
var node = this;
|
||||||
|
var mode = n.mode || "link";
|
||||||
|
|
||||||
var event = "node:"+n.id;
|
var event = "node:"+n.id;
|
||||||
this.on("input", function(msg, send, done) {
|
this.on("input", function(msg, send, done) {
|
||||||
msg._event = event;
|
msg._event = event;
|
||||||
RED.events.emit(event,msg)
|
RED.events.emit(event,msg)
|
||||||
|
|
||||||
|
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);
|
send(msg);
|
||||||
done();
|
done();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
RED.nodes.registerType("link out",LinkOutNode);
|
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">
|
<script type="text/html" data-help-name="link out">
|
||||||
<p>Create virtual wires between flows.</p>
|
<p>Create virtual wires between flows.</p>
|
||||||
<h3>Details</h3>
|
<h3>Details</h3>
|
||||||
<p>The node can be connected to any <code>link in</code> node that exists on any tab.
|
<p>This node can be configured to either send messages to all <code>link in</code>
|
||||||
Once connected, they behave as if they were wired together.</p>
|
nodes it is connected to, or to send a response back to the <code>link call</code>
|
||||||
<p>The wires between link nodes are only displayed when a link node is selected.
|
node that triggered the flow.</p>
|
||||||
If there are any wires to other tabs, a virtual node is show that can be clicked
|
<p>When in 'send to all' mode, the wires between link nodes are only displayed when
|
||||||
on to jump to the appropriate tab.</p>
|
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>
|
<p><b>Note: </b>Links cannot be created going into, or out of, a subflow.</p>
|
||||||
</script>
|
</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": {
|
"link": {
|
||||||
"linkIn": "link in",
|
"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": "TLS configuration",
|
"tls": "TLS configuration",
|
||||||
|
@ -119,4 +119,69 @@ describe('link Node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("link-call node", function() {
|
||||||
|
it('should call link-in node and get response', function(done) {
|
||||||
|
var flow = [{id:"link-in-1", type:"link in", wires: [[ "func"]]},
|
||||||
|
{id:"func", type:"helper", wires: [["link-out-1"]]},
|
||||||
|
{id:"link-out-1", type:"link out", mode: "return"},
|
||||||
|
{id:"link-call", type:"link call", links:["link-in-1"], wires:[["n4"]]},
|
||||||
|
{id:"n4", type:"helper"} ];
|
||||||
|
helper.load(linkNode, flow, function() {
|
||||||
|
var func = helper.getNode("func");
|
||||||
|
func.on("input", function(msg, send, done) {
|
||||||
|
msg.payload = "123";
|
||||||
|
send(msg);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
var n1 = helper.getNode("link-call");
|
||||||
|
var n4 = helper.getNode("n4");
|
||||||
|
n4.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property('payload', '123');
|
||||||
|
done();
|
||||||
|
} catch(err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({payload:"hello"});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow nested link-call flows', function(done) {
|
||||||
|
var flow = [/** Multiply by 2 link flow **/
|
||||||
|
{id:"li1", type:"link in", wires: [[ "m2"]]},
|
||||||
|
{id:"m2", type:"helper", wires: [["lo1"]]},
|
||||||
|
{id:"lo1", type:"link out", mode: "return"},
|
||||||
|
/** Multiply by 3 link flow **/
|
||||||
|
{id:"li2", type:"link in", wires: [[ "m3"]]},
|
||||||
|
{id:"m3", type:"helper", wires: [["lo2"]]},
|
||||||
|
{id:"lo2", type:"link out", mode: "return"},
|
||||||
|
/** Multiply by 6 link flow **/
|
||||||
|
{id:"li3", type:"link in", wires: [[ "link-call-1"]]},
|
||||||
|
{id:"link-call-1", type:"link call", links:["m2"], wires:[["link-call-2"]]},
|
||||||
|
{id:"link-call-2", type:"link call", links:["m3"], wires:[["lo3"]]},
|
||||||
|
{id:"lo3", type:"link out", mode: "return"},
|
||||||
|
/** Test Flow Entry **/
|
||||||
|
{id:"link-call", type:"link call", links:["li3"], wires:[["n4"]]},
|
||||||
|
{id:"n4", type:"helper"} ];
|
||||||
|
helper.load(linkNode, flow, function() {
|
||||||
|
var m2 = helper.getNode("m2");
|
||||||
|
m2.on("input", function(msg, send, done) { msg.payload *= 2 ; send(msg); done(); })
|
||||||
|
var m3 = helper.getNode("m3");
|
||||||
|
m3.on("input", function(msg, send, done) { msg.payload *= 3 ; send(msg); done(); })
|
||||||
|
|
||||||
|
var n1 = helper.getNode("link-call");
|
||||||
|
var n4 = helper.getNode("n4");
|
||||||
|
n4.on("input", function(msg) {
|
||||||
|
try {
|
||||||
|
msg.should.have.property('payload', 24);
|
||||||
|
done();
|
||||||
|
} catch(err) {
|
||||||
|
done(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
n1.receive({payload:4});
|
||||||
|
});
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user