@node-red/nodes 2.2.0 → 3.0.0-beta.1
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/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 +60 -6
- package/core/common/21-debug.js +60 -29
- package/core/common/60-link.html +66 -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 +3 -1
- 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 +205 -95
- package/core/network/21-httpin.html +6 -2
- package/core/network/21-httprequest.html +217 -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 +5 -1
- 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 +18 -3
- package/locales/en-US/messages.json +68 -17
- 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 +12 -0
- package/locales/ja/messages.json +65 -18
- 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
|
})();
|
|
@@ -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},
|
|
@@ -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,
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<option value="handlebars">mustache</option>
|
|
19
19
|
<option value="html">HTML</option>
|
|
20
20
|
<option value="json">JSON</option>
|
|
21
|
-
<option value="javascript">
|
|
21
|
+
<option value="javascript">JavaScript</option>
|
|
22
22
|
<option value="css">CSS</option>
|
|
23
23
|
<option value="markdown">Markdown</option>
|
|
24
24
|
<option value="python">Python</option>
|
|
@@ -56,7 +56,9 @@
|
|
|
56
56
|
category: 'function',
|
|
57
57
|
defaults: {
|
|
58
58
|
name: {value:""},
|
|
59
|
-
field: {value:"payload",
|
|
59
|
+
field: {value:"payload",
|
|
60
|
+
label:"payload",
|
|
61
|
+
validate:RED.validators.typedInput("fieldType", false)},
|
|
60
62
|
fieldType: {value:"msg"},
|
|
61
63
|
format: {value:"handlebars"},
|
|
62
64
|
syntax: {value:"mustache"},
|
|
@@ -73,7 +75,8 @@
|
|
|
73
75
|
return this.name?"node_label_italic":"";
|
|
74
76
|
},
|
|
75
77
|
oneditprepare: function() {
|
|
76
|
-
|
|
78
|
+
const that = this;
|
|
79
|
+
const stateId = RED.editor.generateViewStateId("node", this, "");
|
|
77
80
|
if (!this.field) {
|
|
78
81
|
this.field = 'payload';
|
|
79
82
|
$("#node-input-field").val("payload");
|
|
@@ -90,10 +93,10 @@
|
|
|
90
93
|
types: ['msg','flow','global'],
|
|
91
94
|
typeField: $("#node-input-fieldType")
|
|
92
95
|
});
|
|
93
|
-
|
|
94
96
|
this.editor = RED.editor.createEditor({
|
|
95
97
|
id: 'node-input-template-editor',
|
|
96
98
|
mode: 'ace/mode/html',
|
|
99
|
+
stateId: stateId,
|
|
97
100
|
value: $("#node-input-template").val()
|
|
98
101
|
});
|
|
99
102
|
RED.library.create({
|
|
@@ -103,7 +106,6 @@
|
|
|
103
106
|
fields:['name','format','output','syntax'],
|
|
104
107
|
ext: "txt"
|
|
105
108
|
});
|
|
106
|
-
this.editor.focus();
|
|
107
109
|
|
|
108
110
|
$("#node-input-format").on("change", function() {
|
|
109
111
|
var mod = "ace/mode/"+$("#node-input-format").val();
|
|
@@ -113,20 +115,22 @@
|
|
|
113
115
|
});
|
|
114
116
|
});
|
|
115
117
|
RED.popover.tooltip($("#node-template-expand-editor"), RED._("node-red:common.label.expand"));
|
|
116
|
-
$("#node-template-expand-editor").on("click", function(e) {
|
|
118
|
+
$("#node-template-expand-editor").on("click", function (e) {
|
|
117
119
|
e.preventDefault();
|
|
118
|
-
|
|
120
|
+
const value = that.editor.getValue();
|
|
121
|
+
that.editor.saveView();
|
|
119
122
|
RED.editor.editText({
|
|
120
123
|
mode: $("#node-input-format").val(),
|
|
121
124
|
value: value,
|
|
125
|
+
stateId: stateId,
|
|
122
126
|
width: "Infinity",
|
|
123
|
-
|
|
124
|
-
complete: function(v,cursor) {
|
|
127
|
+
focus: true,
|
|
128
|
+
complete: function (v, cursor) {
|
|
125
129
|
that.editor.setValue(v, -1);
|
|
126
|
-
|
|
127
|
-
|
|
130
|
+
setTimeout(function () {
|
|
131
|
+
that.editor.restoreView();
|
|
128
132
|
that.editor.focus();
|
|
129
|
-
},
|
|
133
|
+
}, 250);
|
|
130
134
|
}
|
|
131
135
|
})
|
|
132
136
|
})
|