@node-red/nodes 2.2.1 → 3.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/99-sample.html.demo +1 -1
- package/core/common/05-junction.html +5 -0
- package/core/common/05-junction.js +12 -0
- package/core/common/20-inject.html +25 -13
- package/core/common/21-debug.html +59 -6
- package/core/common/21-debug.js +57 -27
- package/core/common/60-link.html +65 -29
- package/core/common/60-link.js +169 -20
- package/core/common/lib/debug/debug-utils.js +34 -1
- package/core/function/10-function.html +57 -21
- package/core/function/10-switch.html +19 -12
- package/core/function/10-switch.js +1 -0
- package/core/function/15-change.html +40 -12
- package/core/function/16-range.html +14 -5
- package/core/function/80-template.html +16 -12
- package/core/function/89-delay.html +46 -6
- package/core/function/89-trigger.html +12 -4
- package/core/function/rbe.html +7 -3
- package/core/network/05-tls.html +10 -4
- package/core/network/06-httpproxy.html +10 -1
- package/core/network/10-mqtt.html +73 -17
- package/core/network/10-mqtt.js +252 -105
- package/core/network/21-httpin.html +10 -6
- package/core/network/21-httprequest.html +219 -12
- package/core/network/21-httprequest.js +98 -17
- package/core/network/22-websocket.html +19 -5
- package/core/network/22-websocket.js +16 -13
- package/core/network/31-tcpin.html +47 -10
- package/core/network/31-tcpin.js +8 -3
- package/core/network/32-udp.html +14 -2
- package/core/parsers/70-CSV.html +4 -1
- package/core/parsers/70-JSON.html +3 -2
- package/core/parsers/70-XML.html +2 -1
- package/core/parsers/70-YAML.html +2 -1
- package/core/sequence/17-split.html +6 -2
- package/core/sequence/19-batch.html +28 -4
- package/core/storage/10-file.html +68 -8
- package/core/storage/10-file.js +46 -3
- package/core/storage/23-watch.html +2 -1
- package/core/storage/23-watch.js +21 -43
- package/locales/de/messages.json +1 -0
- package/locales/en-US/common/60-link.html +19 -3
- package/locales/en-US/messages.json +71 -18
- package/locales/en-US/network/21-httprequest.html +1 -1
- package/locales/en-US/storage/10-file.html +6 -2
- package/locales/ja/common/60-link.html +13 -0
- package/locales/ja/messages.json +84 -32
- package/locales/ja/network/21-httprequest.html +1 -1
- package/locales/ja/storage/10-file.html +8 -6
- package/locales/ko/messages.json +1 -0
- package/locales/ru/messages.json +1 -0
- package/locales/zh-CN/messages.json +1 -0
- package/locales/zh-TW/messages.json +1 -0
- package/package.json +12 -12
package/core/common/60-link.js
CHANGED
|
@@ -14,10 +14,119 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
**/
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* @typedef LinkTarget
|
|
19
|
+
* @type {object}
|
|
20
|
+
* @property {string} id - ID of the target node.
|
|
21
|
+
* @property {string} name - Name of target Node
|
|
22
|
+
* @property {number} flowId - ID of flow where the target node exists
|
|
23
|
+
* @property {string} flowName - Name of flow where the target node exists
|
|
24
|
+
* @property {boolean} isSubFlow - True if the link-in node exists in a subflow instance
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
|
|
17
28
|
module.exports = function(RED) {
|
|
18
29
|
"use strict";
|
|
19
|
-
|
|
20
30
|
const crypto = require("crypto");
|
|
31
|
+
const targetCache = (function () {
|
|
32
|
+
const registry = { id: {}, name: {} };
|
|
33
|
+
function getIndex(/** @type {[LinkTarget]}*/ targets, id) {
|
|
34
|
+
for (let index = 0; index < (targets || []).length; index++) {
|
|
35
|
+
const element = targets[index];
|
|
36
|
+
if (element.id === id) {
|
|
37
|
+
return index;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return -1;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Generate a target object from a node
|
|
44
|
+
* @param {LinkInNode} node
|
|
45
|
+
* @returns {LinkTarget} a link target object
|
|
46
|
+
*/
|
|
47
|
+
function generateTarget(node) {
|
|
48
|
+
const isSubFlow = node._flow.TYPE === "subflow";
|
|
49
|
+
return {
|
|
50
|
+
id: node.id,
|
|
51
|
+
name: node.name || node.id,
|
|
52
|
+
flowId: node._flow.flow.id,
|
|
53
|
+
flowName: isSubFlow ? node._flow.subflowDef.name : node._flow.flow.label,
|
|
54
|
+
isSubFlow: isSubFlow
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
/**
|
|
59
|
+
* Get a list of targets registerd to this name
|
|
60
|
+
* @param {string} name Name of the target
|
|
61
|
+
* @param {boolean} [excludeSubflows] set `true` to exclude
|
|
62
|
+
* @returns {[LinkTarget]} Targets registerd to this name.
|
|
63
|
+
*/
|
|
64
|
+
getTargets(name, excludeSubflows) {
|
|
65
|
+
const targets = registry.name[name] || [];
|
|
66
|
+
if (excludeSubflows) {
|
|
67
|
+
return targets.filter(e => e.isSubFlow != true);
|
|
68
|
+
}
|
|
69
|
+
return targets;
|
|
70
|
+
},
|
|
71
|
+
/**
|
|
72
|
+
* Get a single target by registered name.
|
|
73
|
+
* To restrict to a single flow, include the `flowId`
|
|
74
|
+
* If there is no targets OR more than one target, null is returned
|
|
75
|
+
* @param {string} name Name of the node
|
|
76
|
+
* @param {string} [flowId]
|
|
77
|
+
* @returns {LinkTarget} target
|
|
78
|
+
*/
|
|
79
|
+
getTarget(name, flowId) {
|
|
80
|
+
/** @type {[LinkTarget]}*/
|
|
81
|
+
let possibleTargets = this.getTargets(name);
|
|
82
|
+
/** @type {LinkTarget}*/
|
|
83
|
+
let target;
|
|
84
|
+
if (possibleTargets.length && flowId) {
|
|
85
|
+
possibleTargets = possibleTargets.filter(e => e.flowId == flowId);
|
|
86
|
+
}
|
|
87
|
+
if (possibleTargets.length === 1) {
|
|
88
|
+
target = possibleTargets[0];
|
|
89
|
+
}
|
|
90
|
+
return target;
|
|
91
|
+
},
|
|
92
|
+
/**
|
|
93
|
+
* Get a target by node ID
|
|
94
|
+
* @param {string} nodeId ID of the node
|
|
95
|
+
* @returns {LinkTarget} target
|
|
96
|
+
*/
|
|
97
|
+
getTargetById(nodeId) {
|
|
98
|
+
return registry.id[nodeId];
|
|
99
|
+
},
|
|
100
|
+
register(/** @type {LinkInNode} */ node) {
|
|
101
|
+
const target = generateTarget(node);
|
|
102
|
+
const tByName = this.getTarget(target.name, target.flowId);
|
|
103
|
+
if (!tByName || tByName.id !== target.id) {
|
|
104
|
+
registry.name[target.name] = registry.name[target.name] || [];
|
|
105
|
+
registry.name[target.name].push(target)
|
|
106
|
+
}
|
|
107
|
+
registry.id[target.id] = target;
|
|
108
|
+
return target;
|
|
109
|
+
},
|
|
110
|
+
remove(node) {
|
|
111
|
+
const target = generateTarget(node);
|
|
112
|
+
const tn = this.getTarget(target.name, target.flowId);
|
|
113
|
+
if (tn) {
|
|
114
|
+
const targs = this.getTargets(tn.name);
|
|
115
|
+
const idx = getIndex(targs, tn.id);
|
|
116
|
+
if (idx > -1) {
|
|
117
|
+
targs.splice(idx, 1);
|
|
118
|
+
}
|
|
119
|
+
if (targs.length === 0) {
|
|
120
|
+
delete registry.name[tn.name];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
delete registry.id[target.id];
|
|
124
|
+
},
|
|
125
|
+
clear() {
|
|
126
|
+
registry = { id: {}, name: {} };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
})();
|
|
21
130
|
|
|
22
131
|
function LinkInNode(n) {
|
|
23
132
|
RED.nodes.createNode(this,n);
|
|
@@ -27,12 +136,14 @@ module.exports = function(RED) {
|
|
|
27
136
|
msg._event = n.event;
|
|
28
137
|
node.receive(msg);
|
|
29
138
|
}
|
|
139
|
+
targetCache.register(node);
|
|
30
140
|
RED.events.on(event,handler);
|
|
31
141
|
this.on("input", function(msg, send, done) {
|
|
32
142
|
send(msg);
|
|
33
143
|
done();
|
|
34
144
|
});
|
|
35
145
|
this.on("close",function() {
|
|
146
|
+
targetCache.remove(node);
|
|
36
147
|
RED.events.removeListener(event,handler);
|
|
37
148
|
});
|
|
38
149
|
}
|
|
@@ -74,31 +185,69 @@ module.exports = function(RED) {
|
|
|
74
185
|
function LinkCallNode(n) {
|
|
75
186
|
RED.nodes.createNode(this,n);
|
|
76
187
|
const node = this;
|
|
77
|
-
const
|
|
188
|
+
const staticTarget = typeof n.links === "string" ? n.links : n.links[0];
|
|
189
|
+
const linkType = n.linkType;
|
|
78
190
|
const messageEvents = {};
|
|
79
|
-
|
|
191
|
+
|
|
192
|
+
let timeout = parseFloat(n.timeout || "30") * 1000;
|
|
80
193
|
if (isNaN(timeout)) {
|
|
81
194
|
timeout = 30000;
|
|
82
195
|
}
|
|
196
|
+
function getTargetNode(msg) {
|
|
197
|
+
const dynamicMode = linkType === "dynamic";
|
|
198
|
+
const target = dynamicMode ? msg.target : staticTarget
|
|
83
199
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
200
|
+
////1st see if the target is a direct node id
|
|
201
|
+
let foundNode;
|
|
202
|
+
if (targetCache.getTargetById(target)) {
|
|
203
|
+
foundNode = RED.nodes.getNode(target)
|
|
204
|
+
}
|
|
205
|
+
if (target && !foundNode && dynamicMode) {
|
|
206
|
+
//next, look in **this flow only** for the node
|
|
207
|
+
let cachedTarget = targetCache.getTarget(target, node._flow.flow.id);
|
|
208
|
+
if (!cachedTarget) {
|
|
209
|
+
//single target node not found in registry!
|
|
210
|
+
//get all possible targets from regular flows (exclude subflow instances)
|
|
211
|
+
const possibleTargets = targetCache.getTargets(target, true);
|
|
212
|
+
if (possibleTargets.length === 1) {
|
|
213
|
+
//only 1 link-in found with this name - good, lets use it
|
|
214
|
+
cachedTarget = possibleTargets[0];
|
|
215
|
+
} else if (possibleTargets.length > 1) {
|
|
216
|
+
//more than 1 link-in has this name, raise an error
|
|
217
|
+
throw new Error(`Multiple link-in nodes named '${target}' found`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (cachedTarget) {
|
|
221
|
+
foundNode = RED.nodes.getNode(cachedTarget.id);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (foundNode instanceof LinkInNode) {
|
|
225
|
+
return foundNode;
|
|
89
226
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
227
|
+
throw new Error(`target link-in node '${target || ""}' not found`);
|
|
228
|
+
}
|
|
229
|
+
this.on("input", function (msg, send, done) {
|
|
230
|
+
try {
|
|
231
|
+
const targetNode = getTargetNode(msg);
|
|
232
|
+
if (targetNode instanceof LinkInNode) {
|
|
233
|
+
msg._linkSource = msg._linkSource || [];
|
|
234
|
+
const messageEvent = {
|
|
235
|
+
id: crypto.randomBytes(14).toString('hex'),
|
|
236
|
+
node: node.id,
|
|
237
|
+
}
|
|
238
|
+
messageEvents[messageEvent.id] = {
|
|
239
|
+
msg: RED.util.cloneMessage(msg),
|
|
240
|
+
send,
|
|
241
|
+
done,
|
|
242
|
+
ts: setTimeout(function () {
|
|
243
|
+
timeoutMessage(messageEvent.id)
|
|
244
|
+
}, timeout)
|
|
245
|
+
};
|
|
246
|
+
msg._linkSource.push(messageEvent);
|
|
247
|
+
targetNode.receive(msg);
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
node.error(error, msg);
|
|
102
251
|
}
|
|
103
252
|
});
|
|
104
253
|
|
|
@@ -581,12 +581,45 @@ RED.debug = (function() {
|
|
|
581
581
|
var metaRow = $('<div class="red-ui-debug-msg-meta"></div>').appendTo(msg);
|
|
582
582
|
$('<span class="red-ui-debug-msg-date">'+ getTimestamp()+'</span>').appendTo(metaRow);
|
|
583
583
|
if (sourceNode) {
|
|
584
|
-
|
|
584
|
+
|
|
585
|
+
var nodeLink = $('<a>',{href:"#",class:"red-ui-debug-msg-name"}).text("node: "+(o.name||sourceNode.name||sourceNode.id))
|
|
585
586
|
.appendTo(metaRow)
|
|
586
587
|
.on("click", function(evt) {
|
|
587
588
|
evt.preventDefault();
|
|
588
589
|
config.messageSourceClick(sourceNode.id, sourceNode._alias, sourceNode.path);
|
|
589
590
|
});
|
|
591
|
+
|
|
592
|
+
if (sourceNode.pathHierarchy) {
|
|
593
|
+
RED.popover.create({
|
|
594
|
+
tooltip: true,
|
|
595
|
+
target:nodeLink,
|
|
596
|
+
trigger: "hover",
|
|
597
|
+
size: "small",
|
|
598
|
+
direction: "bottom",
|
|
599
|
+
interactive: true,
|
|
600
|
+
content: function() {
|
|
601
|
+
const content = $("<div>")
|
|
602
|
+
sourceNode.pathHierarchy.forEach((pathPart,idx) => {
|
|
603
|
+
const link = $("<a>", {href:"#" ,style:'display: block'})
|
|
604
|
+
.css({
|
|
605
|
+
paddingLeft:((idx*10)+((idx === sourceNode.pathHierarchy.length - 1)?10:0))+"px",
|
|
606
|
+
paddingRight:'2px'
|
|
607
|
+
})
|
|
608
|
+
.text(pathPart.label)
|
|
609
|
+
.appendTo(content)
|
|
610
|
+
.on("click", function(evt) {
|
|
611
|
+
evt.preventDefault();
|
|
612
|
+
config.messageSourceClick(pathPart.id);
|
|
613
|
+
})
|
|
614
|
+
if (idx < sourceNode.pathHierarchy.length - 1) {
|
|
615
|
+
$('<i class="fa fa-angle-down" style="margin-right: 3px"></i>').prependTo(link)
|
|
616
|
+
}
|
|
617
|
+
})
|
|
618
|
+
return content
|
|
619
|
+
},
|
|
620
|
+
delay: { show: 50, hide: 150 }
|
|
621
|
+
});
|
|
622
|
+
}
|
|
590
623
|
} else if (name) {
|
|
591
624
|
$('<span class="red-ui-debug-msg-name">'+name+'</span>').appendTo(metaRow);
|
|
592
625
|
}
|
|
@@ -355,27 +355,41 @@
|
|
|
355
355
|
color:"#fdd0a2",
|
|
356
356
|
category: 'function',
|
|
357
357
|
defaults: {
|
|
358
|
-
name: {value:""},
|
|
358
|
+
name: {value:"_DEFAULT_"},
|
|
359
359
|
func: {value:"\nreturn msg;"},
|
|
360
360
|
outputs: {value:1},
|
|
361
|
-
noerr: {value:0,required:true,
|
|
361
|
+
noerr: {value:0,required:true,
|
|
362
|
+
validate: function(v, opt) {
|
|
363
|
+
if (!v) {
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
return RED._("node-red:function.error.invalid-js");
|
|
367
|
+
}},
|
|
362
368
|
initialize: {value:""},
|
|
363
369
|
finalize: {value:""},
|
|
364
|
-
libs: {value: [], validate: function(v) {
|
|
370
|
+
libs: {value: [], validate: function(v, opt) {
|
|
365
371
|
if (!v) { return true; }
|
|
366
372
|
for (var i=0,l=v.length;i<l;i++) {
|
|
367
373
|
var m = v[i];
|
|
368
374
|
if (!RED.utils.checkModuleAllowed(m.module,null,installAllowList,installDenyList)) {
|
|
369
|
-
return
|
|
375
|
+
return RED._("node-red:function.error.moduleNotAllowed", {
|
|
376
|
+
module: m.module
|
|
377
|
+
});
|
|
370
378
|
}
|
|
371
379
|
if (m.var === "" || / /.test(m.var)) {
|
|
372
|
-
return
|
|
380
|
+
return RED._("node-red:function.error.moduleNameError", {
|
|
381
|
+
name: m.var
|
|
382
|
+
});
|
|
373
383
|
}
|
|
374
384
|
if (missingModules.indexOf(m.module) > -1) {
|
|
375
|
-
return
|
|
385
|
+
return RED._("node-red:function.error.missing-module", {
|
|
386
|
+
module: m.module
|
|
387
|
+
});
|
|
376
388
|
}
|
|
377
389
|
if (invalidModuleVNames.indexOf(m.var) !== -1){
|
|
378
|
-
return
|
|
390
|
+
return RED._("node-red:function.error.moduleNameError", {
|
|
391
|
+
name: m.var
|
|
392
|
+
});
|
|
379
393
|
}
|
|
380
394
|
}
|
|
381
395
|
return true;
|
|
@@ -399,11 +413,19 @@
|
|
|
399
413
|
$("#func-tabs-content").children().hide();
|
|
400
414
|
$("#" + tab.id).show();
|
|
401
415
|
let editor = $("#" + tab.id).find('.monaco-editor').first();
|
|
402
|
-
|
|
416
|
+
if(editor.length) {
|
|
403
417
|
if(that.editor.nodered && that.editor.type == "monaco") {
|
|
404
418
|
that.editor.nodered.refreshModuleLibs(getLibsList());
|
|
405
419
|
}
|
|
406
420
|
RED.tray.resize();
|
|
421
|
+
//auto focus editor on tab switch
|
|
422
|
+
if (that.initEditor.getDomNode() == editor[0]) {
|
|
423
|
+
that.initEditor.focus();
|
|
424
|
+
} else if (that.editor.getDomNode() == editor[0]) {
|
|
425
|
+
that.editor.focus();
|
|
426
|
+
} else if (that.finalizeEditor.getDomNode() == editor[0]) {
|
|
427
|
+
that.finalizeEditor.focus();
|
|
428
|
+
}
|
|
407
429
|
}
|
|
408
430
|
}
|
|
409
431
|
});
|
|
@@ -438,11 +460,13 @@
|
|
|
438
460
|
}
|
|
439
461
|
});
|
|
440
462
|
|
|
441
|
-
var buildEditor = function(id, value, defaultValue, extraLibs) {
|
|
463
|
+
var buildEditor = function(id, stateId, focus, value, defaultValue, extraLibs) {
|
|
442
464
|
var editor = RED.editor.createEditor({
|
|
443
465
|
id: id,
|
|
444
466
|
mode: 'ace/mode/nrjavascript',
|
|
445
467
|
value: value || defaultValue || "",
|
|
468
|
+
stateId: stateId,
|
|
469
|
+
focus: true,
|
|
446
470
|
globals: {
|
|
447
471
|
msg:true,
|
|
448
472
|
context:true,
|
|
@@ -462,11 +486,12 @@
|
|
|
462
486
|
if (defaultValue && value === "") {
|
|
463
487
|
editor.moveCursorTo(defaultValue.split("\n").length - 1, 0);
|
|
464
488
|
}
|
|
489
|
+
editor.__stateId = stateId;
|
|
465
490
|
return editor;
|
|
466
491
|
}
|
|
467
|
-
this.initEditor = buildEditor('node-input-init-editor'
|
|
468
|
-
this.editor = buildEditor('node-input-func-editor'
|
|
469
|
-
this.finalizeEditor = buildEditor('node-input-finalize-editor'
|
|
492
|
+
this.initEditor = buildEditor('node-input-init-editor', this.id + "/" + "initEditor", false, $("#node-input-initialize").val(), RED._("node-red:function.text.initialize"))
|
|
493
|
+
this.editor = buildEditor('node-input-func-editor', this.id + "/" + "editor", true, $("#node-input-func").val(), undefined, that.libs || [])
|
|
494
|
+
this.finalizeEditor = buildEditor('node-input-finalize-editor', this.id + "/" + "finalizeEditor", false, $("#node-input-finalize").val(), RED._("node-red:function.text.finalize"))
|
|
470
495
|
|
|
471
496
|
RED.library.create({
|
|
472
497
|
url:"functions", // where to get the data from
|
|
@@ -505,28 +530,33 @@
|
|
|
505
530
|
],
|
|
506
531
|
ext:"js"
|
|
507
532
|
});
|
|
508
|
-
this.editor.focus();
|
|
509
|
-
|
|
510
533
|
|
|
511
534
|
var expandButtonClickHandler = function(editor) {
|
|
512
|
-
return function(e) {
|
|
535
|
+
return function (e) {
|
|
513
536
|
e.preventDefault();
|
|
514
537
|
var value = editor.getValue();
|
|
538
|
+
editor.saveView(`inside function-expandButtonClickHandler ${editor.__stateId}`);
|
|
515
539
|
var extraLibs = that.libs || [];
|
|
516
540
|
RED.editor.editJavaScript({
|
|
517
541
|
value: value,
|
|
518
542
|
width: "Infinity",
|
|
519
|
-
|
|
543
|
+
stateId: editor.__stateId,
|
|
520
544
|
mode: "ace/mode/nrjavascript",
|
|
521
|
-
|
|
545
|
+
focus: true,
|
|
546
|
+
cancel: function () {
|
|
547
|
+
setTimeout(function () {
|
|
548
|
+
editor.focus();
|
|
549
|
+
}, 250);
|
|
550
|
+
},
|
|
551
|
+
complete: function (v, cursor) {
|
|
522
552
|
editor.setValue(v, -1);
|
|
523
|
-
|
|
524
|
-
|
|
553
|
+
setTimeout(function () {
|
|
554
|
+
editor.restoreView();
|
|
525
555
|
editor.focus();
|
|
526
|
-
},
|
|
556
|
+
}, 250);
|
|
527
557
|
},
|
|
528
558
|
extraLibs: extraLibs
|
|
529
|
-
})
|
|
559
|
+
});
|
|
530
560
|
}
|
|
531
561
|
}
|
|
532
562
|
$("#node-init-expand-js").on("click", expandButtonClickHandler(this.initEditor));
|
|
@@ -605,6 +635,12 @@
|
|
|
605
635
|
this.finalizeEditor.resize();
|
|
606
636
|
|
|
607
637
|
$("#node-input-libs-container").css("height", (height - 192)+"px");
|
|
638
|
+
},
|
|
639
|
+
onadd: function() {
|
|
640
|
+
if (this.name === '_DEFAULT_') {
|
|
641
|
+
this.name = ''
|
|
642
|
+
RED.actions.invoke("core:generate-node-names", this)
|
|
643
|
+
}
|
|
608
644
|
}
|
|
609
645
|
});
|
|
610
646
|
})();
|
|
@@ -146,13 +146,13 @@
|
|
|
146
146
|
|
|
147
147
|
function createTypeValueField(row, defaultType){
|
|
148
148
|
return $('<input/>',{class:"node-input-rule-type-value",type:"text",style:"width: 100%;"}).appendTo(row).typedInput({default:defaultType || 'string',types:[
|
|
149
|
-
{value:"string",label:RED._("common.type.string"),hasValue:false,icon:"red/images/typedInput/az.
|
|
150
|
-
{value:"number",label:RED._("common.type.number"),hasValue:false,icon:"red/images/typedInput/09.
|
|
151
|
-
{value:"boolean",label:RED._("common.type.boolean"),hasValue:false,icon:"red/images/typedInput/bool.
|
|
152
|
-
{value:"array",label:RED._("common.type.array"),hasValue:false,icon:"red/images/typedInput/json.
|
|
153
|
-
{value:"buffer",label:RED._("common.type.buffer"),hasValue:false,icon:"red/images/typedInput/bin.
|
|
154
|
-
{value:"object",label:RED._("common.type.object"),hasValue:false,icon:"red/images/typedInput/json.
|
|
155
|
-
{value:"json",label:RED._("common.type.jsonString"),hasValue:false,icon:"red/images/typedInput/json.
|
|
149
|
+
{value:"string",label:RED._("common.type.string"),hasValue:false,icon:"red/images/typedInput/az.svg"},
|
|
150
|
+
{value:"number",label:RED._("common.type.number"),hasValue:false,icon:"red/images/typedInput/09.svg"},
|
|
151
|
+
{value:"boolean",label:RED._("common.type.boolean"),hasValue:false,icon:"red/images/typedInput/bool.svg"},
|
|
152
|
+
{value:"array",label:RED._("common.type.array"),hasValue:false,icon:"red/images/typedInput/json.svg"},
|
|
153
|
+
{value:"buffer",label:RED._("common.type.buffer"),hasValue:false,icon:"red/images/typedInput/bin.svg"},
|
|
154
|
+
{value:"object",label:RED._("common.type.object"),hasValue:false,icon:"red/images/typedInput/json.svg"},
|
|
155
|
+
{value:"json",label:RED._("common.type.jsonString"),hasValue:false,icon:"red/images/typedInput/json.svg"},
|
|
156
156
|
{value:"undefined",label:RED._("common.type.undefined"),hasValue:false},
|
|
157
157
|
{value:"null",label:RED._("common.type.null"),hasValue:false}
|
|
158
158
|
]});
|
|
@@ -163,7 +163,9 @@
|
|
|
163
163
|
category: 'function',
|
|
164
164
|
defaults: {
|
|
165
165
|
name: {value:""},
|
|
166
|
-
property: {value:"payload", required:true,
|
|
166
|
+
property: {value:"payload", required:true,
|
|
167
|
+
label:RED._("node-red:common.label.payload"),
|
|
168
|
+
validate: RED.validators.typedInput("propertyType", false)},
|
|
167
169
|
propertyType: { value:"msg" },
|
|
168
170
|
rules: {value:[{t:"eq", v:"", vt:"str"}]},
|
|
169
171
|
checkall: {value:"true", required:true},
|
|
@@ -245,14 +247,16 @@
|
|
|
245
247
|
var row2 = $('<div/>',{style:"display: flex; padding-top: 5px; padding-left: 175px;"}).appendTo(inputRows);
|
|
246
248
|
var row3 = $('<div/>',{style:"display: flex; padding-top: 5px; align-items: center"}).appendTo(inputRows);
|
|
247
249
|
|
|
250
|
+
var row4 = $('<div/>',{style:"visibility: hidden; height: 0px;"}).appendTo(inputRows);
|
|
251
|
+
var textSpan = $("<span/>").appendTo(row4);
|
|
248
252
|
var selectField = $('<select/>',{style:"width:120px; text-align: center;"}).appendTo(row);
|
|
249
|
-
var group0 = $('<optgroup/>', { label: "value
|
|
253
|
+
var group0 = $('<optgroup/>', { label: RED._("node-red:switch.label.value-rules") }).appendTo(selectField);
|
|
250
254
|
for (var d in operators) {
|
|
251
255
|
if(operators[d].kind === 'V') {
|
|
252
256
|
group0.append($("<option></option>").val(operators[d].v).text(/^switch/.test(operators[d].t)?node._(operators[d].t):operators[d].t));
|
|
253
257
|
}
|
|
254
258
|
}
|
|
255
|
-
var group1 = $('<optgroup/>', { label: "sequence
|
|
259
|
+
var group1 = $('<optgroup/>', { label: RED._("node-red:switch.label.sequence-rules") }).appendTo(selectField);
|
|
256
260
|
for (var d in operators) {
|
|
257
261
|
if(operators[d].kind === 'S') {
|
|
258
262
|
group1.append($("<option></option>").val(operators[d].v).text(/^switch/.test(operators[d].t)?node._(operators[d].t):operators[d].t));
|
|
@@ -338,9 +342,12 @@
|
|
|
338
342
|
row3.hide();
|
|
339
343
|
}
|
|
340
344
|
var selectedLabel = selectField.find("option:selected").text();
|
|
341
|
-
|
|
345
|
+
|
|
346
|
+
textSpan.text(selectedLabel);
|
|
347
|
+
var width = textSpan.width();
|
|
348
|
+
if (width <= 30) {
|
|
342
349
|
selectField.outerWidth(60);
|
|
343
|
-
} else if (
|
|
350
|
+
} else if (width <= 85) {
|
|
344
351
|
selectField.outerWidth(120);
|
|
345
352
|
} else {
|
|
346
353
|
selectField.width("auto")
|
|
@@ -55,6 +55,7 @@ module.exports = function(RED) {
|
|
|
55
55
|
catch(e) { return false;}
|
|
56
56
|
}
|
|
57
57
|
else if (b === "null") { return a === null; }
|
|
58
|
+
else if (b === "number") { return typeof a === b && !isNaN(a) }
|
|
58
59
|
else { return typeof a === b && !Array.isArray(a) && !Buffer.isBuffer(a) && a !== null; }
|
|
59
60
|
},
|
|
60
61
|
'head': function(a, b, c, d, parts) {
|
|
@@ -19,38 +19,66 @@
|
|
|
19
19
|
|
|
20
20
|
<script type="text/javascript">
|
|
21
21
|
(function() {
|
|
22
|
-
function
|
|
22
|
+
function isInvalidProperty(v,vt) {
|
|
23
23
|
if (/msg|flow|global/.test(vt)) {
|
|
24
24
|
if (!RED.utils.validatePropertyExpression(v)) {
|
|
25
|
-
return
|
|
25
|
+
return RED._("node-red:change.errors.invalid-prop", {
|
|
26
|
+
property: v
|
|
27
|
+
});
|
|
26
28
|
}
|
|
27
29
|
} else if (vt === "jsonata") {
|
|
28
|
-
try{jsonata(v);}catch(e){
|
|
30
|
+
try{ jsonata(v); } catch(e) {
|
|
31
|
+
return RED._("node-red:change.errors.invalid-expr", {
|
|
32
|
+
error: e.message
|
|
33
|
+
});
|
|
34
|
+
}
|
|
29
35
|
} else if (vt === "json") {
|
|
30
|
-
try{JSON.parse(v);}catch(e){
|
|
36
|
+
try{ JSON.parse(v); } catch(e) {
|
|
37
|
+
return RED._("node-red:change.errors.invalid-json-data", {
|
|
38
|
+
error: e.message
|
|
39
|
+
});
|
|
40
|
+
}
|
|
31
41
|
}
|
|
32
|
-
return
|
|
42
|
+
return false;
|
|
33
43
|
}
|
|
44
|
+
|
|
34
45
|
RED.nodes.registerType('change', {
|
|
35
46
|
color: "#E2D96E",
|
|
36
47
|
category: 'function',
|
|
37
48
|
defaults: {
|
|
38
49
|
name: {value:""},
|
|
39
|
-
rules:{value:[{t:"set",p:"payload",pt:"msg",to:"",tot:"str"}],validate: function(rules) {
|
|
50
|
+
rules:{value:[{t:"set",p:"payload",pt:"msg",to:"",tot:"str"}],validate: function(rules, opt) {
|
|
51
|
+
var msg;
|
|
40
52
|
if (!rules || rules.length === 0) { return true }
|
|
41
53
|
for (var i=0;i<rules.length;i++) {
|
|
42
54
|
var r = rules[i];
|
|
43
55
|
if (r.t === 'set') {
|
|
44
|
-
if (
|
|
45
|
-
return
|
|
56
|
+
if (msg = isInvalidProperty(r.p,r.pt)) {
|
|
57
|
+
return msg;
|
|
58
|
+
}
|
|
59
|
+
if (msg = isInvalidProperty(r.to,r.tot)) {
|
|
60
|
+
return msg;
|
|
46
61
|
}
|
|
47
62
|
} else if (r.t === 'change') {
|
|
48
|
-
if (
|
|
49
|
-
return
|
|
63
|
+
if (msg = isInvalidProperty(r.p,r.pt)) {
|
|
64
|
+
return msg;
|
|
65
|
+
}
|
|
66
|
+
if(msg = isInvalidProperty(r.from,r.fromt)) {
|
|
67
|
+
return msg;
|
|
68
|
+
}
|
|
69
|
+
if(msg = isInvalidProperty(r.to,r.tot)) {
|
|
70
|
+
return msg;
|
|
71
|
+
}
|
|
72
|
+
} else if (r.t === 'delete') {
|
|
73
|
+
if (msg = isInvalidProperty(r.p,r.pt)) {
|
|
74
|
+
return msg;
|
|
50
75
|
}
|
|
51
76
|
} else if (r.t === 'move') {
|
|
52
|
-
if (
|
|
53
|
-
return
|
|
77
|
+
if (msg = isInvalidProperty(r.p,r.pt)) {
|
|
78
|
+
return msg;
|
|
79
|
+
}
|
|
80
|
+
if (msg = isInvalidProperty(r.to,r.tot)) {
|
|
81
|
+
return msg;
|
|
54
82
|
}
|
|
55
83
|
}
|
|
56
84
|
}
|
|
@@ -41,13 +41,22 @@
|
|
|
41
41
|
color: "#E2D96E",
|
|
42
42
|
category: 'function',
|
|
43
43
|
defaults: {
|
|
44
|
-
minin: {value:"",required:true,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
minin: {value:"", required: true,
|
|
45
|
+
label:RED._("node-red:range.label.minin"),
|
|
46
|
+
validate:RED.validators.number(false)},
|
|
47
|
+
maxin: {value:"", required: true,
|
|
48
|
+
label:RED._("node-red:range.label.maxin"),
|
|
49
|
+
validate:RED.validators.number(false)},
|
|
50
|
+
minout: {value:"", required:true,
|
|
51
|
+
label:RED._("node-red:range.label.minout"),
|
|
52
|
+
validate:RED.validators.number(false)},
|
|
53
|
+
maxout: {value:"", required:true,
|
|
54
|
+
label:RED._("node-red:range.label.maxout"),
|
|
55
|
+
validate:RED.validators.number(false)},
|
|
48
56
|
action: {value:"scale"},
|
|
49
57
|
round: {value:false},
|
|
50
|
-
property: {value:"payload",required:true
|
|
58
|
+
property: {value:"payload",required:true,
|
|
59
|
+
label:RED._("node-red:common.label.property")},
|
|
51
60
|
name: {value:""}
|
|
52
61
|
},
|
|
53
62
|
inputs: 1,
|