Merge branch 'dev' into pr_2439

This commit is contained in:
Nick O'Leary
2020-06-01 11:56:19 +01:00
437 changed files with 18418 additions and 4644 deletions

View File

@@ -14,16 +14,14 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="inject">
<script type="text/html" data-template-name="inject">
<div class="form-row">
<label for="node-input-payload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
<input type="text" id="node-input-payload" style="width:70%">
<input type="hidden" id="node-input-payloadType">
<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-topic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
<input type="text" id="node-input-topic">
<div class="form-row node-input-property-container-row">
<ol id="node-input-property-container"></ol>
</div>
<div class="form-row" id="node-once">
@@ -114,12 +112,7 @@
</div>
</div>
</div>
<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-tips" data-i18n="[html]inject.tip"></div>
</script>
<style>
.inject-time-row {
@@ -155,37 +148,76 @@
</style>
<script type="text/javascript">
(function() {
function resizeDialog(size) {
size = size || { height: $(".red-ui-tray-content form").height() }
var rows = $("#dialog-form>div:not(.node-input-property-container-row):visible");
var height = size.height;
for (var i=0; i<rows.length; i++) {
height -= $(rows[i]).outerHeight(true);
}
var editorRow = $("#dialog-form>div.node-input-property-container-row");
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
height += 16;
$("#node-input-property-container").editableList('height',height);
}
RED.nodes.registerType('inject',{
category: 'common',
color:"#a6bbcf",
defaults: {
name: {value:""},
topic: {value:""},
payload: {value:"", validate: RED.validators.typedInput("payloadType")},
payloadType: {value:"date"},
props:{value:[{p:"payload"},{p:"topic",vt:"str"}]},
repeat: {value:"", validate:function(v) { return ((v === "") || (RED.validators.number(v) && (v >= 0) && (v <= 2147483))) }},
crontab: {value:""},
once: {value:false},
onceDelay: {value:0.1}
onceDelay: {value:0.1},
topic: {value:""},
payload: {value:"", validate: RED.validators.typedInput("payloadType")},
payloadType: {value:"date"},
},
icon: "inject.svg",
inputs:0,
outputs:1,
outputLabels: function(index) {
var lab = this.payloadType;
if (lab === "json") {
try {
lab = typeof JSON.parse(this.payload);
if (lab === "object") {
if (Array.isArray(JSON.parse(this.payload))) { lab = "Array"; }
}
} catch(e) {
return this._("inject.label.invalid"); }
var lab = '';
// if only payload and topic - display payload type
// if only one property - show it's type
// if more than one property (other than payload and topic) - show "x properties" where x is the number of properties.
// this.props will not be an array for legacy inject nodes until they are re-deployed
//
var props = this.props;
if (!Array.isArray(props)) {
props = [
{ p:"payload", v: this.payload, vt: this.payloadType },
{ p:"topic", v: this.topic, vt: "str" }
]
}
var name = "inject.label."+lab;
var label = this._(name);
if (name !== label) {
return label;
if (props) {
for (var i=0,l=props.length; i<l; i++) {
if (i > 0) lab += "\n";
if (i === 5) {
lab += " + "+(props.length-4);
break;
}
lab += props[i].p+": ";
var propType = props[i].p === "payload"? this.payloadType : props[i].vt;
if (propType === "json") {
try {
var parsedProp = JSON.parse(props[i].p === "payload"? this.payload : props[i].v);
propType = typeof parsedProp;
if (propType === "object" && Array.isArray(parsedProp)) {
propType = "Array";
}
} catch(e) {
propType = "invalid";
}
}
lab += this._("inject.label."+propType);
}
}
return lab;
},
@@ -201,27 +233,33 @@
}
if (this.name) {
return this.name+suffix;
} else if (this.payloadType === "string" ||
this.payloadType === "str" ||
this.payloadType === "num" ||
this.payloadType === "bool" ||
this.payloadType === "json") {
if ((this.topic !== "") && ((this.topic.length + this.payload.length) <= 32)) {
return this.topic + ":" + this.payload+suffix;
} else if (this.payload.length > 0 && this.payload.length < 24) {
return this.payload+suffix;
}
var payload = this.payload || "";
var payloadType = this.payloadType || "str";
var topic = this.topic || "";
if (payloadType === "string" ||
payloadType === "str" ||
payloadType === "num" ||
payloadType === "bool" ||
payloadType === "json") {
if ((topic !== "") && ((topic.length + payload.length) <= 32)) {
return topic + ":" + payload+suffix;
} else if (payload.length > 0 && payload.length < 24) {
return payload+suffix;
} else {
return this._("inject.inject")+suffix;
}
} else if (this.payloadType === 'date') {
if ((this.topic !== "") && (this.topic.length <= 16)) {
return this.topic + ":" + this._("inject.timestamp")+suffix;
} else if (payloadType === 'date' || payloadType === 'bin' || payloadType === 'env') {
if ((topic !== "") && (topic.length <= 16)) {
return topic + ":" + this._('inject.label.'+payloadType)+suffix;
} else {
return this._("inject.timestamp")+suffix;
return this._('inject.label.'+payloadType)+suffix;
}
} else if (this.payloadType === 'flow' || this.payloadType === 'global') {
var key = RED.utils.parseContextKey(this.payload);
return this.payloadType+"."+key.key+suffix;
} else if (payloadType === 'flow' || payloadType === 'global') {
var key = RED.utils.parseContextKey(payload);
return payloadType+"."+key.key+suffix;
} else {
return this._("inject.inject")+suffix;
}
@@ -239,13 +277,6 @@
} else if (this.payloadType === 'string' || this.payloadType === 'none') {
this.payloadType = "str";
}
$("#node-input-payloadType").val(this.payloadType);
$("#node-input-payload").typedInput({
default: 'str',
typeField: $("#node-input-payloadType"),
types:['flow','global','str','num','bool','json','bin','date','env']
});
$("#inject-time-type-select").on("change", function() {
$("#node-input-crontab").val('');
@@ -259,6 +290,11 @@
$("#node-once").hide();
$("#node-input-once").prop('checked', false);
}
// Scroll down
var scrollDiv = $("#dialog-form").parent();
scrollDiv.scrollTop(scrollDiv.prop('scrollHeight'));
resizeDialog();
});
$("#node-input-once").on("change", function() {
@@ -378,7 +414,70 @@
$("#inject-time-type-select").val(repeattype);
$("#inject-time-row-"+repeattype).show();
$("#node-input-payload").typedInput('type',this.payloadType);
/* */
$('#node-input-property-container').css('min-height','120px').css('min-width','450px').editableList({
addItem: function(container,i,opt) {
var prop = opt;
if (!prop.hasOwnProperty('p')) {
prop = {p:"",v:"",vt:"str"};
}
container.css({
overflow: 'hidden',
whiteSpace: 'nowrap'
});
var row = $('<div/>').appendTo(container);
var propertyName = $('<input/>',{class:"node-input-prop-property-name",type:"text"})
.css("width","30%")
.appendTo(row)
.typedInput({types:['msg']});
$('<div/>',{style: 'display:inline-block; padding:0px 6px;'})
.text('=')
.appendTo(row);
var propertyValue = $('<input/>',{class:"node-input-prop-property-value",type:"text"})
.css("width","calc(70% - 30px)")
.appendTo(row)
.typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']});
propertyName.typedInput('value',prop.p);
propertyValue.typedInput('value',prop.v);
propertyValue.typedInput('type',prop.vt);
},
removable: true,
sortable: true
});
if (!this.props) {
var payload = {
p:'payload',
v: this.payload ? this.payload : '',
vt:this.payloadType ? this.payloadType : 'date'
};
var topic = {
p:'topic',
v: this.topic ? this.topic : '',
vt:'string'
}
this.props = [payload,topic];
}
for (var i=0; i<this.props.length; i++) {
var prop = this.props[i];
var newProp = { p: prop.p, v: prop.v, vt: prop.vt };
if (newProp.v === undefined) {
if (prop.p === 'payload') {
newProp.v = this.payload ? this.payload : '';
newProp.vt = this.payloadType ? this.payloadType : 'date';
} else if (prop.p === 'topic' && prop.vt === "str") {
newProp.v = this.topic ? this.topic : '';
}
}
$("#node-input-property-container").editableList('addItem',newProp);
}
$("#inject-time-type-select").trigger("change");
$("#inject-time-interval-time-start").trigger("change");
@@ -474,6 +573,34 @@
$("#node-input-repeat").val(repeat);
$("#node-input-crontab").val(crontab);
/* Gather the injected properties of the msg object */
var props = $("#node-input-property-container").editableList('items');
var node = this;
node.props= [];
delete node.payloadType;
delete node.payload;
delete node.topic;
props.each(function(i) {
var prop = $(this);
var p = {
p:prop.find(".node-input-prop-property-name").typedInput('value')
};
if (p.p) {
p.v = prop.find(".node-input-prop-property-value").typedInput('value');
p.vt = prop.find(".node-input-prop-property-value").typedInput('type');
if (p.p === "payload") { // save payload to old "legacy" property
node.payloadType = p.vt;
node.payload = p.v;
delete p.v;
delete p.vt;
} else if (p.p === "topic" && p.vt === "str") {
node.topic = p.v;
delete p.v;
}
node.props.push(p);
}
});
},
button: {
enabled: function() {
@@ -483,12 +610,7 @@
if (this.changed) {
return RED.notify(RED._("notification.warning", {message:RED._("notification.warnings.undeployedChanges")}),"warning");
}
var payload = this.payload;
if ((this.payloadType === 'flow') ||
(this.payloadType === 'global')) {
var key = RED.utils.parseContextKey(payload);
payload = this.payloadType+"."+key.key;
}
var label = this._def.label.call(this);
if (label.length > 30) {
label = label.substring(0,50)+"...";
@@ -514,7 +636,8 @@
}
});
}
}
},
oneditresize: resizeDialog
});
})();
</script>

View File

@@ -20,9 +20,32 @@ module.exports = function(RED) {
function InjectNode(n) {
RED.nodes.createNode(this,n);
this.topic = n.topic;
this.payload = n.payload;
this.payloadType = n.payloadType;
/* Handle legacy */
if(!Array.isArray(n.props)){
n.props = [];
n.props.push({
p:'payload',
v:n.payload,
vt:n.payloadType
});
n.props.push({
p:'topic',
v:n.topic,
vt:'str'
});
} else {
for (var i=0,l=n.props.length; i<l; i++) {
if (n.props[i].p === 'payload' && !n.props[i].hasOwnProperty('v')) {
n.props[i].v = n.payload;
n.props[i].vt = n.payloadType;
} else if (n.props[i].p === 'topic' && n.props[i].vt === 'str' && !n.props[i].hasOwnProperty('v')) {
n.props[i].v = n.topic;
}
}
}
this.props = n.props;
this.repeat = n.repeat;
this.crontab = n.crontab;
this.once = n.once;
@@ -31,65 +54,83 @@ module.exports = function(RED) {
this.cronjob = null;
var node = this;
node.props.forEach(function (prop) {
if (prop.vt === "jsonata") {
try {
var val = prop.v ? prop.v : "";
prop.exp = RED.util.prepareJSONataExpression(val, node);
}
catch (err) {
node.error(RED._("inject.errors.invalid-expr", {error:err.message}));
prop.exp = null;
}
}
});
if (node.repeat > 2147483) {
node.error(RED._("inject.errors.toolong", this));
delete node.repeat;
}
node.repeaterSetup = function () {
if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
this.repeat = this.repeat * 1000;
if (RED.settings.verbose) {
this.log(RED._("inject.repeat", this));
if (this.repeat && !isNaN(this.repeat) && this.repeat > 0) {
this.repeat = this.repeat * 1000;
if (RED.settings.verbose) {
this.log(RED._("inject.repeat", this));
}
this.interval_id = setInterval(function() {
node.emit("input", {});
}, this.repeat);
} else if (this.crontab) {
if (RED.settings.verbose) {
this.log(RED._("inject.crontab", this));
}
this.cronjob = new cron.CronJob(this.crontab, function() { node.emit("input", {}); }, null, true);
}
this.interval_id = setInterval(function() {
node.emit("input", {});
}, this.repeat);
} else if (this.crontab) {
if (RED.settings.verbose) {
this.log(RED._("inject.crontab", this));
}
this.cronjob = new cron.CronJob(this.crontab, function() { node.emit("input", {}); }, null, true);
}
};
if (this.once) {
this.onceTimeout = setTimeout( function() {
node.emit("input",{});
node.repeaterSetup();
node.emit("input",{});
node.repeaterSetup();
}, this.onceDelay);
} else {
node.repeaterSetup();
node.repeaterSetup();
}
this.on("input",function(msg) {
msg.topic = this.topic;
if (this.payloadType !== 'flow' && this.payloadType !== 'global') {
try {
if ( (this.payloadType == null && this.payload === "") || this.payloadType === "date") {
msg.payload = Date.now();
} else if (this.payloadType == null) {
msg.payload = this.payload;
} else if (this.payloadType === 'none') {
msg.payload = "";
} else {
msg.payload = RED.util.evaluateNodeProperty(this.payload,this.payloadType,this,msg);
}
this.send(msg);
msg = null;
} catch(err) {
this.error(err,msg);
}
} else {
RED.util.evaluateNodeProperty(this.payload,this.payloadType,this,msg, function(err,res) {
if (err) {
node.error(err,msg);
} else {
msg.payload = res;
node.send(msg);
}
this.on("input", function(msg) {
var errors = [];
});
this.props.forEach(p => {
var property = p.p;
var value = p.v ? p.v : '';
var valueType = p.vt ? p.vt : 'str';
if (!property) return;
if (valueType === "jsonata") {
if (p.exp) {
try {
var val = RED.util.evaluateJSONataExpression(p.exp, msg);
RED.util.setMessageProperty(msg, property, val, true);
}
catch (err) {
errors.push(err.message);
}
}
return;
}
try {
RED.util.setMessageProperty(msg,property,RED.util.evaluateNodeProperty(value, valueType, this, msg),true);
} catch (err) {
errors.push(err.toString());
}
});
if (errors.length) {
node.error(errors.join('; '), msg);
} else {
node.send(msg);
}
});
}

View File

@@ -1,30 +1,35 @@
<script type="text/x-red" data-template-name="debug">
<script type="text/html" data-template-name="debug">
<div class="form-row">
<label for="node-input-typed-complete"><i class="fa fa-list"></i> <span data-i18n="debug.output"></span></label>
<input id="node-input-typed-complete" type="text" style="width: 70%">
<input id="node-input-complete" type="hidden">
<input id="node-input-targetType" type="hidden">
</div>
<div class="form-row">
<label for="node-input-tosidebar"><i class="fa fa-random"></i> <span data-i18n="debug.to"></span></label>
<label for="node-input-tosidebar" style="width:70%">
<input type="checkbox" id="node-input-tosidebar" style="display:inline-block; width:22px; vertical-align:baseline;"><span data-i18n="debug.toSidebar"></span>
<input type="checkbox" id="node-input-tosidebar" style="display:inline-block; width:22px; vertical-align:top;"><span data-i18n="debug.toSidebar"></span>
</label>
</div>
<div class="form-row">
<label for="node-input-console"> </label>
<label for="node-input-console" style="width:70%">
<input type="checkbox" id="node-input-console" style="display:inline-block; width:22px; vertical-align:baseline;"><span data-i18n="debug.toConsole"></span>
<input type="checkbox" id="node-input-console" style="display:inline-block; width:22px; vertical-align:top;"><span data-i18n="debug.toConsole"></span>
</label>
</div>
<div class="form-row" id="node-tostatus-line">
<div class="form-row">
<label for="node-input-tostatus"> </label>
<label for="node-input-tostatus" style="width:70%">
<input type="checkbox" id="node-input-tostatus" style="display:inline-block; width:22px; vertical-align:baseline;"><span data-i18n="debug.toStatus"></span>
<input type="checkbox" id="node-input-tostatus" style="display:inline-block; width:22px; vertical-align:top;"><span data-i18n="debug.toStatus"></span>
</label>
</div>
<div class="form-row" id="node-tostatus-line">
<label for="node-input-typed-status"><i class="fa fa-ellipsis-h"></i> <span data-i18n="debug.status"></span></label>
<input id="node-input-typed-status" type="text" style="width: 70%">
<input id="node-input-statusVal" type="hidden">
<input id="node-input-statusType" type="hidden">
</div>
<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">
@@ -36,6 +41,36 @@
<script type="text/javascript">
(function() {
var subWindow = null;
function activateAjaxCall(node, active, successCallback) {
var url;
var body;
if (Array.isArray(node)) {
url = "debug/"+(active?"enable":"disable");
body = {nodes: node.map(function(n) { return n.id})}
node = node[0];
} else {
url = "debug/"+node.id+"/"+(active?"enable":"disable");
}
$.ajax({
url: url,
type: "POST",
data: body,
success: successCallback,
error: function(jqXHR,textStatus,errorThrown) {
if (jqXHR.status == 404) {
RED.notify(node._("common.notification.error", {message: node._("common.notification.errors.not-deployed")}),"error");
} else if (jqXHR.status === 0) {
RED.notify(node._("common.notification.error", {message: node._("common.notification.errors.no-response")}),"error");
} else {
// TODO where is the err.status comming from?
RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.unexpected",{status:err.status,message:err.response})}),"error");
}
}
});
}
RED.nodes.registerType('debug',{
category: 'common',
defaults: {
@@ -45,7 +80,9 @@
console: {value:false},
tostatus: {value:false},
complete: {value:"false", required:true},
targetType: {value:undefined}
targetType: {value:undefined},
statusVal: {value:""},
statusType: {value:"auto"}
},
label: function() {
var suffix = "";
@@ -73,38 +110,28 @@
onclick: function() {
var label = this.name||"debug";
var node = this;
$.ajax({
url: "debug/"+this.id+"/"+(this.active?"enable":"disable"),
type: "POST",
success: function(resp, textStatus, xhr) {
var historyEvent = {
t:'edit',
node:node,
changes:{
active:!node.active
},
dirty:node.dirty,
changed:node.changed
};
node.changed = true;
node.dirty = true;
RED.nodes.dirty(true);
RED.history.push(historyEvent);
RED.view.redraw();
if (xhr.status == 200) {
RED.notify(node._("debug.notification.activated",{label:label}),"success");
} else if (xhr.status == 201) {
RED.notify(node._("debug.notification.deactivated",{label:label}),"success");
}
},
error: function(jqXHR,textStatus,errorThrown) {
if (jqXHR.status == 404) {
RED.notify(node._("common.notification.error", {message: node._("common.notification.errors.not-deployed")}),"error");
} else if (jqXHR.status === 0) {
RED.notify(node._("common.notification.error", {message: node._("common.notification.errors.no-response")}),"error");
} else {
RED.notify(node._("common.notification.error",{message:node._("common.notification.errors.unexpected",{status:err.status,message:err.response})}),"error");
activateAjaxCall(node, node.active, function(resp, textStatus, xhr) {
var historyEvent = {
t:'edit',
node:node,
changes:{
active:!node.active
},
dirty:node.dirty,
changed:node.changed,
callback: function(ev) {
activateAjaxCall(ev.node, ev.node.active);
}
};
node.changed = true;
node.dirty = true;
RED.nodes.dirty(true);
RED.history.push(historyEvent);
RED.view.redraw();
if (xhr.status == 200) {
RED.notify(node._("debug.notification.activated",{label:label}),"success");
} else if (xhr.status == 201) {
RED.notify(node._("debug.notification.deactivated",{label:label}),"success");
}
});
}
@@ -266,6 +293,78 @@
RED.events.on("project:change", this.clearMessageList);
RED.actions.add("core:clear-debug-messages", function() { RED.debug.clearMessageList(true) });
RED.actions.add("core:activate-selected-debug-nodes", function() { setDebugNodeState(getSelectedDebugNodes(true), true); });
RED.actions.add("core:activate-all-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(true, true),true); });
RED.actions.add("core:activate-all-flow-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(true, false),true); });
RED.actions.add("core:deactivate-selected-debug-nodes", function() { setDebugNodeState(getSelectedDebugNodes(false), false); });
RED.actions.add("core:deactivate-all-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(false, true),false); });
RED.actions.add("core:deactivate-all-flow-debug-nodes", function() { setDebugNodeState(getMatchingDebugNodes(false, false),false); });
function getSelectedDebugNodes(state) {
var nodes = [];
var selection = RED.view.selection();
if (selection.nodes) {
selection.nodes.forEach(function(n) {
if (RED.nodes.subflow(n.z)) {
return;
}
if (n.type === 'debug' && n.active !== state) {
nodes.push(n);
} else if (n.type === 'group') {
nodes = nodes.concat( RED.group.getNodes(n,true).filter(function(n) {
return n.type === 'debug' && n.active !== state
}));
}
});
}
return nodes;
}
function getMatchingDebugNodes(state,globally) {
var nodes = [];
var filter = {type:"debug"};
if (!globally) {
filter.z = RED.workspaces.active();
}
var candidateNodes = RED.nodes.filterNodes(filter);
nodes = candidateNodes.filter(function(n) {
return n.active !== state && !RED.nodes.subflow(n.z)
})
return nodes;
}
function setDebugNodeState(nodes,state) {
var historyEvents = [];
if (nodes.length > 0) {
activateAjaxCall(nodes,false, function(resp, textStatus, xhr) {
nodes.forEach(function(n) {
historyEvents.push({
t: "edit",
node: n,
changed: n.changed,
changes: {
active: n.active
}
});
n.active = state;
n.changed = true;
n.dirty = true;
})
RED.history.push({
t: "multi",
events: historyEvents,
dirty: RED.nodes.dirty(),
callback: function() {
activateAjaxCall(nodes,nodes[0].active);
}
});
RED.nodes.dirty(true);
RED.view.redraw();
});
}
}
$("#red-ui-sidebar-debug-open").on("click", function(e) {
e.preventDefault();
subWindow = window.open(document.location.toString().replace(/[?#].*$/,"")+"debug/view/view.html"+document.location.search,"nodeREDDebugView","menubar=no,location=no,toolbar=no,chrome,height=500,width=600");
@@ -308,10 +407,20 @@
window.removeEventListener("message",this.handleWindowMessage);
RED.actions.remove("core:show-debug-tab");
RED.actions.remove("core:clear-debug-messages");
delete RED._debug;
},
oneditprepare: function() {
var autoType = {
value: "auto",
label: RED._("node-red:debug.autostatus"),
hasValue: false
};
$("#node-input-typed-status").typedInput({
default: "auto",
types:[autoType, "msg", "jsonata"],
typeField: $("#node-input-statusType")
});
var that = this;
var none = {
value: "none",
label: RED._("node-red:debug.none"),
@@ -321,6 +430,14 @@
this.tosidebar = true;
$("#node-input-tosidebar").prop('checked', true);
}
if (this.statusVal === undefined) {
this.statusVal = (this.complete === "false") ? "payload" : ((this.complete === "true") ? "payload" : this.complete+"");
$("#node-input-typed-status").typedInput('value',this.statusVal || "");
}
if (this.statusType === undefined) {
this.statusType = this.targetType;
$("#node-input-typed-status").typedInput('type',this.statusType || "auto");
}
if (typeof this.console === "string") {
this.console = (this.console == 'true');
$("#node-input-console").prop('checked', this.console);
@@ -331,6 +448,7 @@
label: RED._("node-red:debug.msgobj"),
hasValue: false
};
$("#node-input-typed-complete").typedInput({
default: "msg",
types:['msg', fullType, "jsonata"],
@@ -354,17 +472,29 @@
) {
$("#node-input-typed-complete").typedInput('value','payload');
}
if ($("#node-input-typed-complete").typedInput('type') === 'full') {
$("#node-tostatus-line").hide();
} else {
});
$("#node-input-tostatus").on('change',function() {
if ($(this).is(":checked")) {
if (!that.hasOwnProperty("statusVal") || that.statusVal === "") {
var type = $("#node-input-typed-complete").typedInput('type');
var comp = "payload";
if (type !== 'full') {
comp = $("#node-input-typed-complete").typedInput('value');
}
that.statusType = "auto";
that.statusVal = comp;
}
$("#node-input-typed-status").typedInput('type',that.statusType);
$("#node-input-typed-status").typedInput('value',that.statusVal);
$("#node-tostatus-line").show();
}
});
$("#node-input-complete").on('change',function() {
if ($("#node-input-typed-complete").typedInput('type') === 'full') {
else {
$("#node-tostatus-line").hide();
} else {
$("#node-tostatus-line").show();
that.statusType = "auto";
that.statusVal = "";
$("#node-input-typed-status").typedInput('type',that.statusType);
$("#node-input-typed-status").typedInput('value',that.statusVal);
}
});
},
@@ -375,6 +505,7 @@
} else {
$("#node-input-complete").val($("#node-input-typed-complete").typedInput('value'));
}
$("#node-input-statusVal").val($("#node-input-typed-status").typedInput('value'));
}
});
})();

View File

@@ -2,7 +2,7 @@ module.exports = function(RED) {
"use strict";
var util = require("util");
var events = require("events");
var path = require("path");
//var path = require("path");
var debuglength = RED.settings.debugMaxLength || 1000;
var useColors = RED.settings.debugUseColors || false;
util.inspect.styles.boolean = "red";
@@ -15,36 +15,20 @@ module.exports = function(RED) {
this.complete = hasEditExpression ? null : (n.complete||"payload").toString();
if (this.complete === "false") { this.complete = "payload"; }
this.console = ""+(n.console || false);
this.tostatus = (this.complete !== "true") && (n.tostatus || false);
this.tostatus = n.tostatus || false;
this.statusType = n.statusType || "msg";
this.statusVal = n.statusVal || this.complete;
this.tosidebar = n.tosidebar;
if (this.tosidebar === undefined) { this.tosidebar = true; }
this.severity = n.severity || 40;
this.active = (n.active === null || typeof n.active === "undefined") || n.active;
if (this.tostatus) { this.status({fill:"grey", shape:"ring"}); }
else { this.status({}); }
var hasStatExpression = (n.statusType === "jsonata");
var statExpression = hasStatExpression ? n.statusVal : null;
var node = this;
var levels = {
off: 1,
fatal: 10,
error: 20,
warn: 30,
info: 40,
debug: 50,
trace: 60,
audit: 98,
metric: 99
};
var colors = {
"0": "grey",
"10": "grey",
"20": "red",
"30": "yellow",
"40": "grey",
"50": "green",
"60": "blue"
};
var preparedEditExpression = null;
var preparedStatExpression = null;
if (editExpression) {
try {
preparedEditExpression = RED.util.prepareJSONataExpression(editExpression, this);
@@ -54,16 +38,22 @@ module.exports = function(RED) {
return;
}
}
if (statExpression) {
try {
preparedStatExpression = RED.util.prepareJSONataExpression(statExpression, this);
}
catch (e) {
node.error(RED._("debug.invalid-exp", {error: editExpression}));
return;
}
}
function prepareValue(msg, done) {
// Either apply the jsonata expression or...
if (preparedEditExpression) {
RED.util.evaluateJSONataExpression(preparedEditExpression, msg, (err, value) => {
if (err) {
done(RED._("debug.invalid-exp", {error: editExpression}));
} else {
done(null,{id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:value});
}
if (err) { done(RED._("debug.invalid-exp", {error: editExpression})); }
else { done(null,{id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:value}); }
});
} else {
// Extract the required message property
@@ -71,17 +61,67 @@ module.exports = function(RED) {
var output = msg[property];
if (node.complete !== "false" && typeof node.complete !== "undefined") {
property = node.complete;
try {
output = RED.util.getMessageProperty(msg,node.complete);
} catch(err) {
output = undefined;
}
try { output = RED.util.getMessageProperty(msg,node.complete); }
catch(err) { output = undefined; }
}
done(null,{id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, property:property, msg:output});
}
}
function prepareStatus(msg, done) {
if (node.statusType === "auto") {
if (node.complete === "true") {
done(null,{msg:msg.payload});
}
else {
prepareValue(msg,function(err,debugMsg) {
if (err) { node.error(err); return; }
done(null,{msg:debugMsg.msg});
});
}
}
else {
// Either apply the jsonata expression or...
if (preparedStatExpression) {
RED.util.evaluateJSONataExpression(preparedStatExpression, msg, (err, value) => {
if (err) { done(RED._("debug.invalid-exp", {error:editExpression})); }
else { done(null,{msg:value}); }
});
}
else {
// Extract the required message property
var output;
try { output = RED.util.getMessageProperty(msg,node.statusVal); }
catch(err) { output = undefined; }
done(null,{msg:output});
}
}
}
this.on("input", function(msg, send, done) {
if (node.tostatus === true) {
prepareStatus(msg, function(err,debugMsg) {
if (err) { node.error(err); return; }
var output = debugMsg.msg;
var st = (typeof output === 'string') ? output : util.inspect(output);
var fill = "grey";
var shape = "dot";
if (node.statusType === "auto") {
if (msg.hasOwnProperty("error")) {
fill = "red";
st = msg.error.message;
}
if (msg.hasOwnProperty("status")) {
if (msg.status.hasOwnProperty("fill")) { fill = msg.status.fill; }
if (msg.status.hasOwnProperty("shape")) { shape = msg.status.shape; }
if (msg.status.hasOwnProperty("text")) { st = msg.status.text; }
}
}
if (st.length > 32) { st = st.substr(0,32) + "..."; }
node.status({fill:fill, shape:shape, text:st});
});
}
if (this.complete === "true") {
// debug complete msg object
if (this.console === "true") {
@@ -91,7 +131,8 @@ module.exports = function(RED) {
sendDebug({id:node.id, z:node.z, _alias: node._alias, path:node._flow.path, name:node.name, topic:msg.topic, msg:msg});
}
done();
} else {
}
else {
prepareValue(msg,function(err,debugMsg) {
if (err) {
node.error(err);
@@ -107,12 +148,6 @@ module.exports = function(RED) {
node.log(util.inspect(output, {colors:useColors}));
}
}
if (node.tostatus === true) {
var st = (typeof output === 'string')?output:util.inspect(output);
var severity = node.severity;
if (st.length > 32) { st = st.substr(0,32) + "..."; }
node.status({fill:colors[severity], shape:"dot", text:st});
}
if (node.active) {
if (node.tosidebar == true) {
sendDebug(debugMsg);
@@ -150,24 +185,49 @@ module.exports = function(RED) {
});
RED.log.addHandler(DebugNode.logHandler);
RED.httpAdmin.post("/debug/:id/:state", RED.auth.needsPermission("debug.write"), function(req,res) {
var node = RED.nodes.getNode(req.params.id);
var state = req.params.state;
if (node !== null && typeof node !== "undefined" ) {
if (state === "enable") {
node.active = true;
res.sendStatus(200);
if (node.tostatus) { node.status({fill:"grey", shape:"dot"}); }
} else if (state === "disable") {
node.active = false;
res.sendStatus(201);
if (node.tostatus && node.hasOwnProperty("oldStatus")) {
node.oldStatus.shape = "dot";
node.status(node.oldStatus);
}
} else {
res.sendStatus(404);
function setNodeState(node,state) {
if (state) {
node.active = true;
if (node.tostatus) { node.status({fill:"grey", shape:"dot"}); }
} else {
node.active = false;
if (node.tostatus && node.hasOwnProperty("oldStatus")) {
node.oldStatus.shape = "dot";
node.status(node.oldStatus);
}
}
}
RED.httpAdmin.post("/debug/:state", RED.auth.needsPermission("debug.write"), function(req,res) {
var state = req.params.state;
if (state !== 'enable' && state !== 'disable') {
res.sendStatus(404);
return;
}
var nodes = req.body && req.body.nodes;
if (Array.isArray(nodes)) {
nodes.forEach(function(id) {
var node = RED.nodes.getNode(id);
if (node !== null && typeof node !== "undefined" ) {
setNodeState(node, state === "enable");
}
})
res.sendStatus(state === "enable" ? 200 : 201);
} else {
res.sendStatus(400);
}
})
RED.httpAdmin.post("/debug/:id/:state", RED.auth.needsPermission("debug.write"), function(req,res) {
var state = req.params.state;
if (state !== 'enable' && state !== 'disable') {
res.sendStatus(404);
return;
}
var node = RED.nodes.getNode(req.params.id);
if (node !== null && typeof node !== "undefined" ) {
setNodeState(node,state === "enable");
res.sendStatus(state === "enable" ? 200 : 201);
} else {
res.sendStatus(404);
}

View File

@@ -1,4 +1,4 @@
<script type="text/x-red" data-template-name="complete">
<script type="text/html" data-template-name="complete">
<div class="form-row node-input-target-row">
<button id="node-input-complete-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button>
</div>
@@ -89,7 +89,8 @@
node: n,
label: label,
sublabel: sublabel,
selected: isChecked
selected: isChecked,
checkbox: true
};
items.push(nodeItemMap[n.id]);
});

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="catch">
<script type="text/html" data-template-name="catch">
<div class="form-row">
<label style="width: auto" for="node-input-scope" data-i18n="catch.label.source"></label>
<select id="node-input-scope-select">
@@ -104,7 +104,8 @@
node: n,
label: label,
sublabel: sublabel,
selected: isChecked
selected: isChecked,
checkbox: true
};
items.push(nodeItemMap[n.id]);
});

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="status">
<script type="text/html" data-template-name="status">
<div class="form-row">
<label style="width: auto" for="node-input-scope" data-i18n="status.label.source"></label>
<select id="node-input-scope-select">
@@ -92,7 +92,8 @@
node: n,
label: label,
sublabel: sublabel,
selected: isChecked
selected: isChecked,
checkbox: true
};
items.push(nodeItemMap[n.id]);
});

View File

@@ -1,12 +1,12 @@
<script type="text/x-red" data-template-name="link in">
<script type="text/html" data-template-name="link in">
<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 node-input-link-row"></div>
</script>
<script type="text/x-red" data-template-name="link out">
<script type="text/html" data-template-name="link out">
<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">
@@ -83,7 +83,8 @@
id: n.id,
node: n,
label: n.name||n.id,
selected: isChecked
selected: isChecked,
checkbox: true
})
}
});

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="comment">
<script type="text/html" data-template-name="comment">
<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">

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="unknown">
<script type="text/html" data-template-name="unknown">
<div class="form-tips"><span data-i18n="[html]unknown.tip"></span></div>
</script>

View File

@@ -457,7 +457,7 @@ RED.debug = (function() {
var metaRow = $('<div class="red-ui-debug-msg-meta"></div>').appendTo(msg);
$('<span class="red-ui-debug-msg-date">'+ getTimestamp()+'</span>').appendTo(metaRow);
if (sourceNode) {
$('<a>',{href:"#",class:"red-ui-debug-msg-name"}).text('node: '+sanitize(o.name||sourceNode.name||sourceNode.id))
$('<a>',{href:"#",class:"red-ui-debug-msg-name"}).text('node: '+(o.name||sourceNode.name||sourceNode.id))
.appendTo(metaRow)
.on("click", function(evt) {
evt.preventDefault();

View File

@@ -1,22 +1,57 @@
<script type="text/x-red" data-template-name="function">
<script type="text/html" data-template-name="function">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<div style="display: inline-block; width: calc(100% - 105px)"><input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"></div>
</div>
<div class="form-row" style="margin-bottom: 0px;">
<label for="node-input-func"><i class="fa fa-wrench"></i> <span data-i18n="function.label.function"></span></label>
<input type="hidden" id="node-input-func" autofocus="autofocus">
<input type="hidden" id="node-input-noerr">
<div class="form-row">
<ul style="min-width: 600px; margin-bottom: 20px;" id="func-tabs"></ul>
</div>
<div class="form-row node-text-editor-row" style="position:relative">
<div style="position: absolute; right:0; bottom:calc(100% + 3px);"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-func-editor" ></div>
</div>
<div class="form-row" style="margin-bottom: 0px">
<label for="node-input-outputs"><i class="fa fa-random"></i> <span data-i18n="function.label.outputs"></span></label>
<input id="node-input-outputs" style="width: 60px;" value="1">
<div id="func-tabs-content" style="min-height: calc(100% - 95px);">
<div id="func-tab-init" style="display:none">
<div class="form-row" style="margin-bottom: 0px;">
<input type="hidden" id="node-input-initialize" autofocus="autofocus">
</div>
<div class="form-row node-text-editor-row" style="position:relative">
<div style="position: absolute; right:0; bottom: calc(100% + 3px);"><button id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
<div style="height: 250px; min-height:150px; margin-top: 30px;" class="node-text-editor" id="node-input-init-editor" ></div>
</div>
</div>
<div id="func-tab-body" style="display:none">
<div class="form-row" style="margin-bottom: 0px;">
<input type="hidden" id="node-input-func" autofocus="autofocus">
<input type="hidden" id="node-input-noerr">
</div>
<div class="form-row node-text-editor-row" style="position:relative">
<div style="position: absolute; right:0; bottom: calc(100% + 3px);"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
<div style="height: 220px; min-height:120px; margin-top: 30px;" class="node-text-editor" id="node-input-func-editor" ></div>
</div>
<div class="form-row" style="margin-bottom: 0px">
<label for="node-input-outputs"><i class="fa fa-random"></i> <span data-i18n="function.label.outputs"></span></label>
<input id="node-input-outputs" style="width: 60px;" value="1">
</div>
</div>
<div id="func-tab-finalize" style="display:none">
<div class="form-row" style="margin-bottom: 0px;">
<input type="hidden" id="node-input-finalize" autofocus="autofocus">
</div>
<div class="form-row node-text-editor-row" style="position:relative">
<div style="position: absolute; right:0; bottom: calc(100% + 3px);"><button id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
<div style="height: 250px; min-height:150px; margin-top: 30px;" class="node-text-editor" id="node-input-finalize-editor" ></div>
</div>
</div>
</div>
</script>
<script type="text/javascript">
@@ -27,7 +62,9 @@
name: {value:""},
func: {value:"\nreturn msg;"},
outputs: {value:1},
noerr: {value:0,required:true,validate:function(v) { return !v; }}
noerr: {value:0,required:true,validate:function(v) { return !v; }},
initialize: {value:""},
finalize: {value:""}
},
inputs:1,
outputs:1,
@@ -40,6 +77,28 @@
},
oneditprepare: function() {
var that = this;
var tabs = RED.tabs.create({
id: "func-tabs",
onchange: function(tab) {
$("#func-tabs-content").children().hide();
$("#" + tab.id).show();
}
});
tabs.addTab({
id: "func-tab-init",
label: that._("function.label.initialize")
});
tabs.addTab({
id: "func-tab-body",
label: that._("function.label.function")
});
tabs.addTab({
id: "func-tab-finalize",
label: that._("function.label.finalize")
});
tabs.activateTab("func-tab-body");
$( "#node-input-outputs" ).spinner({
min:0,
change: function(event, ui) {
@@ -50,74 +109,145 @@
}
});
this.editor = RED.editor.createEditor({
id: 'node-input-func-editor',
mode: 'ace/mode/nrjavascript',
value: $("#node-input-func").val(),
globals: {
msg:true,
context:true,
RED: true,
util: true,
flow: true,
global: true,
console: true,
Buffer: true,
setTimeout: true,
clearTimeout: true,
setInterval: true,
clearInterval: true
var buildEditor = function(id, value, defaultValue) {
var editor = RED.editor.createEditor({
id: id,
mode: 'ace/mode/nrjavascript',
value: value || defaultValue || "",
globals: {
msg:true,
context:true,
RED: true,
util: true,
flow: true,
global: true,
console: true,
Buffer: true,
setTimeout: true,
clearTimeout: true,
setInterval: true,
clearInterval: true
}
});
if (defaultValue && value === "") {
editor.moveCursorTo(defaultValue.split("\n").length - 1, 0);
}
});
return editor;
}
this.initEditor = buildEditor('node-input-init-editor',$("#node-input-initialize").val(),RED._("node-red:function.text.initialize"))
this.editor = buildEditor('node-input-func-editor',$("#node-input-func").val())
this.finalizeEditor = buildEditor('node-input-finalize-editor',$("#node-input-finalize").val(),RED._("node-red:function.text.finalize"))
RED.library.create({
url:"functions", // where to get the data from
type:"function", // the type of object the library is for
editor:this.editor, // the field name the main text body goes to
mode:"ace/mode/nrjavascript",
fields:['name','outputs'],
fields:[
'name', 'outputs',
{
name: 'initialize',
get: function() {
return that.initEditor.getValue();
},
set: function(v) {
that.initEditor.setValue(v||RED._("node-red:function.text.initialize"), -1);
}
},
{
name: 'finalize',
get: function() {
return that.finalizeEditor.getValue();
},
set: function(v) {
that.finalizeEditor.setValue(v||RED._("node-red:function.text.finalize"), -1);
}
},
{
name: 'info',
get: function() {
return that.infoEditor.getValue();
},
set: function(v) {
that.infoEditor.setValue(v||"", -1);
}
}
],
ext:"js"
});
this.editor.focus();
RED.popover.tooltip($("#node-function-expand-js"), RED._("node-red:common.label.expand"));
$("#node-function-expand-js").on("click", function(e) {
e.preventDefault();
var value = that.editor.getValue();
RED.editor.editJavaScript({
value: value,
width: "Infinity",
cursor: that.editor.getCursorPosition(),
mode: "ace/mode/nrjavascript",
complete: function(v,cursor) {
that.editor.setValue(v, -1);
that.editor.gotoLine(cursor.row+1,cursor.column,false);
setTimeout(function() {
that.editor.focus();
},300);
}
})
})
},
oneditsave: function() {
var annot = this.editor.getSession().getAnnotations();
this.noerr = 0;
$("#node-input-noerr").val(0);
for (var k=0; k < annot.length; k++) {
//console.log(annot[k].type,":",annot[k].text, "on line", annot[k].row);
if (annot[k].type === "error") {
$("#node-input-noerr").val(annot.length);
this.noerr = annot.length;
var expandButtonClickHandler = function(editor) {
return function(e) {
e.preventDefault();
var value = editor.getValue();
RED.editor.editJavaScript({
value: value,
width: "Infinity",
cursor: editor.getCursorPosition(),
mode: "ace/mode/nrjavascript",
complete: function(v,cursor) {
editor.setValue(v, -1);
editor.gotoLine(cursor.row+1,cursor.column,false);
setTimeout(function() {
editor.focus();
},300);
}
})
}
}
$("#node-input-func").val(this.editor.getValue());
this.editor.destroy();
delete this.editor;
$("#node-init-expand-js").on("click", expandButtonClickHandler(this.initEditor));
$("#node-function-expand-js").on("click", expandButtonClickHandler(this.editor));
$("#node-finalize-expand-js").on("click", expandButtonClickHandler(this.finalizeEditor));
RED.popover.tooltip($("#node-init-expand-js"), RED._("node-red:common.label.expand"));
RED.popover.tooltip($("#node-function-expand-js"), RED._("node-red:common.label.expand"));
RED.popover.tooltip($("#node-finalize-expand-js"), RED._("node-red:common.label.expand"));
},
oneditsave: function() {
var node = this;
var noerr = 0;
$("#node-input-noerr").val(0);
var disposeEditor = function(editorName,targetName,defaultValue) {
var editor = node[editorName];
var annot = editor.getSession().getAnnotations();
for (var k=0; k < annot.length; k++) {
if (annot[k].type === "error") {
noerr += annot.length;
break;
}
}
var val = editor.getValue();
if (defaultValue && val == defaultValue) {
val = "";
}
editor.destroy();
delete node[editorName];
$("#"+targetName).val(val);
}
disposeEditor("editor","node-input-func");
disposeEditor("initEditor","node-input-initialize", RED._("node-red:function.text.initialize"));
disposeEditor("finalizeEditor","node-input-finalize", RED._("node-red:function.text.finalize"));
$("#node-input-noerr").val(noerr);
this.noerr = noerr;
},
oneditcancel: function() {
this.editor.destroy();
delete this.editor;
var node = this;
node.editor.destroy();
delete node.editor;
node.initEditor.destroy();
delete node.initEditor;
node.finalizeEditor.destroy();
delete node.finalizeEditor;
},
oneditresize: function(size) {
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
@@ -129,6 +259,16 @@
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
$(".node-text-editor").css("height",height+"px");
this.editor.resize();
var height = size.height;
$("#node-input-init-editor").css("height", (height -105)+"px");
$("#node-input-func-editor").css("height", (height -145)+"px");
$("#node-input-finalize-editor").css("height", (height -105)+"px");
this.initEditor.resize();
this.editor.resize();
this.finalizeEditor.resize();
}
});
</script>

View File

@@ -57,22 +57,55 @@ module.exports = function(RED) {
}
}
function createVMOpt(node, kind) {
var opt = {
filename: 'Function node'+kind+':'+node.id+(node.name?' ['+node.name+']':''), // filename for stack traces
displayErrors: true
// Using the following options causes node 4/6 to not include the line number
// in the stack output. So don't use them.
// lineOffset: -11, // line number offset to be used for stack traces
// columnOffset: 0, // column number offset to be used for stack traces
};
return opt;
}
function updateErrorInfo(err) {
if (err.stack) {
var stack = err.stack.toString();
var m = /^([^:]+):([^:]+):(\d+).*/.exec(stack);
if (m) {
var line = parseInt(m[3]) -1;
var kind = "body:";
if (/setup/.exec(m[1])) {
kind = "setup:";
}
if (/cleanup/.exec(m[1])) {
kind = "cleanup:";
}
err.message += " ("+kind+"line "+line+")";
}
}
}
function FunctionNode(n) {
RED.nodes.createNode(this,n);
var node = this;
this.name = n.name;
this.func = n.func;
node.name = n.name;
node.func = n.func;
node.ini = n.initialize ? n.initialize : "";
node.fin = n.finalize ? n.finalize : "";
var handleNodeDoneCall = true;
// Check to see if the Function appears to call `node.done()`. If so,
// we will assume it is well written and does actually call node.done().
// Otherwise, we will call node.done() after the function returns regardless.
if (/node\.done\s*\(\s*\)/.test(this.func)) {
if (/node\.done\s*\(\s*\)/.test(node.func)) {
handleNodeDoneCall = false;
}
var functionText = "var results = null;"+
"results = (function(msg,__send__,__done__){ "+
"results = (async function(msg,__send__,__done__){ "+
"var __msgid__ = msg._msgid;"+
"var node = {"+
"id:__node__.id,"+
@@ -87,11 +120,13 @@ module.exports = function(RED) {
"send:function(msgs,cloneMsg){ __node__.send(__send__,__msgid__,msgs,cloneMsg);},"+
"done:__done__"+
"};\n"+
this.func+"\n"+
node.func+"\n"+
"})(msg,send,done);";
this.topic = n.topic;
this.outstandingTimers = [];
this.outstandingIntervals = [];
var finScript = null;
var finOpt = null;
node.topic = n.topic;
node.outstandingTimers = [];
node.outstandingIntervals = [];
var sandbox = {
console:console,
util:util,
@@ -182,12 +217,12 @@ module.exports = function(RED) {
arguments[0] = function() {
sandbox.clearTimeout(timerId);
try {
func.apply(this,arguments);
func.apply(node,arguments);
} catch(err) {
node.error(err,{});
}
};
timerId = setTimeout.apply(this,arguments);
timerId = setTimeout.apply(node,arguments);
node.outstandingTimers.push(timerId);
return timerId;
},
@@ -203,12 +238,12 @@ module.exports = function(RED) {
var timerId;
arguments[0] = function() {
try {
func.apply(this,arguments);
func.apply(node,arguments);
} catch(err) {
node.error(err,{});
}
};
timerId = setInterval.apply(this,arguments);
timerId = setInterval.apply(node,arguments);
node.outstandingIntervals.push(timerId);
return timerId;
},
@@ -226,37 +261,48 @@ module.exports = function(RED) {
sandbox.setTimeout(function(){ resolve(value); }, after);
});
};
sandbox.promisify = util.promisify;
}
var context = vm.createContext(sandbox);
try {
this.script = vm.createScript(functionText, {
filename: 'Function node:'+this.id+(this.name?' ['+this.name+']':''), // filename for stack traces
displayErrors: true
// Using the following options causes node 4/6 to not include the line number
// in the stack output. So don't use them.
// lineOffset: -11, // line number offset to be used for stack traces
// columnOffset: 0, // column number offset to be used for stack traces
});
this.on("input", function(msg,send,done) {
try {
var start = process.hrtime();
context.msg = msg;
context.send = send;
context.done = done;
var iniScript = null;
var iniOpt = null;
if (node.ini && (node.ini !== "")) {
var iniText = "(async function () {\n"+node.ini +"\n})();";
iniOpt = createVMOpt(node, " setup");
iniScript = new vm.Script(iniText, iniOpt);
}
node.script = vm.createScript(functionText, createVMOpt(node, ""));
if (node.fin && (node.fin !== "")) {
var finText = "(function () {\n"+node.fin +"\n})();";
finOpt = createVMOpt(node, " cleanup");
finScript = new vm.Script(finText, finOpt);
}
var promise = Promise.resolve();
if (iniScript) {
promise = iniScript.runInContext(context, iniOpt);
}
this.script.runInContext(context);
sendResults(this,send,msg._msgid,context.results,false);
function processMessage(msg, send, done) {
var start = process.hrtime();
context.msg = msg;
context.send = send;
context.done = done;
node.script.runInContext(context);
context.results.then(function(results) {
sendResults(node,send,msg._msgid,results,false);
if (handleNodeDoneCall) {
done();
}
var duration = process.hrtime(start);
var converted = Math.floor((duration[0] * 1e9 + duration[1])/10000)/100;
this.metric("duration", msg, converted);
node.metric("duration", msg, converted);
if (process.env.NODE_RED_FUNCTION_TIME) {
this.status({fill:"yellow",shape:"dot",text:""+converted});
node.status({fill:"yellow",shape:"dot",text:""+converted});
}
} catch(err) {
}).catch(err => {
if ((typeof err === "object") && err.hasOwnProperty("stack")) {
//remove unwanted part
var index = err.stack.search(/\n\s*at ContextifyScript.Script.runInContext/);
@@ -294,23 +340,67 @@ module.exports = function(RED) {
else {
done(JSON.stringify(err));
}
});
}
const RESOLVING = 0;
const RESOLVED = 1;
const ERROR = 2;
var state = RESOLVING;
var messages = [];
node.on("input", function(msg,send,done) {
if(state === RESOLVING) {
messages.push({msg:msg, send:send, done:done});
}
else if(state === RESOLVED) {
processMessage(msg, send, done);
}
});
this.on("close", function() {
node.on("close", function() {
if (finScript) {
try {
finScript.runInContext(context, finOpt);
}
catch (err) {
node.error(err);
}
}
while (node.outstandingTimers.length > 0) {
clearTimeout(node.outstandingTimers.pop());
}
while (node.outstandingIntervals.length > 0) {
clearInterval(node.outstandingIntervals.pop());
}
this.status({});
node.status({});
});
} catch(err) {
promise.then(function (v) {
var msgs = messages;
messages = [];
while (msgs.length > 0) {
msgs.forEach(function (s) {
processMessage(s.msg, s.send, s.done);
});
msgs = messages;
messages = [];
}
state = RESOLVED;
}).catch((error) => {
messages = [];
state = ERROR;
node.error(error);
});
}
catch(err) {
// eg SyntaxError - which v8 doesn't include line number information
// so we can't do better than this
this.error(err);
updateErrorInfo(err);
node.error(err);
}
}
RED.nodes.registerType("function",FunctionNode);
RED.library.register("functions");
};

View File

@@ -1,5 +1,10 @@
<script type="text/html" data-template-name="change">
<style>
ol#node-input-rule-container .red-ui-typedInput-container {
flex:1;
}
</style>
<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">
@@ -76,11 +81,6 @@
var replace = this._("change.action.replace");
var regex = this._("change.label.regex");
function resizeRule(rule) {
var newWidth = rule.width();
rule.find('.red-ui-typedInput').typedInput("width",newWidth-130);
}
$('#node-input-rule-container').css('min-height','150px').css('min-width','450px').editableList({
addItem: function(container,i,opt) {
var rule = opt;
@@ -106,10 +106,11 @@
overflow: 'hidden',
whiteSpace: 'nowrap'
});
var row1 = $('<div/>').appendTo(container);
var row2 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container);
var row3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container);
var row4 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container);
let fragment = document.createDocumentFragment();
var row1 = $('<div/>',{style:"display:flex;"}).appendTo(fragment);
var row2 = $('<div/>',{style:"display:flex;margin-top:8px;"}).appendTo(fragment);
var row3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(fragment);
var row4 = $('<div/>',{style:"display:flex;margin-top:8px;"}).appendTo(fragment);
var selectField = $('<select/>',{class:"node-input-rule-type",style:"width:110px; margin-right:10px;"}).appendTo(row1);
var selectOptions = [{v:"set",l:set},{v:"change",l:change},{v:"delete",l:del},{v:"move",l:move}];
@@ -124,41 +125,82 @@
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
.text(to)
.appendTo(row2);
var propertyValue = $('<input/>',{class:"node-input-rule-property-value",type:"text"})
function createPropertyValue() {
return $('<input/>',{class:"node-input-rule-property-value",type:"text"})
.appendTo(row2)
.typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']});
}
var row3_1 = $('<div/>').appendTo(row3);
var row3_1 = $('<div/>', {style:"display:flex;"}).appendTo(row3);
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
.text(search)
.appendTo(row3_1);
var fromValue = $('<input/>',{class:"node-input-rule-property-search-value",type:"text"})
function createFromValue() {
return $('<input/>',{class:"node-input-rule-property-search-value",type:"text"})
.appendTo(row3_1)
.typedInput({default:'str',types:['msg','flow','global','str','re','num','bool','env']});
}
var row3_2 = $('<div/>',{style:"margin-top:8px;"}).appendTo(row3);
var row3_2 = $('<div/>',{style:"display:flex;margin-top:8px;"}).appendTo(row3);
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
.text(replace)
.appendTo(row3_2);
var toValue = $('<input/>',{class:"node-input-rule-property-replace-value",type:"text"})
function createToValue() {
return $('<input/>',{class:"node-input-rule-property-replace-value",type:"text"})
.appendTo(row3_2)
.typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','env']});
}
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
.text(to)
.appendTo(row4);
var moveValue = $('<input/>',{class:"node-input-rule-property-move-value",type:"text"})
function createMoveValue() {
return $('<input/>',{class:"node-input-rule-property-move-value",type:"text"})
.appendTo(row4)
.typedInput({default:'msg',types:['msg','flow','global']});
}
let propertyValue = null;
let fromValue = null;
let toValue = null;
let moveValue = null;
selectField.on("change", function() {
var width = $("#node-input-rule-container").width();
var type = $(this).val();
if (propertyValue) {
propertyValue.typedInput('hide');
}
if (fromValue) {
fromValue.typedInput('hide');
}
if (toValue) {
toValue.typedInput('hide');
}
if (moveValue) {
moveValue.typedInput('hide');
}
if (type == "set") {
if(!propertyValue) {
propertyValue = createPropertyValue();
}
propertyValue.typedInput('show');
row2.show();
row3.hide();
row4.hide();
} else if (type == "change") {
if(!fromValue) {
fromValue = createFromValue();
}
fromValue.typedInput('show');
if(!toValue) {
toValue = createToValue();
}
toValue.typedInput('show');
row2.hide();
row3.show();
row4.hide();
@@ -167,30 +209,48 @@
row3.hide();
row4.hide();
} else if (type == "move") {
if(!moveValue) {
moveValue = createMoveValue();
}
moveValue.typedInput('show');
row2.hide();
row3.hide();
row4.show();
}
resizeRule(container);
});
selectField.val(rule.t);
propertyName.typedInput('value',rule.p);
propertyName.typedInput('type',rule.pt);
propertyValue.typedInput('value',rule.to);
propertyValue.typedInput('type',rule.tot);
moveValue.typedInput('value',rule.to);
moveValue.typedInput('type',rule.tot);
fromValue.typedInput('value',rule.from);
fromValue.typedInput('type',rule.fromt);
toValue.typedInput('value',rule.to);
toValue.typedInput('type',rule.tot);
if (rule.t == "set") {
if(!propertyValue) {
propertyValue = createPropertyValue();
}
propertyValue.typedInput('value',rule.to);
propertyValue.typedInput('type',rule.tot);
}
if (rule.t == "move") {
if(!moveValue) {
moveValue = createMoveValue();
}
moveValue.typedInput('value',rule.to);
moveValue.typedInput('type',rule.tot);
}
if (rule.t == "change") {
if(!fromValue) {
fromValue = createFromValue();
}
fromValue.typedInput('value',rule.from);
fromValue.typedInput('type',rule.fromt);
if (!toValue) {
toValue = createToValue();
}
toValue.typedInput('value',rule.to);
toValue.typedInput('type',rule.tot);
}
selectField.change();
var newWidth = $("#node-input-rule-container").width();
resizeRule(container);
container[0].appendChild(fragment);
},
resizeItem: resizeRule,
removable: true,
sortable: true
});

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="range">
<script type="text/html" data-template-name="range">
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:calc(70% - 1px)"/>

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="template">
<script type="text/html" data-template-name="template">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<div style="display: inline-block; width: calc(100% - 105px)"><input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"></div>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="delay">
<script type="text/html" data-template-name="delay">
<div class="form-row">
<label for="node-input-delay-action"><i class="fa fa-tasks"></i> <span data-i18n="delay.action"></span></label>
<select id="node-input-delay-action" style="width:270px !important">

View File

@@ -158,6 +158,7 @@ module.exports = function(RED) {
clearInterval(node.intervalID);
node.intervalID = -1;
}
delete node.lastSent;
node.buffer = [];
node.status({text:"reset"});
return;

View File

@@ -14,11 +14,11 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="trigger">
<script type="text/html" data-template-name="trigger">
<div class="form-row">
<label data-i18n="trigger.send" for="node-input-op1"></label>
<input type="hidden" id="node-input-op1type">
<input style="width: 70%" type="text" id="node-input-op1" placeholder="1">
<input style="width:70%" type="text" id="node-input-op1" placeholder="1">
</div>
<div class="form-row">
<label data-i18n="trigger.then"></label>
@@ -40,13 +40,17 @@
</div>
<div class="form-row node-type-wait">
<label></label>
<input type="checkbox" id="node-input-extend" style="margin-left: 0px; vertical-align: top; width: auto !important;"> <label style="width:auto !important;" for="node-input-extend" data-i18n="trigger.extend"></label>
<input type="checkbox" id="node-input-extend" style="margin-left:0px; vertical-align:top; width:auto !important;"> <label style="width:auto !important;" for="node-input-extend" data-i18n="trigger.extend"></label>
</div>
<div class="form-row node-type-wait">
<label data-i18n="trigger.then-send"></label>
<input type="hidden" id="node-input-op2type">
<input style="width: 70%" type="text" id="node-input-op2" placeholder="0">
<input style="width:70%" type="text" id="node-input-op2" placeholder="0">
</div>
<div class="form-row" id="node-second-output">
<label></label>
<input type="checkbox" id="node-input-second" style="margin-left: 0px; vertical-align: top; width: auto !important;"> <label style="width:auto !important;" for="node-input-second" data-i18n="trigger.second"></label>
</div>
<div class="form-row">
<label data-i18n="trigger.label.reset" style="width:auto"></label>
<div style="display:inline-block; width:70%;vertical-align:top">
@@ -58,10 +62,13 @@
<br/>
<div class="form-row">
<label data-i18n="trigger.for" for="node-input-bytopic"></label>
<select id="node-input-bytopic">
<select id="node-input-bytopic" style="width:120px;">
<option value="all" data-i18n="trigger.alltopics"></option>
<option value="topic" data-i18n="trigger.bytopics"></option>
</select>
<span class="form-row" id="node-stream-topic">
<input type="text" id="node-input-topic" style="width:46%;"/>
</span>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
@@ -74,6 +81,7 @@
category: 'function',
color:"#E6E0F8",
defaults: {
name: {value:""},
op1: {value:"1", validate: RED.validators.typedInput("op1type")},
op2: {value:"0", validate: RED.validators.typedInput("op2type")},
op1type: {value:"val"},
@@ -82,8 +90,9 @@
extend: {value:"false"},
units: {value:"ms"},
reset: {value:""},
bytopic: {value: "all"},
name: {value:""}
bytopic: {value:"all"},
topic: {value:"topic",required:true},
outputs: {value:1}
},
inputs:1,
outputs:1,
@@ -103,17 +112,48 @@
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
var that = this;
if (this.topic === undefined) { $("#node-input-topic").val("topic"); }
$("#node-input-topic").typedInput({default:'msg',types:['msg']});
$("#node-input-bytopic").on("change", function() {
if ($("#node-input-bytopic").val() === "all") {
$("#node-stream-topic").hide();
} else {
$("#node-stream-topic").show();
}
});
if (this.outputs == 2) { $("#node-input-second").prop('checked', true) }
else { $("#node-input-second").prop('checked', false) }
$("#node-input-second").change(function() {
if ($("#node-input-second").is(":checked")) {
that.outputs = 2;
}
else {
that.outputs = 1;
}
});
$("#node-then-type").on("change", function() {
if ($(this).val() == "block") {
$(".node-type-wait").hide();
$(".node-type-duration").hide();
$("#node-second-output").hide();
$("#node-input-second").prop('checked', false);
that.outputs = 1;
}
else if ($(this).val() == "loop") {
if ($("#node-input-duration").val() == 0) { $("#node-input-duration").val(250); }
$(".node-type-wait").hide();
$(".node-type-duration").show();
$("#node-second-output").hide();
$("#node-input-second").prop('checked', false);
that.outputs = 1;
} else {
if ($("#node-input-duration").val() == 0) { $("#node-input-duration").val(250); }
$(".node-type-wait").show();
$(".node-type-duration").show();
$("#node-second-output").show();
}
});
@@ -176,8 +216,6 @@
if ($("#node-then-type").val() == "loop") {
$("#node-input-duration").val($("#node-input-duration").val() * -1);
}
}
});
</script>

View File

@@ -24,6 +24,8 @@ module.exports = function(RED) {
this.op2 = n.op2 || "0";
this.op1type = n.op1type || "str";
this.op2type = n.op2type || "str";
this.second = (n.outputs == 2) ? true : false;
this.topic = n.topic || "topic";
if (this.op1type === 'val') {
if (this.op1 === 'true' || this.op1 === 'false') {
@@ -76,6 +78,7 @@ module.exports = function(RED) {
var node = this;
node.topics = {};
var npay = {};
var pendingMessages = [];
var activeMessagePromise = null;
var processMessageQueue = function(msg) {
@@ -110,8 +113,15 @@ module.exports = function(RED) {
processMessageQueue(msg);
});
var stat = function() {
var l = Object.keys(node.topics).length;
if (l === 0) { return {} }
else if (l === 1) { return {fill:"blue",shape:"dot"} }
else return {fill:"blue",shape:"dot",text:l};
}
var processMessage = function(msg) {
var topic = msg.topic || "_none";
var topic = RED.util.getMessageProperty(msg,node.topic) || "_none";
var promise;
if (node.bytopic === "all") { topic = "_none"; }
node.topics[topic] = node.topics[topic] || {};
@@ -119,12 +129,13 @@ module.exports = function(RED) {
if (node.loop === true) { clearInterval(node.topics[topic].tout); }
else { clearTimeout(node.topics[topic].tout); }
delete node.topics[topic];
node.status({});
node.status(stat());
}
else {
if (node.op2type === "payl") { npay[topic] = RED.util.cloneMessage(msg); }
if (((!node.topics[topic].tout) && (node.topics[topic].tout !== 0)) || (node.loop === true)) {
promise = Promise.resolve();
if (node.op2type === "pay" || node.op2type === "payl") { node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); }
if (node.op2type === "pay") { node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); }
else if (node.op2Templated) { node.topics[topic].m2 = mustache.render(node.op2,msg); }
else if (node.op2type !== "nul") {
promise = new Promise((resolve,reject) => {
@@ -186,22 +197,30 @@ module.exports = function(RED) {
});
}
promise.then(() => {
msg2.payload = node.topics[topic].m2;
if (node.op2type === "payl") {
if (node.second === true) { node.send([null,npay[topic]]); }
else { node.send(npay[topic]); }
delete npay[topic];
}
else {
msg2.payload = node.topics[topic].m2;
if (node.second === true) { node.send([null,msg2]); }
else { node.send(msg2); }
}
delete node.topics[topic];
node.send(msg2);
node.status({});
node.status(stat());
}).catch(err => {
node.error(err);
});
} else {
delete node.topics[topic];
node.status({});
node.status(stat());
}
}, node.duration);
}
}
node.status({fill:"blue",shape:"dot",text:" "});
node.status(stat());
if (node.op1type !== "nul") { node.send(RED.util.cloneMessage(msg)); }
});
});
@@ -237,16 +256,17 @@ module.exports = function(RED) {
}
}
delete node.topics[topic];
node.status({});
node.send(msg2);
node.status(stat());
if (node.second === true) { node.send([null,msg2]); }
else { node.send(msg2); }
}).catch(err => {
node.error(err);
});
}, node.duration);
}
else {
if (node.op2type === "payl") { node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); }
}
// else {
// if (node.op2type === "payl") {node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); }
// }
}
return Promise.resolve();
}
@@ -259,7 +279,7 @@ module.exports = function(RED) {
delete node.topics[t];
}
}
node.status({});
node.status(stat());
});
}
RED.nodes.registerType("trigger",TriggerNode);

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="exec">
<script type="text/html" data-template-name="exec">
<div class="form-row">
<label for="node-input-command"><i class="fa fa-file"></i> <span data-i18n="exec.label.command"></span></label>
<input type="text" id="node-input-command" data-i18n="[placeholder]exec.label.command">

View File

@@ -145,7 +145,7 @@ module.exports = function(RED) {
if (error.signal) { msg3.payload.signal = error.signal; }
if (error.code === null) { node.status({fill:"red",shape:"dot",text:"killed"}); }
else { node.status({fill:"red",shape:"dot",text:"error:"+error.code}); }
node.log('error:' + error);
if (RED.settings.verbose) { node.log('error:' + error); }
}
else if (node.oldrc === "false") {
msg3 = RED.util.cloneMessage(msg);

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="tls-config">
<script type="text/html" data-template-name="tls-config">
<div class="form-row" class="hide" id="node-config-row-uselocalfiles">
<input type="checkbox" id="node-config-input-uselocalfiles" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-config-input-uselocalfiles" style="width: 70%;"><span data-i18n="tls.label.use-local-files"></label>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="http proxy">
<script type="text/html" data-template-name="http proxy">
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-config-input-name">

View File

@@ -11,7 +11,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="mqtt in">
<script type="text/html" data-template-name="mqtt in">
<div class="form-row">
<label for="node-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
<input type="text" id="node-input-broker">
@@ -75,7 +75,7 @@
});
</script>
<script type="text/x-red" data-template-name="mqtt out">
<script type="text/html" data-template-name="mqtt out">
<div class="form-row">
<label for="node-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
<input type="text" id="node-input-broker">
@@ -303,7 +303,7 @@
return this.name;
}
var b = this.broker;
if (b === "") { b = "undefined"; }
if (!b) { b = "undefined"; }
var lab = "";
lab = (this.clientid?this.clientid+"@":"")+b;
if (b.indexOf("://") === -1){

View File

@@ -153,7 +153,12 @@ module.exports = function(RED) {
this.brokerurl="mqtt://";
}
if (this.broker !== "") {
this.brokerurl = this.brokerurl+this.broker+":";
//Check for an IPv6 address
if (/(?:^|(?<=\s))(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(?=\s|$)/.test(this.broker)) {
this.brokerurl = this.brokerurl+"["+this.broker+"]:";
} else {
this.brokerurl = this.brokerurl+this.broker+":";
}
// port now defaults to 1883 if unset.
if (!this.port){
this.brokerurl = this.brokerurl+"1883";
@@ -464,6 +469,7 @@ module.exports = function(RED) {
this.broker = n.broker;
this.brokerConn = RED.nodes.getNode(this.broker);
var node = this;
var chk = /[\+#]/;
if (this.brokerConn) {
this.status({fill:"red",shape:"ring",text:"node-red:common.status.disconnected"});
@@ -482,6 +488,7 @@ module.exports = function(RED) {
}
if ( msg.hasOwnProperty("payload")) {
if (msg.hasOwnProperty("topic") && (typeof msg.topic === "string") && (msg.topic !== "")) { // topic must exist
if (chk.test(msg.topic)) { node.warn(RED._("mqtt.errors.invalid-topic")); }
this.brokerConn.publish(msg, done); // send the message
} else {
node.warn(RED._("mqtt.errors.invalid-topic"));

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="http in">
<script type="text/html" data-template-name="http in">
<div class="form-row">
<label for="node-input-method"><i class="fa fa-tasks"></i> <span data-i18n="httpin.label.method"></span></label>
<select type="text" id="node-input-method" style="width:70%;">
@@ -45,7 +45,7 @@
<div id="node-input-tip" class="form-tips"><span data-i18n="httpin.tip.in"></span><code><span id="node-input-path"></span></code>.</div>
</script>
<script type="text/x-red" data-template-name="http response">
<script type="text/html" data-template-name="http response">
<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">

View File

@@ -228,7 +228,7 @@ module.exports = function(RED) {
var diff = process.hrtime(startAt);
var ms = diff[0] * 1e3 + diff[1] * 1e-6;
var metricResponseTime = ms.toFixed(3);
var metricContentLength = res._headers["content-length"];
var metricContentLength = res.getHeader("content-length");
//assuming that _id has been set for res._metrics in HttpOut node!
node.metric("response.time.millis", {_msgid:res._msgid} , metricResponseTime);
node.metric("response.content-length.bytes", {_msgid:res._msgid} , metricContentLength);

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="http request">
<script type="text/html" data-template-name="http request">
<div class="form-row">
<label for="node-input-method"><i class="fa fa-tasks"></i> <span data-i18n="httpin.label.method"></span></label>
<select type="text" id="node-input-method" style="width:70%;">
@@ -22,6 +22,7 @@
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
<option value="HEAD">HEAD</option>
<option value="use" data-i18n="httpin.setby"></option>
</select>
</div>
@@ -32,8 +33,12 @@
</div>
<div class="form-row node-input-paytoqs-row">
<input type="checkbox" id="node-input-paytoqs" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-paytoqs" style="width: auto" data-i18n="httpin.label.paytoqs"></label>
<label for="node-input-paytoqs"><span data-i18n="common.label.payload"></span></label>
<select id="node-input-paytoqs" style="width: 70%;">
<option value="ignore" data-i18n="httpin.label.paytoqs.ignore"></option>
<option value="query" data-i18n="httpin.label.paytoqs.query"></option>
<option value="body" data-i18n="httpin.label.paytoqs.body"></option>
</select>
</div>
<div class="form-row">
@@ -167,6 +172,13 @@
$(".node-input-paytoqs-row").hide();
}
});
if (this.paytoqs === true || this.paytoqs == "query") {
$("#node-input-paytoqs").val("query");
} else if (this.paytoqs === "body") {
$("#node-input-paytoqs").val("body");
} else {
$("#node-input-paytoqs").val("ignore");
}
if (this.authType) {
$('#node-input-useAuth').prop('checked', true);
$("#node-input-authType-select").val(this.authType);

View File

@@ -28,7 +28,8 @@ module.exports = function(RED) {
var nodeUrl = n.url;
var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1;
var nodeMethod = n.method || "GET";
var paytoqs = n.paytoqs;
var paytoqs = false;
var paytobody = false;
var nodeHTTPPersistent = n["persist"];
if (n.tls) {
var tlsNode = RED.nodes.getNode(n.tls);
@@ -38,6 +39,10 @@ module.exports = function(RED) {
if (RED.settings.httpRequestTimeout) { this.reqTimeout = parseInt(RED.settings.httpRequestTimeout) || 120000; }
else { this.reqTimeout = 120000; }
if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; }
else if (n.paytoqs === "body") { paytobody = true; }
var prox, noprox;
if (process.env.http_proxy) { prox = process.env.http_proxy; }
if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; }
@@ -277,6 +282,14 @@ module.exports = function(RED) {
node.error(RED._("httpin.errors.invalid-payload"),msg);
nodeDone();
return;
}
} else if ( method == "GET" && typeof msg.payload !== "undefined" && paytobody) {
if (typeof msg.payload === "object") {
opts.body = JSON.stringify(msg.payload);
} else if (typeof msg.payload == "number") {
opts.body = msg.payload+"";
} else if (typeof msg.payload === "string" || Buffer.isBuffer(msg.payload)) {
opts.body = msg.payload;
}
}

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<!-- WebSocket Input Node -->
<script type="text/x-red" data-template-name="websocket in">
<script type="text/html" data-template-name="websocket in">
<div class="form-row">
<label for="node-input-mode"><i class="fa fa-dot-circle-o"></i> <span data-i18n="websocket.label.type"></span></label>
<select id="node-input-mode">
@@ -163,7 +163,7 @@
if (root === "") {
$("#node-config-ws-tip").hide();
} else {
$("#node-config-ws-path").html(root);
$("#node-config-ws-path").html(RED._("node-red:websocket.tip.path2", { path: root }));
$("#node-config-ws-tip").show();
}
}
@@ -198,7 +198,7 @@
</script>
<!-- WebSocket out Node -->
<script type="text/x-red" data-template-name="websocket out">
<script type="text/html" data-template-name="websocket out">
<div class="form-row">
<label for="node-input-mode"><i class="fa fa-dot-circle-o"></i> <span data-i18n="websocket.label.type"></span></label>
<select id="node-input-mode">
@@ -221,7 +221,7 @@
</script>
<!-- WebSocket Server configuration node -->
<script type="text/x-red" data-template-name="websocket-listener">
<script type="text/html" data-template-name="websocket-listener">
<div class="form-row">
<label for="node-config-input-path"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.path"></span></label>
<input id="node-config-input-path" type="text" placeholder="/ws/example">
@@ -235,12 +235,12 @@
</div>
<div class="form-tips">
<span data-i18n="[html]websocket.tip.path1"></span>
<p id="node-config-ws-tip"><span data-i18n="[html]websocket.tip.path2"></span><code><span id="node-config-ws-path"></span></code>.</p>
<p id="node-config-ws-tip"><span id="node-config-ws-path"></span></p>
</div>
</script>
<!-- WebSocket Client configuration node -->
<script type="text/x-red" data-template-name="websocket-client">
<script type="text/html" data-template-name="websocket-client">
<div class="form-row">
<label for="node-config-input-path"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.url"></span></label>
<input id="node-config-input-path" type="text" placeholder="ws://example.com/ws">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="tcp in">
<script type="text/html" data-template-name="tcp in">
<div class="form-row">
<label for="node-input-server"><i class="fa fa-dot-circle-o"></i> <span data-i18n="tcpin.label.type"></span></label>
<select id="node-input-server" style="width:120px; margin-right:5px;">
@@ -108,7 +108,7 @@
</script>
<script type="text/x-red" data-template-name="tcp out">
<script type="text/html" data-template-name="tcp out">
<div class="form-row">
<label for="node-input-beserver"><i class="fa fa-dot-circle-o"></i> <span data-i18n="tcpin.label.type"></span></label>
<select id="node-input-beserver" style="width:150px; margin-right:5px;">
@@ -170,15 +170,14 @@
$("#node-input-port-row").hide();
$("#node-input-host-row").hide();
$("#node-input-end-row").hide();
} else if (sockettype == "client"){
$("#node-input-port-row").show();
$("#node-input-host-row").show();
$("#node-input-end-row").show();
} else {
$("#node-input-port-row").show();
$("#node-input-end-row").show();
}
if (sockettype == "client") {
$("#node-input-host-row").show();
} else {
$("#node-input-host-row").hide();
$("#node-input-end-row").show();
}
};
updateOptions();
@@ -188,7 +187,7 @@
</script>
<script type="text/x-red" data-template-name="tcp request">
<script type="text/html" data-template-name="tcp request">
<div class="form-row">
<label for="node-input-server"><i class="fa fa-globe"></i> <span data-i18n="tcpin.label.server"></span></label>
<input type="text" id="node-input-server" placeholder="ip.address" style="width:45%">

View File

@@ -74,7 +74,7 @@ module.exports = function(RED) {
buffer = (node.datatype == 'buffer') ? Buffer.alloc(0) : "";
node.connected = true;
node.log(RED._("tcpin.status.connected",{host:node.host,port:node.port}));
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
node.status({fill:"green",shape:"dot",text:"common.status.connected",_session:{type:"tcp",id:id}});
});
client.setKeepAlive(true,120000);
connectionPool[id] = client;
@@ -121,7 +121,7 @@ module.exports = function(RED) {
client.on('close', function() {
delete connectionPool[id];
node.connected = false;
node.status({fill:"red",shape:"ring",text:"common.status.disconnected"});
node.status({fill:"red",shape:"ring",text:"common.status.disconnected",_session:{type:"tcp",id:id}});
if (!node.closing) {
if (end) { // if we were asked to close then try to reconnect once very quick.
end = false;

View File

@@ -15,7 +15,7 @@
-->
<!-- The Input Node -->
<script type="text/x-red" data-template-name="udp in">
<script type="text/html" data-template-name="udp in">
<div class="form-row">
<label for="node-input-port"><i class="fa fa-sign-in"></i> <span data-i18n="udp.label.listen"></span></label>
<select id="node-input-multicast" style='width:70%'>
@@ -115,7 +115,7 @@
<!-- The Output Node -->
<script type="text/x-red" data-template-name="udp out">
<script type="text/html" data-template-name="udp out">
<div class="form-row">
<label for="node-input-port"><i class="fa fa-envelope"></i> <span data-i18n="udp.label.send"></span></label>
<select id="node-input-multicast" style="width:40%">

View File

@@ -180,6 +180,10 @@ module.exports = function(RED) {
node.tout = setTimeout(function() {
if (udpInputPortsInUse[p]) {
sock = udpInputPortsInUse[p];
if (node.multicast != "false") {
sock.setBroadcast(true);
sock.setMulticastLoopback(false);
}
node.log(RED._("udp.status.re-use",{outport:node.outport,host:node.addr,port:node.port}));
if (node.iface) { node.status({text:n.iface+" : "+node.iface}); }
}

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="csv">
<script type="text/html" data-template-name="csv">
<div class="form-row">
<label for="node-input-temp"><i class="fa fa-list"></i> <span data-i18n="csv.label.columns"></span></label>
<input type="text" id="node-input-temp" data-i18n="[placeholder]csv.placeholder.columns">
@@ -28,11 +28,15 @@
</div>
<div class="form-row" style="padding-left:20px;">
<label><i class="fa fa-sign-in"></i> <span data-i18n="csv.label.input"></span></label>
<span data-i18n="csv.label.skip-s"></span>&nbsp;<input type="text" id="node-input-skip" style="width:30px; height:25px;"/>&nbsp;<span data-i18n="csv.label.skip-e"></span><br/>
<span data-i18n="csv.label.skip-s"></span>&nbsp;<input type="text" id="node-input-skip" style="width:40px; height:25px;"/>&nbsp;<span data-i18n="csv.label.skip-e"></span><br/>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-hdrin"><label style="width:auto; margin-top:7px;" for="node-input-hdrin"><span data-i18n="csv.label.firstrow"></span></label><br/>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-strings"><label style="width:auto; margin-top:7px;" for="node-input-strings"><span data-i18n="csv.label.usestrings"></span></label><br/>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-include_empty_strings"><label style="width:auto; margin-top:7px;" for="node-input-include_empty_strings"><span data-i18n="csv.label.include_empty_strings"></span></label><br/>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-include_null_values"><label style="width:auto; margin-top:7px;" for="node-input-include_null_values"><span data-i18n="csv.label.include_null_values"></span></label><br/>
</div>
<div class="form-row" style="padding-left:20px;">
<label><i class="fa fa-sign-out"></i> <span data-i18n="csv.label.output"></span></label>
@@ -45,8 +49,13 @@
<label style="width:100%;"><span data-i18n="csv.label.o2c"></span></label>
</div>
<div class="form-row" style="padding-left:20px;">
<label><i class="fa fa-sign-in"></i> <span data-i18n="csv.label.output"></span></label>
<input style="width:20px; vertical-align:top; margin-right:5px;" type="checkbox" id="node-input-hdrout"><label style="width:auto;" for="node-input-hdrout"><span data-i18n="csv.label.includerow"></span></span>
<label><i class="fa fa-sign-out"></i> <span data-i18n="csv.label.output"></span></label>
<!-- <input style="width:20px; vertical-align:top; margin-right:5px;" type="checkbox" id="node-input-hdrout"><label style="width:auto;" for="node-input-hdrout"><span data-i18n="csv.label.includerow"></span></span> -->
<select style="width:60%" id="node-input-hdrout">
<option value="none" data-i18n="csv.hdrout.none"></option>
<option value="all" data-i18n="csv.hdrout.all"></option>
<option value="once" data-i18n="csv.hdrout.once"></option>
</select>
</div>
<div class="form-row" style="padding-left:20px;">
<label></label>
@@ -69,12 +78,14 @@
sep: {value:',',required:true,validate:RED.validators.regex(/^.{1,2}$/)},
//quo: {value:'"',required:true},
hdrin: {value:""},
hdrout: {value:""},
hdrout: {value:"none"},
multi: {value:"one",required:true},
ret: {value:'\\n'},
temp: {value:""},
skip: {value:"0"},
strings: {value:true}
strings: {value:true},
include_empty_strings: {value:""},
include_null_values: {value:""}
},
inputs:1,
outputs:1,
@@ -86,6 +97,8 @@
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (this.hdrout === false) { this.hdrout = "none"; $("#node-input-hdrout").val("none"); }
if (this.hdrout === true) { this.hdrout = "all"; $("#node-input-hdrout").val("all");}
if (this.strings === undefined) { this.strings = true; $("#node-input-strings").prop('checked', true); }
if (this.skip === undefined) { this.skip = 0; $("#node-input-skip").val("0");}
$("#node-input-skip").spinner({ min:0 });

View File

@@ -26,12 +26,16 @@ module.exports = function(RED) {
this.lineend = "\n";
this.multi = n.multi || "one";
this.hdrin = n.hdrin || false;
this.hdrout = n.hdrout || false;
this.hdrout = n.hdrout || "none";
this.goodtmpl = true;
this.skip = parseInt(n.skip || 0);
this.store = [];
this.parsestrings = n.strings;
this.include_empty_strings = n.include_empty_strings || false;
this.include_null_values = n.include_null_values || false;
if (this.parsestrings === undefined) { this.parsestrings = true; }
if (this.hdrout === false) { this.hdrout = "none"; }
if (this.hdrout === true) { this.hdrout = "all"; }
var tmpwarn = true;
var node = this;
@@ -49,14 +53,22 @@ module.exports = function(RED) {
return col;
}
node.template = clean(node.template);
node.hdrSent = false;
this.on("input", function(msg) {
if (msg.hasOwnProperty("reset")) {
node.hdrSent = false;
}
if (msg.hasOwnProperty("payload")) {
if (typeof msg.payload == "object") { // convert object to CSV string
try {
var ou = "";
if (node.hdrout) {
if (node.hdrout !== "none" && node.hdrSent === false) {
if ((node.template.length === 1) && (node.template[0] === '') && (msg.hasOwnProperty("columns"))) {
node.template = clean((msg.columns || "").split(","));
}
ou += node.template.join(node.sep) + node.ret;
if (node.hdrout === "once") { node.hdrSent = true; }
}
if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
for (var s = 0; s < msg.payload.length; s++) {
@@ -75,13 +87,15 @@ module.exports = function(RED) {
ou += msg.payload[s].join(node.sep) + node.ret;
}
else {
if ((node.template.length === 1) && (node.template[0] === '') && (msg.hasOwnProperty("columns"))) {
node.template = clean((msg.columns || "").split(","));
}
if ((node.template.length === 1) && (node.template[0] === '')) {
/* istanbul ignore else */
if (tmpwarn === true) { // just warn about missing template once
node.warn(RED._("csv.errors.obj_csv"));
tmpwarn = false;
}
ou = "";
for (var p in msg.payload[0]) {
/* istanbul ignore else */
if (msg.payload[0].hasOwnProperty(p)) {
@@ -125,6 +139,7 @@ module.exports = function(RED) {
}
}
msg.payload = ou;
msg.columns = node.template.join(',');
if (msg.payload !== '') { node.send(msg); }
}
catch(e) { node.error(e,msg); }
@@ -173,20 +188,29 @@ module.exports = function(RED) {
}
else if ((line[i] === node.sep) && f) { // if it is the end of the line then finish
if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "" ) ) {
if ( (node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
o[node.template[j]] = k[j];
if ( node.template[j] && (node.template[j] !== "") ) {
// if no value between separators ('1,,"3"...') or if the line beings with separator (',1,"2"...') treat value as null
if (line[i-1] === node.sep || line[i-1].includes('\n','\r')) k[j] = null;
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
if (node.include_null_values && k[j] === null) o[node.template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[node.template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[node.template[j]] = k[j];
}
j += 1;
k[j] = "";
// if separator is last char in processing string line (without end of line), add null value at the end - example: '1,2,3\n3,"3",'
k[j] = line.length - 1 === i ? null : "";
}
else if ((line[i] === "\n") || (line[i] === "\r")) { // handle multiple lines
else if (((line[i] === "\n") || (line[i] === "\r")) && f) { // handle multiple lines
//console.log(j,k,o,k[j]);
if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "") ) {
if ( (node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
else { k[j].replace(/\r$/,''); }
o[node.template[j]] = k[j];
if ( node.template[j] && (node.template[j] !== "") ) {
// if separator before end of line, set null value ie. '1,2,"3"\n1,2,\n1,2,3'
if (line[i-1] === node.sep) k[j] = null;
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
else { if (k[j] !== null) k[j].replace(/\r$/,''); }
if (node.include_null_values && k[j] === null) o[node.template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[node.template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[node.template[j]] = k[j];
}
if (JSON.stringify(o) !== "{}") { // don't send empty objects
a.push(o); // add to the array
@@ -202,17 +226,21 @@ module.exports = function(RED) {
}
}
// Finished so finalize and send anything left
//console.log(j,k,o,k[j]);
if (f === false) { node.warn(RED._("csv.errors.bad_csv")); }
if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "") ) {
if ( (node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
else { k[j].replace(/\r$/,''); }
o[node.template[j]] = k[j];
if ( node.template[j] && (node.template[j] !== "") ) {
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
else { if (k[j] !== null) k[j].replace(/\r$/,''); }
if (node.include_null_values && k[j] === null) o[node.template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[node.template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[node.template[j]] = k[j];
}
if (JSON.stringify(o) !== "{}") { // don't send empty objects
a.push(o); // add to the array
}
var has_parts = msg.hasOwnProperty("parts");
if (node.multi !== "one") {
msg.payload = a;
if (has_parts) {
@@ -221,12 +249,14 @@ module.exports = function(RED) {
}
if (msg.parts.index + 1 === msg.parts.count) {
msg.payload = node.store;
msg.columns = node.template.filter(val => val).join(',');
delete msg.parts;
node.send(msg);
node.store = [];
}
}
else {
msg.columns = node.template.filter(val => val).join(',');
node.send(msg); // finally send the array
}
}
@@ -234,6 +264,7 @@ module.exports = function(RED) {
var len = a.length;
for (var i = 0; i < len; i++) {
var newMessage = RED.util.cloneMessage(msg);
newMessage.columns = node.template.filter(val => val).join(',');
newMessage.payload = a[i];
if (!has_parts) {
newMessage.parts = {
@@ -259,7 +290,11 @@ module.exports = function(RED) {
}
else { node.warn(RED._("csv.errors.csv_js")); }
}
else { node.send(msg); } // If no payload - just pass it on.
else {
if (!msg.hasOwnProperty("reset")) {
node.send(msg); // If no payload and not reset - just pass it on.
}
}
});
}
RED.nodes.registerType("csv",CSVNode);

View File

@@ -1,7 +1,7 @@
<script type="text/x-red" data-template-name="html">
<script type="text/html" data-template-name="html">
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%">
</div>
<div class="form-row">

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="json">
<script type="text/html" data-template-name="json">
<div class="form-row">
<label for="node-input-action"><i class="fa fa-dot-circle-o"></i> <span data-i18n="json.label.action"></span></label>
<select style="width:70%" id="node-input-action">

View File

@@ -1,7 +1,7 @@
<script type="text/x-red" data-template-name="xml">
<script type="text/html" data-template-name="xml">
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
</div>
<div class="form-row">

View File

@@ -1,7 +1,7 @@
<script type="text/x-red" data-template-name="yaml">
<script type="text/html" data-template-name="yaml">
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
</div>
<div class="form-row">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="split">
<script type="text/html" data-template-name="split">
<div class="form-row"><span data-i18n="[html]split.intro"></span></div>
<div class="form-row"><span data-i18n="[html]split.strBuff"></span></div>
<div class="form-row">
@@ -113,7 +113,7 @@
</script>
<script type="text/x-red" data-template-name="join">
<script type="text/html" data-template-name="join">
<div class="form-row">
<label data-i18n="join.mode.mode"></label>
<select id="node-input-mode" style="width:200px;">

View File

@@ -292,7 +292,6 @@ module.exports = function(RED) {
reduceMessageGroup(node,msgs,exp,fixup,count,result,done);
}
});
}
function reduceAndSendGroup(node, group, done) {
var is_right = node.reduce_right;
@@ -331,7 +330,7 @@ module.exports = function(RED) {
var pending_count = node.pending_count;
var gid = msg.parts.id;
var count;
if(!pending.hasOwnProperty(gid)) {
if (!pending.hasOwnProperty(gid)) {
if(parts.hasOwnProperty('count')) {
count = msg.parts.count;
}
@@ -361,7 +360,6 @@ module.exports = function(RED) {
}
return done();
}
if (msgs.length === group.count) {
delete pending[gid];
pending_count -= msgs.length;
@@ -408,7 +406,7 @@ module.exports = function(RED) {
if (this.joinerType === "str") {
this.joiner = this.joiner.replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t").replace(/\\e/g,"\e").replace(/\\f/g,"\f").replace(/\\0/g,"\0");
} else if (this.joinerType === "bin") {
var joinArray = JSON.parse(n.joiner)
var joinArray = JSON.parse(n.joiner || "[]");
if (Array.isArray(joinArray)) {
this.joiner = Buffer.from(joinArray);
} else {
@@ -429,7 +427,7 @@ module.exports = function(RED) {
var completeSend = function(partId) {
var group = inflight[partId];
clearTimeout(group.timeout);
if (group.timeout) { clearTimeout(group.timeout); }
if ((node.accumulate !== true) || group.msg.hasOwnProperty("complete")) { delete inflight[partId]; }
if (group.type === 'array' && group.arrayLen > 1) {
var newArray = [];
@@ -448,6 +446,9 @@ module.exports = function(RED) {
buffers.push(joinBuffer);
bufferLen += joinBuffer.length;
}
if (!Buffer.isBuffer(group.payload[i])) {
group.payload[i] = Buffer.from(group.payload[i]);
}
buffers.push(group.payload[i]);
bufferLen += group.payload[i].length;
}
@@ -629,7 +630,13 @@ module.exports = function(RED) {
var group = inflight[partId];
if (payloadType === 'buffer') {
if (property !== undefined) {
inflight[partId].bufferLen += property.length;
if (Buffer.isBuffer(property) || (typeof property === "string") || Array.isArray(property)) {
inflight[partId].bufferLen += property.length;
}
else {
node.error(RED._("join.errors.invalid-type",{error:(typeof property)}),msg);
return;
}
}
}
if (payloadType === 'object') {

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="sort">
<script type="text/html" data-template-name="sort">
<div class="form-row">
<label for="node-input-target"><i class="fa fa-dot-circle-o"></i> <span data-i18n="sort.target"></span></label>
@@ -53,8 +53,8 @@
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
<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>
</script>

View File

@@ -60,8 +60,8 @@
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
<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>
</script>

View File

@@ -179,6 +179,11 @@ module.exports = function(RED) {
}
node.pending = [];
this.on("input", function(msg) {
if (msg.hasOwnProperty("reset")) {
node.pending = [];
node.pending_count = 0;
return;
}
var queue = node.pending;
queue.push(msg);
node.pending_count++;
@@ -204,11 +209,26 @@ module.exports = function(RED) {
var interval = Number(n.interval || "0") *1000;
var allow_empty_seq = n.allowEmptySequence;
node.pending = []
var timer = setInterval(function() {
function msgHandler() {
send_interval(node, allow_empty_seq);
node.pending_count = 0;
}, interval);
}
var timer = undefined;
if (interval > 0) {
timer = setInterval(msgHandler, interval);
}
this.on("input", function(msg) {
if (msg.hasOwnProperty("reset")) {
if (timer !== undefined) {
clearInterval(timer);
}
node.pending = [];
node.pending_count = 0;
if (interval > 0) {
timer = setInterval(msgHandler, interval);
}
return;
}
node.pending.push(msg);
node.pending_count++;
var max_msgs = max_kept_msgs_count(node);
@@ -219,7 +239,9 @@ module.exports = function(RED) {
}
});
this.on("close", function() {
clearInterval(timer);
if (timer !== undefined) {
clearInterval(timer);
}
node.pending = [];
node.pending_count = 0;
});
@@ -230,6 +252,11 @@ module.exports = function(RED) {
});
node.pending = {};
this.on("input", function(msg) {
if (msg.hasOwnProperty("reset")) {
node.pending = {};
node.pending_count = 0;
return;
}
concat_msg(node, msg);
});
this.on("close", function() {

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="file">
<script type="text/html" data-template-name="file">
<div class="form-row node-input-filename">
<label for="node-input-filename"><i class="fa fa-file"></i> <span data-i18n="file.label.filename"></span></label>
<input id="node-input-filename" type="text">
@@ -34,7 +34,7 @@
<div class="form-tips"><span data-i18n="file.tip"></span></div>
</script>
<script type="text/x-red" data-template-name="file in">
<script type="text/html" data-template-name="file in">
<div class="form-row">
<label for="node-input-filename"><i class="fa fa-file"></i> <span data-i18n="file.label.filename"></span></label>
<input id="node-input-filename" type="text" data-i18n="[placeholder]file.label.filename">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="watch">
<script type="text/html" data-template-name="watch">
<div class="form-row node-input-filename">
<label for="node-input-files"><i class="fa fa-file"></i> <span data-i18n="watch.label.files"></span></label>
<input id="node-input-files" type="text" tabindex="1" data-i18n="[placeholder]watch.placeholder.files">

View File

@@ -0,0 +1 @@
[{"id":"bf16276d.2f1758","type":"tab","label":"Example: Number-based Group Mode","disabled":false,"info":"*Number-based Group mode* of batch node can be used to create new message sequences from incoming messages. Recently received *N*-messages are grouped to a sequence. Creating message sequences that has overwrap with adjacent message group is possible.\n"},{"id":"f5a82278.78d6c","type":"batch","z":"bf16276d.2f1758","name":"","mode":"count","count":"5","overlap":0,"interval":"5","allowEmptySequence":false,"topics":[],"x":370,"y":232,"wires":[["b1e514ed.44f328"]]},{"id":"43720065.2891d","type":"comment","z":"bf16276d.2f1758","name":"Group 5 consecutive messages","info":"","x":170,"y":60,"wires":[]},{"id":"b1e514ed.44f328","type":"join","z":"bf16276d.2f1758","name":"","mode":"auto","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":510,"y":232,"wires":[["457e5970.8ceaa8"]]},{"id":"457e5970.8ceaa8","type":"debug","z":"bf16276d.2f1758","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":232,"wires":[]},{"id":"8e3d3ceb.bd0fe","type":"comment","z":"bf16276d.2f1758","name":"↑ create message sequence with 5 messages","info":"","x":490,"y":272,"wires":[]},{"id":"fbe20ae3.cbb6f8","type":"comment","z":"bf16276d.2f1758","name":"↓ join sequence to array","info":"","x":560,"y":192,"wires":[]},{"id":"7ebafe58.2a112","type":"inject","z":"bf16276d.2f1758","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":152,"wires":[["589603c4.1cb5fc"]]},{"id":"589603c4.1cb5fc","type":"function","z":"bf16276d.2f1758","name":"send: 0-49","func":"for(var x = 0; x < 50; x++) {\n node.send({payload: x});\n}","outputs":1,"noerr":0,"x":310,"y":152,"wires":[["f5a82278.78d6c"]]},{"id":"9b59b72c.fcd8f8","type":"comment","z":"bf16276d.2f1758","name":"↓ send sequence: 0-49","info":"","x":340,"y":112,"wires":[]},{"id":"6421756f.6abd5c","type":"batch","z":"bf16276d.2f1758","name":"","mode":"count","count":"5","overlap":"1","interval":"5","allowEmptySequence":false,"topics":[],"x":370,"y":500,"wires":[["199cf232.743e7e"]]},{"id":"657b6a53.2a1fc4","type":"comment","z":"bf16276d.2f1758","name":"Group 5 consecutive messages with overlap of 1 msg","info":"","x":240,"y":328,"wires":[]},{"id":"199cf232.743e7e","type":"join","z":"bf16276d.2f1758","name":"","mode":"auto","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":510,"y":500,"wires":[["91d29dda.d8a4f"]]},{"id":"91d29dda.d8a4f","type":"debug","z":"bf16276d.2f1758","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":500,"wires":[]},{"id":"8bd4d407.aa2af8","type":"comment","z":"bf16276d.2f1758","name":"↑ create message sequence with 5 messages with overlap of 1 msg","info":"","x":560,"y":540,"wires":[]},{"id":"a49ea57d.8d2458","type":"comment","z":"bf16276d.2f1758","name":"↓ join sequence to array","info":"","x":560,"y":460,"wires":[]},{"id":"f689d1b3.90e4b","type":"inject","z":"bf16276d.2f1758","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":420,"wires":[["c021cf24.ad03e"]]},{"id":"c021cf24.ad03e","type":"function","z":"bf16276d.2f1758","name":"send: 0-49","func":"for(var x = 0; x < 50; x++) {\n node.send({payload: x});\n}","outputs":1,"noerr":0,"x":310,"y":420,"wires":[["6421756f.6abd5c"]]},{"id":"8da6e576.901a18","type":"comment","z":"bf16276d.2f1758","name":"↓ send sequence: 0-49","info":"","x":340,"y":380,"wires":[]}]

View File

@@ -0,0 +1 @@
[{"id":"82a01f29.86de","type":"tab","label":"Example: Time-based Group Mode","disabled":false,"info":"*Time-based Group mode* of batch node can be used to create new message sequences from incoming messages received within specified time range. \n"},{"id":"9a7f6539.6e36d8","type":"batch","z":"82a01f29.86de","name":"","mode":"interval","count":10,"overlap":0,"interval":"5","allowEmptySequence":false,"topics":[],"x":350,"y":260,"wires":[["e54a3b57.3677f8"]]},{"id":"71dc607e.98aab","type":"comment","z":"82a01f29.86de","name":"Group messages received within 5s","info":"","x":180,"y":80,"wires":[]},{"id":"e54a3b57.3677f8","type":"join","z":"82a01f29.86de","name":"","mode":"auto","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":490,"y":260,"wires":[["1e263cac.6641f3"]]},{"id":"1e263cac.6641f3","type":"debug","z":"82a01f29.86de","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":650,"y":260,"wires":[]},{"id":"324c7b93.0db734","type":"comment","z":"82a01f29.86de","name":"↑ create message sequence received within 5s","info":"","x":480,"y":300,"wires":[]},{"id":"fb112bae.fbe428","type":"comment","z":"82a01f29.86de","name":"↓ join sequence to array","info":"","x":540,"y":220,"wires":[]},{"id":"34f8dda5.2864c2","type":"inject","z":"82a01f29.86de","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":160,"wires":[["c9e23ee4.ce535"]]},{"id":"c9e23ee4.ce535","type":"function","z":"82a01f29.86de","name":"send: 0-49","func":"for(var x = 0; x < 100; x++) {\n node.send({payload: x});\n}","outputs":1,"noerr":0,"x":310,"y":160,"wires":[["7026e0cc.4e3c3"]]},{"id":"7026e0cc.4e3c3","type":"delay","z":"82a01f29.86de","name":"","pauseType":"rate","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":490,"y":160,"wires":[["9a7f6539.6e36d8"]]},{"id":"40f8c766.6ed198","type":"comment","z":"82a01f29.86de","name":"↓ send sequence: 0-49","info":"","x":340,"y":120,"wires":[]}]

View File

@@ -0,0 +1 @@
[{"id":"845b226d.a4b18","type":"tab","label":"Example: Concatenate Mode","disabled":false,"info":"*Concatenate mode* of batch node can be used to combine input message sequences to create a new message sequence. Order of the sequences can be specified using message topic assigned to each message in a sequence. Message sequence can be specified multiple times.\n"},{"id":"72afe7b0.38b9d8","type":"inject","z":"845b226d.a4b18","name":"","topic":"SEQ","payload":"[1,2,3,4,5]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":100,"wires":[["6dea90dd.c442c"]]},{"id":"6dea90dd.c442c","type":"split","z":"845b226d.a4b18","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":330,"y":100,"wires":[["3ac93a4b.ddbbc6"]]},{"id":"3ac93a4b.ddbbc6","type":"batch","z":"845b226d.a4b18","name":"","mode":"concat","count":10,"overlap":0,"interval":10,"allowEmptySequence":false,"topics":[{"topic":"SEQ"},{"topic":"SEQ"}],"x":470,"y":100,"wires":[["48ec7040.56f5f"]]},{"id":"48ec7040.56f5f","type":"join","z":"845b226d.a4b18","name":"","mode":"auto","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":610,"y":100,"wires":[["902613c4.769b5"]]},{"id":"902613c4.769b5","type":"debug","z":"845b226d.a4b18","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":770,"y":100,"wires":[]},{"id":"a84cf2e1.65adc","type":"comment","z":"845b226d.a4b18","name":"Duplicate","info":"","x":100,"y":60,"wires":[]},{"id":"3256f015.45c36","type":"inject","z":"845b226d.a4b18","name":"","topic":"SEQ","payload":"[1,-6,-8,7,2,-3]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":220,"wires":[["c308dcb2.621da"]]},{"id":"c308dcb2.621da","type":"split","z":"845b226d.a4b18","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":330,"y":220,"wires":[["2222098b.7fd036"]]},{"id":"247a5fab.239cc","type":"comment","z":"845b226d.a4b18","name":"Filter & Concat","info":"","x":120,"y":180,"wires":[]},{"id":"2222098b.7fd036","type":"switch","z":"845b226d.a4b18","name":"","property":"payload","propertyType":"msg","rules":[{"t":"gt","v":"0","vt":"num"},{"t":"else"}],"checkall":"true","repair":true,"outputs":2,"x":390,"y":280,"wires":[["56e3a974.2bfde8"],["86a1b43a.ff4cb8"]]},{"id":"cd00a796.e4e478","type":"comment","z":"845b226d.a4b18","name":"↑ Duplicate SEQ","info":"","x":500,"y":140,"wires":[]},{"id":"56e3a974.2bfde8","type":"change","z":"845b226d.a4b18","name":"Topic←POS","rules":[{"t":"set","p":"topic","pt":"msg","to":"POS","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":240,"wires":[["fe90d65d.a6b548"]]},{"id":"86a1b43a.ff4cb8","type":"change","z":"845b226d.a4b18","name":"Topic←NEG","rules":[{"t":"set","p":"topic","pt":"msg","to":"NEG","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":300,"wires":[["fe90d65d.a6b548"]]},{"id":"fe90d65d.a6b548","type":"batch","z":"845b226d.a4b18","name":"","mode":"concat","count":10,"overlap":0,"interval":10,"allowEmptySequence":false,"topics":[{"topic":"NEG"},{"topic":"POS"}],"x":710,"y":280,"wires":[["5b089f16.62b96"]]},{"id":"2f46c0af.bff71","type":"debug","z":"845b226d.a4b18","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":930,"y":220,"wires":[]},{"id":"5b089f16.62b96","type":"join","z":"845b226d.a4b18","name":"","mode":"auto","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":770,"y":220,"wires":[["2f46c0af.bff71"]]},{"id":"e069eb28.6eb358","type":"comment","z":"845b226d.a4b18","name":"↑ Order sequence: negative→positive","info":"","x":810,"y":320,"wires":[]},{"id":"aeae162b.efd118","type":"comment","z":"845b226d.a4b18","name":"Filter pos/neg and make separate sequence↑ (but not a simple sort) ","info":"","x":320,"y":340,"wires":[]}]

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="inject">
<script type="text/html" data-help-name="inject">
<p> Injiziert eine Nachricht manuell oder in regelmäßigen Intervallen in einen Nachrichtenflow.
Bei den Nutzdaten kann es sich um eine Vielzahl von Typen handeln, einschließlich Zeichenfolgen, JavaScript-Objekte oder die aktuelle Zeit. </p>
<h3> Ausgaben </h3>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="debug">
<script type="text/html" data-help-name="debug">
<p> Zeigt die ausgewählten Nachrichteneigenschaften auf der Registerkarte "Debug" und
optional im Laufzeitprotokoll an. Standardmäßig wird <code>msg.payload</code> angezeigt. </p>
<h3> Details </h3>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="catch">
<script type="text/html" data-help-name="catch">
<p> Fängt Fehler von Nodes auf derselben Registerkarte ab. </p>
<h3> Ausgaben </h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="status">
<script type="text/html" data-help-name="status">
<p> Berichtet Statusnachrichten von anderen Node auf derselben Registerkarte. </p>
<h3> Ausgaben </h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="link in">
<script type="text/html" data-help-name="link in">
<p> Erstellt virtuelle Verbindungen zwischen Flows. </p>
<h3> Details </h3>
<p> Der Node kann mit jedem beliebigen <code>Link-out</code> Node auf einer beliebigen Registerkarte verbunden werden.
@@ -25,7 +25,7 @@
<p> <b> Hinweis: </b> Links können nicht in einem Subflow erstellt oder aus einem Subflow heraus erstellt werden. </p>
</script>
<script type="text/x-red" data-help-name="link out">
<script type="text/html" data-help-name="link out">
<p> Erstellt virtuelle Verbindungen zwischen Flows. </p>
<h3> Details </h3>
<p> Der Node kann mit jedem beliebigen <code>Link-out</code> Node auf einer beliebigen Registerkarte verbunden werden.

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="comment">
<script type="text/html" data-help-name="comment">
<p>Ein Node, der zur Kommentierung der Flows verwendet werden kann.</p>
<h3>Details</h3>
<p>Das Textfeld des Node akzeptiert die Markdown Syntax. Der eingegebene Text wird dann in diesem Informations Panel angezeigt.

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="unknown">
<script type="text/html" data-help-name="unknown">
<p>Dieser Node ist von einem Typ, der in der aktuellen Node-RED Installation unbekannt ist.</p>
<h3>Details</h3>
<p><i>Wenn der Flow im aktuellen Zustand eingesetzt wird, bleibt die Konfiguration erhalten

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="function">
<script type="text/html" data-help-name="function">
<p> Ein JavaScript-Funktionsblock, der für die Nachrichten ausgeführt werden soll, die vom Node empfangen werden. </p>
<p> Die Nachrichten werden als JavaScript-Objekt mit dem Namen <code>msg</code> übergeben. </p>
<p> Er erwartet eine Eigenschaft <code> msg.payload </code> , die den Hauptteil der Nachricht enthält. </p>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="switch">
<script type="text/html" data-help-name="switch">
<p>Weiterleitung von Nachrichten basierend auf den Werten ihrer Eigenschaften oder der Position der Sequenz.</p>
<h3>Details</h3>
<p>Wenn eine Nachricht ankommt, wertet der Node jede der definierten Regeln aus

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="change">
<script type="text/html" data-help-name="change">
<p>Setzen, Ändern, Löschen oder Verschieben von Eigenschaften einer Nachricht, eines Flow-Kontextes oder eines globalen Kontextes.</p>
<p>Der Node kann mehrere Regeln angeben, die in der Reihenfolge ihrer Definition angewendet werden.</p>
<h3>Details</h3>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="range">
<script type="text/html" data-help-name="range">
<p>Ordnet einen numerischen Wert einem anderen Bereich zu..</p>
<h3>Eingaben</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="template">
<script type="text/html" data-help-name="template">
<p> Legt eine Eigenschaft fest, die auf der bereitgestellten Vorlage basiert. </p>
<h3> Eingaben </h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="delay">
<script type="text/html" data-help-name="delay">
<p> Verzögert jede Nachricht, die den Node durchläuft oder begrenzt die Geschwindigkeit, mit der sie übergeben werden können. </p>
<h3> Eingaben </h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="trigger">
<script type="text/html" data-help-name="trigger">
<p> Wenn der Trigger ausgelöst wird, kann eine Nachricht gesendet werden und dann optional eine weitere Nachricht ,
sofern der Trigger nicht verlängert oder zurückgesetzt wird. </p>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="exec">
<script type="text/html" data-help-name="exec">
<p> Führt einen Systembefehl aus und gibt seine Ausgabe zurück. </p>
<p> Der Node kann so konfiguriert werden, dass er entweder wartet, bis der Befehl abgeschlossen ist,
oder die Ausgabe so sendet wie der Befehl sie generiert. </p>

View File

@@ -397,7 +397,7 @@
"message" : "gesamte Nachricht",
"tip" : {
"path1" : "Standardmäßig enthält <code> Nutzdaten </code> die Daten, die über einen Websocket gesendet oder von einem Websocket empfangen werden. Der Listener kann so konfiguriert werden, dass er das gesamte Nachrichtenobjekt als eine JSON-formatierte Zeichenfolge sendet oder empfängt.",
"path2" : "Dieser Pfad ist relativ zu ",
"path2" : "Dieser Pfad ist relativ zu <code>__path__</code>.",
"url1" : "URL sollte ws: &#47; & #47; oder wss: &#47; & #47; Schema verwenden und auf einen vorhandenen Websocket-Listener verweisen.",
"url2" : "Standardmäßig enthält <code> Nutzdaten </code> die Daten, die über einen Websocket gesendet oder von einem Websocket empfangen werden. Der Client kann so konfiguriert werden, dass er das gesamte Nachrichtenobjekt als eine JSON-formatierte Zeichenfolge sendet oder empfängt."
},

View File

@@ -14,6 +14,6 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="tls-config">
<script type="text/html" data-help-name="tls-config">
<p>Konfigurationsoptionen für TLS Verbindungen.</p>
</script>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="http proxy">
<script type="text/html" data-help-name="http proxy">
<p>Konfigurationsoptionen für den HTTP Proxy.</p>
<h3>Details</h3>

View File

@@ -11,7 +11,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="mqtt in">
<script type="text/html" data-help-name="mqtt in">
<p> Stellt eine Verbindung zu einem MQTT-Broker her und subskribiert Nachrichten zu dem angegebenen Topic. </p>
<h3> Ausgaben </h3>
<dl class="message-properties">
@@ -31,7 +31,7 @@
<p> Mehrere MQTT-Nodes (in oder out) können bei Bedarf dieselbe Brokerverbindung nutzen. </p>
</script>
<script type="text/x-red" data-help-name="mqtt out">
<script type="text/html" data-help-name="mqtt out">
<p>Stellt eine Verbindung zu einem MQTT-Broker her und publiziert Nachrichten.</p>
<h3>Eingaben</h3>
<dl class="message-properties">
@@ -61,7 +61,7 @@
<p>Mehrere MQTT-Nodes (in oder out) können bei Bedarf dieselbe Brokerverbindung nutzen.</p>
</script>
<script type="text/x-red" data-help-name="mqtt-broker">
<script type="text/html" data-help-name="mqtt-broker">
<p> Konfiguration für eine Verbindung zu einem MQTT-Broker. </p>
<p> Diese Konfiguration erstellt eine Verbindung zu einem Broker, die anschließend von den
Nodes <code>MQTT In</code> und <code>MQTT Out</code> verwendet werden. </p>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="http in">
<script type="text/html" data-help-name="http in">
<p>Erstellt einen HTTP Endpunkt zur Erzeugung von Web Services.</p>
<h3>Outputs</h3>
<dl class="message-properties">
@@ -53,7 +53,7 @@
muss einen code>HTTP Response</code> Node enthalten, um die Anforderung abzuschließen. </p>
</script>
<script type="text/x-red" data-help-name="http response">
<script type="text/html" data-help-name="http response">
<p>Sendet Antworten auf Anforderungen, die von einem code>HTTP In</code> Node empfangen wurden. </p>
<h3>Eingaben</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="http request">
<script type="text/html" data-help-name="http request">
<p>Sendet HTTP-Anforderungen und gibt die Antwort zurück.</p>
<h3>Eingaben</h3>

View File

@@ -14,53 +14,29 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="websocket in">
<script type="text/html" data-help-name="websocket in">
<p>WebSocket Eingangs-Node.</p>
<p>Standardmäßig befinden sich die vom WebSocket empfangenen Daten in <code>msg.payload</code>.
Der Socket kann konfiguriert werden, um einen korrekt gebildeten JSON-String zu erwarten,
in diesem Fall wird er das JSON analysieren und das resultierende Objekt als gesamte Nachricht senden.</p>
</script>
<script type="text/x-red" data-help-name="websocket out">
<script type="text/html" data-help-name="websocket out">
<p>WebSocket Ausgabe-Node.</p>
<p>Standardmäßig wird <code>msg.payload</code> über den WebSocket gesendet.
<p>Standardmäßig wird <code>msg.payload</code> über den WebSocket gesendet.
Der Socket kann so konfiguriert werden, dass er das gesamte <code>msg</code> Objekt als JSON-String kodiert und über den WebSocket sendet.</p>
<p>Wenn die an diesem Node ankommende Nachricht an einem WebSocket-Eingangs-Node begann,
wird die Nachricht an den Client zurückgesendet, der den Flow ausgelöst hat.
wird die Nachricht an den Client zurückgesendet, der den Flow ausgelöst hat.
Andernfalls wird die Nachricht an alle verbundenen Clients gesendet..</p>
<p>Wenn eine Nachricht, die an einem WebSocket-Eingangsnoten gestartet wurde, an alle verbunden Clients gesendet werden soll,
<p>Wenn eine Nachricht, die an einem WebSocket-Eingangsnoten gestartet wurde, an alle verbunden Clients gesendet werden soll,
muss die Eigenschaft <code>msg._session</code> innerhalb des Flow gelöscht werden.</p>
</script>
<script type="text/x-red" data-help-name="websocket-listener">
<script type="text/html" data-help-name="websocket-listener">
<p>Dieser Konfigurations-Node erstellt einen WebSocket Server-Endpunkt unter Verwendung des angegebenen Pfades.</p>
</script>
<!-- WebSocket Client configuration node -->
<script type="text/x-red" data-template-name="websocket-client">
<div class="form-row">
<label for="node-config-input-path"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.url"></span></label>
<input id="node-config-input-path" type="text" placeholder="ws://example.com/ws">
</div>
<div class="form-row node-config-row-tls hide">
<label for="node-config-input-tls" data-i18n="httpin.tls-config"></label>
<input type="text" id="node-config-input-tls">
</div>
<div class="form-row">
<label for="node-config-input-wholemsg" data-i18n="websocket.sendrec"></label>
<select type="text" id="node-config-input-wholemsg" style="width: 70%;">
<option value="false" data-i18n="websocket.payload"></option>
<option value="true" data-i18n="websocket.message"></option>
</select>
</div>
<div class="form-tips">
<p><span data-i18n="[html]websocket.tip.url1"></span></p>
<span data-i18n="[html]websocket.tip.url2"></span>
</div>
</script>
<script type="text/x-red" data-help-name="websocket-client">
<script type="text/html" data-help-name="websocket-client">
<p>Dieser Konfigurations-Node verbindet einen WebSocket-Client mit der angegebenen URL.</p>
</script>

View File

@@ -14,13 +14,13 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="tcp in">
<script type="text/html" data-help-name="tcp in">
<p>Bietet eine Auswahl an TCP-Eingängen. Kann sich entweder mit einem entfernten TCP-Port verbinden oder eingehende Verbindungen akzeptieren.</p>
<p><b>Note: </b>Auf einigen Systemen benötigen Sie möglicherweise Root- oder Administratorzugriff, um
Ports unter 1024 und/oder Broadcast nutzen zu können.</p>
</script>
<script type="text/x-red" data-help-name="tcp out">
<script type="text/html" data-help-name="tcp out">
<p>Bietet eine Auswahl an TCP-Ausgängen. Kann sich entweder mit einem entfernten TCP-Port verbinden,
eingehende Verbindungen akzeptieren oder auf Nachrichten antworten, die von einem TCP-In-Node empfangen werden.</p>
<p>Nur der Inhalt von <code>msg.payload</code> wird gesendet.</p>
@@ -32,7 +32,7 @@
Ports unter 1024 und/oder Broadcast nutzen zu können.</p>
</script>
<script type="text/x-red" data-help-name="tcp request">
<script type="text/html" data-help-name="tcp request">
<p>Ein einfacher TCP-Anforderungs-Node - sendet die <code>msg.payload</code> an einen Server-TCP-Port und erwartet eine Antwort.</p>
<p>Verbindet sich, sendet die "Anforderung" und liest die "Antwort". Der Node wartet entweder auf eine vorgegebene Anzahl von
Zeichen in einen festen Buffer, auf ein bestimmtes Zeichen oder einen festen Timeout ab der ersten Antwort,

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="udp in">
<script type="text/html" data-help-name="udp in">
<p>Ein UDP-Eingangs-Node, der eine <code>msg.payload</code> erzeugt, die einen
Buffer, Strings oder base64-kodierter String enthält. Multicast wird unterstützt.</p>
<p>Über die Eigenschaften <code>msg.ip</code> und <code>msg.port</code> kann auf die Werte der
@@ -23,7 +23,7 @@
Ports unter 1024 und/oder Broadcast nutzen zu können.</p>
</script>
<script type="text/x-red" data-help-name="udp out">
<script type="text/html" data-help-name="udp out">
<p>Dieser Node sendet <code>msg.payload</code> an den angegebenen UDP-Host und Port. Multicast wird unterstützt.</p>
<p>Sie können <code>msg.ip</code> und <code>msg.port</code> verwenden, um die Zielwerte festzulegen,
aber die statisch im Node konfigurierten Werte haben Vorrang.</p>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="csv">
<script type="text/html" data-help-name="csv">
<p>Konvertiert zwischen einem CSV-formatierten String und ihrer JavaScript-Objektdarstellung in beide Richtungen.</p>
<h3>Eingaben</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="html">
<script type="text/html" data-help-name="html">
<p>Extrahiert unter Verwendung eines CSS-Selektors Elemente aus einem HTML-Dokument, das sich in <code>msg.payload</code> befindet.</p>
<h3>Eingaben</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="json">
<script type="text/html" data-help-name="json">
<p>Konvertiert zwischen einem JSON-String und seiner JavaScript-Objektdarstellung in beide Richtungen.</p>
<h3>Eingaben</h3>
<dl class="message-properties">

View File

@@ -14,12 +14,16 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="xml">
<script type="text/html" data-help-name="xml">
<p>Konvertiert zwischen einem XML-String und seiner JavaScript-Objektdarstellung - in beiden Richtungen.</p>
<h3>Eingaben</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">Objekt | String</span></dt>
<dd>Ein JavaScript Objekt oder ein XML String.</dd>
<dt class="optional">options <span class="property-type">Objekt</span></dt>
<dd>This optional property can be used to pass in any of the options supported by the underlying
library used to convert to and from XML. See <a href="https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md#options" target="_blank">the xml2js docs</a>
for more information.</dd>
</dl>
<h3>Ausgaben</h3>
<dl class="message-properties">
@@ -30,10 +34,6 @@
<li>Wenn die Eingabe ein JavaScript-Objekt ist, wird versucht ein XML-String zu erstellen.</li>
</ul>
</dd>
<dt class="optional">Optionen <span class="property-type">Objekt</span></dt>
<dd>This optional property can be used to pass in any of the options supported by the underlying
library used to convert to and from XML. See <a href="https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md#options" target="_blank">the xml2js docs</a>
for more information.</dd>
</dl>
<h3>Details</h3>
<p>Bei der Konvertierung zwischen XML und einem Objekt werden standardmäßig alle XML-Attribute als Eigenschaft namens <code>$</code> hinzugefügt.

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="yaml">
<script type="text/html" data-help-name="yaml">
<p>Konvertiert zwischen einer YAML-formatierten String und ihrer JavaScript-Objektdarstellung in beide Richtungen.</p>
<h3>Eingaben</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="split">
<script type="text/html" data-help-name="split">
<p>Teilt eine Nachricht in eine Folge von Nachrichten auf.</p>
<h3>Eingaben</h3>
@@ -62,7 +62,7 @@
Das bedeutet, dass er nicht mit dem <b>join</b> Node im Automatikmodus verwendet werden kann.</p>
</script>
<script type="text/x-red" data-help-name="join">
<script type="text/html" data-help-name="join">
<p>Verbindet Sequenzen von Nachrichten zu einer einzigen Nachricht.</p>
<p>Es sind drei Modi verfügbar:</p>
<dl>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="sort">
<script type="text/html" data-help-name="sort">
<p>Eine Funktion, die die Nachrichteneigenschaft oder eine Folge von Nachrichten sortiert.</p>
<p>Wenn der Node konfiguriert ist, um die Nachrichteneigenschaft zu sortieren,
sortiert er Array-Daten, auf die von der angegebenen Nachrichteneigenschaft verwiesen wird.</p>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="batch">
<script type="text/html" data-help-name="batch">
<p>Erstellt Sequenzen von Nachrichten nach verschiedenen Regeln.</p>
<h3>Details</h3>
<p>Es gibt drei Modi für die Erstellung von Nachrichtensequenzen:</p>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="file">
<script type="text/html" data-help-name="file">
<p>Schreibt <code>msg.payload</code> in eine Datei, wobei entweder der vorhandene Inhalt hinzugefügt oder ersetzt wird.
Alternativ kann die Datei auch gelöscht werden.</p>
<h3>Eingaben</h3>
@@ -34,7 +34,7 @@
<p>Alternativ kann dieser Node konfiguriert werden, um die Datei zu löschen.</p>
</script>
<script type="text/x-red" data-help-name="file in">
<script type="text/html" data-help-name="file in">
<p>Reads the contents of a file as either a string or binary buffer.</p>
<h3>Inputs</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="watch">
<script type="text/html" data-help-name="watch">
<p>Überwacht ein Verzeichnis oder eine Datei auf Änderungen.</p>
<p>Es kann eine kommagetrennte Liste von Verzeichnissen und/oder Dateien eingeben werden.
Es müssen Anführungszeichen "...." um alle Einträge gesetzt werden, die Leerzeichen enthalten.</p>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="inject">
<script type="text/html" data-help-name="inject">
<p>Injects a message into a flow either manually or at regular intervals. The message
payload can be a variety of types, including strings, JavaScript objects or the current time.</p>
<h3>Outputs</h3>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="debug">
<script type="text/html" data-help-name="debug">
<p>Displays selected message properties in the debug sidebar tab and optionally the runtime log. By default it displays <code>msg.payload</code>, but can be configured to display any property, the full message or the result of a JSONata expression.</p>
<h3>Details</h3>
<p>The debug sidebar provides a structured view of the messages it is sent, making it easier to understand their structure.</p>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="complete">
<script type="text/html" data-help-name="complete">
<p>Trigger a flow when another node completes its handling of a message.</p>
<h3>Details</h3>
<p>If a node tells the runtime when it has finished handling a message,

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="catch">
<script type="text/html" data-help-name="catch">
<p>Catch errors thrown by nodes on the same tab.</p>
<h3>Outputs</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="status">
<script type="text/html" data-help-name="status">
<p>Report status messages from other nodes on the same tab.</p>
<h3>Outputs</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="link in">
<script type="text/html" data-help-name="link in">
<p>Create virtual wires between flows.</p>
<h3>Details</h3>
<p>The node can be connected to any <code>link out</code> node that exists on any tab.
@@ -25,7 +25,7 @@
<p><b>Note: </b>Links cannot be created going into, or out of, a subflow.</p>
</script>
<script type="text/x-red" data-help-name="link out">
<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.

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="comment">
<script type="text/html" data-help-name="comment">
<p>A node you can use to add comments to your flows.</p>
<h3>Details</h3>
<p>The edit panel will accept Markdown syntax. The text will be rendered into

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="unknown">
<script type="text/html" data-help-name="unknown">
<p>This node is a type unknown to your installation of Node-RED.</p>
<h3>Details</h3>
<p><i>If you deploy with the node in this state, its configuration will be preserved, but

View File

@@ -14,13 +14,15 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="function">
<p>A JavaScript function block to run against the messages being received by the node.</p>
<script type="text/html" data-help-name="function">
<p>A JavaScript function to run against the messages being received by the node.</p>
<p>The messages are passed in as a JavaScript object called <code>msg</code>.</p>
<p>By convention it will have a <code>msg.payload</code> property containing
the body of the message.</p>
<p>The function is expected to return a message object (or multiple message objects), but can choose
to return nothing in order to halt a flow.</p>
<p>The <b>Setup</b> tab contains code that will be run whenever the node is started.
The <b>Close</b> tab contains code that will be run when the node is stopped.</p>
<h3>Details</h3>
<p>See the <a target="_blank" href="http://nodered.org/docs/writing-functions.html">online documentation</a>
for more information on writing functions.</p>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="switch">
<script type="text/html" data-help-name="switch">
<p>Route messages based on their property values or sequence position.</p>
<h3>Details</h3>
<p>When a message arrives, the node will evaluate each of the defined rules

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="change">
<script type="text/html" data-help-name="change">
<p>Set, change, delete or move properties of a message, flow context or global context.</p>
<p>The node can specify multiple rules that will be applied in the order they are defined.</p>
<h3>Details</h3>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="range">
<script type="text/html" data-help-name="range">
<p>Maps a numeric value to a different range.</p>
<h3>Inputs</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="template">
<script type="text/html" data-help-name="template">
<p>Sets a property based on the provided template.</p>
<h3>Inputs</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="delay">
<script type="text/html" data-help-name="delay">
<p>Delays each message passing through the node or limits the rate at which they can pass.</p>
<h3>Inputs</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="trigger">
<script type="text/html" data-help-name="trigger">
<p>When triggered, can send a message, and then optionally a second message, unless extended or reset.</p>
<h3>Inputs</h3>
@@ -40,6 +40,8 @@
progress will be cleared and no message triggered.</p>
<p>The node can be configured to resend a message at a regular interval until it
is reset by a received message.</p>
<p>Optionally, the node can be configured to treat messages with <code>msg.topic</code> as if they
are separate streams.</p>
<p>Optionally, the node can be configured to treat messages as if they are separate streams,
using a msg property to identify each stream. Default <code>msg.topic</code>.</p>
<p>The status indicates the node is currently active. If multiple streams are used the status
indicates the number of streams being held.</p>
</script>

Some files were not shown because too many files have changed in this diff Show More