@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.
Files changed (51) hide show
  1. package/core/common/05-junction.html +5 -0
  2. package/core/common/05-junction.js +12 -0
  3. package/core/common/20-inject.html +25 -13
  4. package/core/common/21-debug.html +60 -6
  5. package/core/common/21-debug.js +60 -29
  6. package/core/common/60-link.html +66 -29
  7. package/core/common/60-link.js +169 -20
  8. package/core/common/lib/debug/debug-utils.js +34 -1
  9. package/core/function/10-function.html +57 -21
  10. package/core/function/10-switch.html +3 -1
  11. package/core/function/10-switch.js +1 -0
  12. package/core/function/15-change.html +40 -12
  13. package/core/function/16-range.html +14 -5
  14. package/core/function/80-template.html +16 -12
  15. package/core/function/89-delay.html +46 -6
  16. package/core/function/89-trigger.html +12 -4
  17. package/core/function/rbe.html +7 -3
  18. package/core/network/05-tls.html +10 -4
  19. package/core/network/06-httpproxy.html +10 -1
  20. package/core/network/10-mqtt.html +73 -17
  21. package/core/network/10-mqtt.js +205 -95
  22. package/core/network/21-httpin.html +6 -2
  23. package/core/network/21-httprequest.html +217 -12
  24. package/core/network/21-httprequest.js +98 -17
  25. package/core/network/22-websocket.html +19 -5
  26. package/core/network/22-websocket.js +16 -13
  27. package/core/network/31-tcpin.html +47 -10
  28. package/core/network/31-tcpin.js +8 -3
  29. package/core/network/32-udp.html +14 -2
  30. package/core/parsers/70-CSV.html +4 -1
  31. package/core/parsers/70-JSON.html +3 -2
  32. package/core/parsers/70-XML.html +2 -1
  33. package/core/parsers/70-YAML.html +2 -1
  34. package/core/sequence/17-split.html +5 -1
  35. package/core/sequence/19-batch.html +28 -4
  36. package/core/storage/10-file.html +68 -8
  37. package/core/storage/10-file.js +46 -3
  38. package/core/storage/23-watch.html +2 -1
  39. package/core/storage/23-watch.js +21 -43
  40. package/locales/de/messages.json +1 -0
  41. package/locales/en-US/common/60-link.html +18 -3
  42. package/locales/en-US/messages.json +68 -17
  43. package/locales/en-US/network/21-httprequest.html +1 -1
  44. package/locales/en-US/storage/10-file.html +6 -2
  45. package/locales/ja/common/60-link.html +12 -0
  46. package/locales/ja/messages.json +65 -18
  47. package/locales/ko/messages.json +1 -0
  48. package/locales/ru/messages.json +1 -0
  49. package/locales/zh-CN/messages.json +1 -0
  50. package/locales/zh-TW/messages.json +1 -0
  51. package/package.json +12 -12
@@ -50,7 +50,8 @@
50
50
  </div>
51
51
 
52
52
  <div id="node-row-newline" class="form-row hidden" style="padding-left:110px;">
53
- <span data-i18n="tcpin.label.delimited"></span> <input type="text" id="node-input-newline" style="width:110px;" data-i18n="[placeholder]tcpin.label.optional">
53
+ <span data-i18n="tcpin.label.delimited"></span> <input type="text" id="node-input-newline" style="width:110px;" data-i18n="[placeholder]tcpin.label.optional"><br/>
54
+ <input type="checkbox" id="node-input-trim" style="display:inline-block; width:auto; vertical-align:top;"> <span data-i18n="tcpin.label.reattach"></span>
54
55
  </div>
55
56
 
56
57
  <div class="form-row">
@@ -70,14 +71,27 @@
70
71
  defaults: {
71
72
  name: {value:""},
72
73
  server: {value:"server", required:true},
73
- host: {value:"", validate:function(v) { return (this.server == "server")||v.length > 0;} },
74
- port: {value:"", required:true, validate:RED.validators.number()},
74
+ host: {
75
+ value:"",
76
+ validate:function(v, opt) {
77
+ if ((this.server == "server")||v.length > 0) {
78
+ return true;
79
+ }
80
+ return RED._("node-red:tcpin.errors.invalid-host");
81
+ }
82
+ },
83
+ port: {
84
+ value:"", required:true,
85
+ label:RED._("node-red:tcpin.label.port"),
86
+ validate:RED.validators.number(false)},
75
87
  datamode:{value:"stream"},
76
88
  datatype:{value:"buffer"},
77
89
  newline:{value:""},
78
90
  topic: {value:""},
91
+ trim: {value:false},
79
92
  base64: {/*deprecated*/ value:false, required:true},
80
- tls: {type:"tls-config", value:'', required:false}
93
+ tls: {type:"tls-config", value:'', required:false,
94
+ label:RED._("node-red:httpin.tls-config") }
81
95
  },
82
96
  inputs:0,
83
97
  outputs:1,
@@ -186,12 +200,29 @@
186
200
  color: "Silver",
187
201
  defaults: {
188
202
  name: {value:""},
189
- host: {value:"",validate:function(v) { return (this.beserver != "client")||v.length > 0;} },
190
- port: {value:"",validate:function(v) { return (this.beserver == "reply")||RED.validators.number()(v); } },
203
+ host: {
204
+ value:"",
205
+ validate:function(v, opt) {
206
+ if ((this.beserver != "client")||v.length > 0) {
207
+ return true;
208
+ }
209
+ return RED._("node-red:tcpin.errors.invalid-host");
210
+ }
211
+ },
212
+ port: {
213
+ value:"",
214
+ validate:function(v) {
215
+ if ((this.beserver == "reply")||RED.validators.number()(v)) {
216
+ return true;
217
+ }
218
+ return RED._("node-red:tcpin.errors.invalid-port");
219
+ }
220
+ },
191
221
  beserver: {value:"client", required:true},
192
222
  base64: {value:false, required:true},
193
223
  end: {value:false, required:true},
194
- tls: {type:"tls-config", value:'', required:false}
224
+ tls: {type:"tls-config", value:'', required:false,
225
+ label:RED._("node-red:httpin.tls-config") }
195
226
  },
196
227
  inputs:1,
197
228
  outputs:0,
@@ -286,7 +317,8 @@
286
317
  <span id="node-units"></span>
287
318
  </div>
288
319
  <div id="node-row-newline" class="form-row hidden" style="padding-left:162px;">
289
- <span data-i18n="tcpin.label.delimited"></span> <input type="text" id="node-input-newline" style="width:110px;" data-i18n="[placeholder]tcpin.label.optional">
320
+ <span data-i18n="tcpin.label.delimited"></span> <input type="text" id="node-input-newline" style="width:110px;" data-i18n="[placeholder]tcpin.label.optional"><br/>
321
+ <input type="checkbox" id="node-input-trim" style="display:inline-block; width:auto; vertical-align:top;"> <span data-i18n="tcpin.label.reattach"></span>
290
322
  </div>
291
323
  <div class="form-row">
292
324
  <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
@@ -301,12 +333,17 @@
301
333
  defaults: {
302
334
  name: {value:""},
303
335
  server: {value:""},
304
- port: {value:"", validate:RED.validators.regex(/^(\d*|)$/)},
336
+ port: {
337
+ value:"",
338
+ label: RED._("node-red:tcpin.label.port"),
339
+ validate:RED.validators.regex(/^(\d*|)$/)
340
+ },
305
341
  out: {value:"time", required:true},
306
342
  ret: {value:"buffer"},
307
343
  splitc: {value:"0", required:true},
308
344
  newline: {value:""},
309
- tls: {type:"tls-config", value:'', required:false}
345
+ trim: {value:false},
346
+ tls: {type:"tls-config", value:'', required:false, label:RED._("node-red:httpin.tls-config")}
310
347
  },
311
348
  inputs:1,
312
349
  outputs:1,
@@ -88,6 +88,7 @@ module.exports = function(RED) {
88
88
  this.datatype = n.datatype||'buffer'; /* buffer,utf8,base64 */
89
89
  this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r").replace("\\t","\t");
90
90
  this.base64 = n.base64;
91
+ this.trim = n.trim || false;
91
92
  this.server = (typeof n.server == 'boolean')?n.server:(n.server == "server");
92
93
  this.closing = false;
93
94
  this.connected = false;
@@ -135,7 +136,8 @@ module.exports = function(RED) {
135
136
  buffer = buffer+data;
136
137
  var parts = buffer.split(node.newline);
137
138
  for (var i = 0; i<parts.length-1; i+=1) {
138
- msg = {topic:node.topic, payload:parts[i] + node.newline.trimEnd()};
139
+ msg = {topic:node.topic, payload:parts[i]};
140
+ if (node.trim == true) { msg.payload += node.newline; }
139
141
  msg._session = {type:"tcp",id:id};
140
142
  node.send(msg);
141
143
  }
@@ -229,7 +231,8 @@ module.exports = function(RED) {
229
231
  buffer = buffer+data;
230
232
  var parts = buffer.split(node.newline);
231
233
  for (var i = 0; i<parts.length-1; i+=1) {
232
- msg = {topic:node.topic, payload:parts[i] + node.newline.trimEnd(), ip:socket.remoteAddress, port:socket.remotePort};
234
+ msg = {topic:node.topic, payload:parts[i], ip:socket.remoteAddress, port:socket.remotePort};
235
+ if (node.trim == true) { msg.payload += node.newline; }
233
236
  msg._session = {type:"tcp",id:id};
234
237
  node.send(msg);
235
238
  }
@@ -518,6 +521,7 @@ module.exports = function(RED) {
518
521
  this.out = n.out;
519
522
  this.ret = n.ret || "buffer";
520
523
  this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r").replace("\\t","\t");
524
+ this.trim = n.trim || false;
521
525
  this.splitc = n.splitc;
522
526
  if (n.tls) {
523
527
  var tlsNode = RED.nodes.getNode(n.tls);
@@ -653,7 +657,8 @@ module.exports = function(RED) {
653
657
  let parts = chunk.split(node.newline);
654
658
  for (var p=0; p<parts.length-1; p+=1) {
655
659
  let m = RED.util.cloneMessage(msg);
656
- m.payload = parts[p] + node.newline.trimEnd();
660
+ m.payload = parts[p];
661
+ if (node.trim == true) { m.payload += node.newline; }
657
662
  nodeSend(m);
658
663
  }
659
664
  chunk = parts[parts.length-1];
@@ -62,10 +62,22 @@
62
62
  defaults: {
63
63
  name: {value:""},
64
64
  iface: {value:""},
65
- port: {value:"",required:true,validate:RED.validators.number()},
65
+ port: {
66
+ value:"", required:true,
67
+ label:RED._("node-red:udp.label.port"),
68
+ validate:RED.validators.number(false)
69
+ },
66
70
  ipv: {value:"udp4"},
67
71
  multicast: {value:"false"},
68
- group: {value:"",validate:function(v) { return (this.multicast !== "true")||v.length > 0;} },
72
+ group: {
73
+ value:"",
74
+ validate:function(v,opt) {
75
+ if ((this.multicast !== "true")||v.length > 0) {
76
+ return true;
77
+ }
78
+ return RED._("node-red:udp.errors.invalid-group");
79
+ }
80
+ },
69
81
  datatype: {value:"buffer",required:true}
70
82
  },
71
83
  inputs:0,
@@ -75,7 +75,10 @@
75
75
  color:"#DEBD5C",
76
76
  defaults: {
77
77
  name: {value:""},
78
- sep: {value:',',required:true,validate:RED.validators.regex(/^.{1,2}$/)},
78
+ sep: {
79
+ value:',', required:true,
80
+ label:RED._("node-red:csv.label.separator"),
81
+ validate:RED.validators.regex(/^.{1,2}$/)},
79
82
  //quo: {value:'"',required:true},
80
83
  hdrin: {value:""},
81
84
  hdrout: {value:"none"},
@@ -21,7 +21,7 @@
21
21
  <label style="width:100%;"><span data-i18n="json.label.o2j"></span></label>
22
22
  </div>
23
23
  <div class="form-row node-json-to-json-options" style="padding-left: 20px;">
24
- <input style="width:20px; vertical-align:top; margin-right: 5px;" type="checkbox" id="node-input-pretty"><label style="width: auto;" for="node-input-pretty" data-i18n="json.label.pretty"></span>
24
+ <input style="width:20px; vertical-align:top; margin-right: 5px;" type="checkbox" id="node-input-pretty"><label style="width: auto;" for="node-input-pretty" data-i18n="json.label.pretty"></label>
25
25
  </div>
26
26
  </script>
27
27
 
@@ -31,7 +31,8 @@
31
31
  color:"#DEBD5C",
32
32
  defaults: {
33
33
  name: {value:""},
34
- property: {value:"payload",required:true},
34
+ property: {value:"payload",required:true,
35
+ label:RED._("node-red:json.label.property")},
35
36
  action: {value:""},
36
37
  pretty: {value:false}
37
38
  },
@@ -26,7 +26,8 @@
26
26
  color:"#DEBD5C",
27
27
  defaults: {
28
28
  name: {value:""},
29
- property: {value:"payload",required:true},
29
+ property: {value:"payload",required:true,
30
+ label:RED._("node-red:common.label.property")},
30
31
  attr: {value:""},
31
32
  chr: {value:""}
32
33
  },
@@ -15,7 +15,8 @@
15
15
  category: 'parser',
16
16
  color:"#DEBD5C",
17
17
  defaults: {
18
- property: {value:"payload",required:true},
18
+ property: {value:"payload",required:true,
19
+ label:RED._("node-red:common.label.property")},
19
20
  name: {value:""}
20
21
  },
21
22
  inputs:1,
@@ -202,7 +202,11 @@
202
202
  name: {value:""},
203
203
  mode: {value:"auto"},
204
204
  build: { value:"object"},
205
- property: { value:"payload", validate:RED.validators.typedInput("propertyType")},
205
+ property: {
206
+ value:"payload",
207
+ label: RED._("node-red:join.message-prop"),
208
+ validate:RED.validators.typedInput("propertyType", false)
209
+ },
206
210
  propertyType: { value:"msg"},
207
211
  key: {value:"topic"},
208
212
  joiner: { value:"\\n"},
@@ -47,7 +47,7 @@
47
47
  <div class="form-row">
48
48
  <input type="checkbox" id="node-input-allowEmptySequence" style="margin-left:20px; margin-right: 10px; vertical-align:top; width:auto;">
49
49
  <label for="node-input-allowEmptySequence" style="width:auto;" data-i18n="batch.interval.empty"></label>
50
- </div>
50
+ </div>
51
51
  </div>
52
52
 
53
53
  <div class="node-row-msg-concat">
@@ -73,9 +73,33 @@
73
73
  defaults: {
74
74
  name: {value:""},
75
75
  mode: {value:"count"},
76
- count: {value:10,validate:function(v) { return RED.validators.number(v) && (v >= 1); }},
77
- overlap: {value:0,validate:function(v) { return RED.validators.number(v) && (v >= 0); }},
78
- interval: {value:10,validate:function(v) { return RED.validators.number(v) && (v >= 1); }},
76
+ count: {
77
+ value:10,
78
+ validate:function(v, opt) {
79
+ if (RED.validators.number(v) && (v >= 1)) {
80
+ return true;
81
+ }
82
+ return RED._("node-red:batch.error.invalid-count");
83
+ }
84
+ },
85
+ overlap: {
86
+ value:0,
87
+ validate:function(v, opt) {
88
+ if (RED.validators.number(v) && (v >= 0)) {
89
+ return true;
90
+ }
91
+ return RED._("node-red:batch.error.invalid-overlap");
92
+ }
93
+ },
94
+ interval: {
95
+ value:10,
96
+ validate:function(v, opt) {
97
+ if (RED.validators.number(v) && (v >= 1)) {
98
+ return true;
99
+ }
100
+ return RED._("node-red:batch.error.invalid-interval");
101
+ }
102
+ },
79
103
  allowEmptySequence: {value:false},
80
104
  topics: {value:[{topic:""}]}
81
105
  },
@@ -3,6 +3,7 @@
3
3
  <div class="form-row node-input-filename">
4
4
  <label for="node-input-filename"><i class="fa fa-file"></i> <span data-i18n="file.label.filename"></span></label>
5
5
  <input id="node-input-filename" type="text">
6
+ <input type="hidden" id="node-input-filenameType">
6
7
  </div>
7
8
  <div class="form-row">
8
9
  <label for="node-input-overwriteFile"><i class="fa fa-random"></i> <span data-i18n="file.label.action"></span></label>
@@ -29,7 +30,7 @@
29
30
  </div>
30
31
  <div class="form-row">
31
32
  <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
32
- <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
33
+ <input type="text" id="node-input-name">
33
34
  </div>
34
35
  <div class="form-tips"><span data-i18n="file.tip"></span></div>
35
36
  </script>
@@ -37,7 +38,8 @@
37
38
  <script type="text/html" data-template-name="file in">
38
39
  <div class="form-row">
39
40
  <label for="node-input-filename"><i class="fa fa-file"></i> <span data-i18n="file.label.filename"></span></label>
40
- <input id="node-input-filename" type="text" data-i18n="[placeholder]file.label.filename">
41
+ <input id="node-input-filename" type="text">
42
+ <input type="hidden" id="node-input-filenameType">
41
43
  </div>
42
44
  <div class="form-row">
43
45
  <label for="node-input-format"><i class="fa fa-sign-out"></i> <span data-i18n="file.label.outputas"></span></label>
@@ -60,7 +62,7 @@
60
62
  </div>
61
63
  <div class="form-row">
62
64
  <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
63
- <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
65
+ <input type="text" id="node-input-name">
64
66
  </div>
65
67
  <div class="form-tips"><span data-i18n="file.tip"></span></div>
66
68
  </script>
@@ -196,7 +198,8 @@
196
198
  category: 'storage',
197
199
  defaults: {
198
200
  name: {value:""},
199
- filename: {value:""},
201
+ filename: {value:"filename"},
202
+ filenameType: {value:"msg"},
200
203
  appendNewline: {value:true},
201
204
  createDir: {value:false},
202
205
  overwriteFile: {value:"false"},
@@ -207,10 +210,13 @@
207
210
  outputs:1,
208
211
  icon: "file-out.svg",
209
212
  label: function() {
213
+ var fn = this.filename;
214
+ if(this.filenameType != "str" && this.filenameType != "env" ) { fn = ""; }
215
+ if(this.filenameType === "env") { fn = "env."+fn; }
210
216
  if (this.overwriteFile === "delete") {
211
- return this.name||this._("file.label.deletelabel",{file:this.filename});
217
+ return this.name||this._("file.label.deletelabel",{file:fn});
212
218
  } else {
213
- return this.name||this.filename||this._("file.label.write");
219
+ return this.name||fn||this._("file.label.write");
214
220
  }
215
221
  },
216
222
  paletteLabel: RED._("node-red:file.label.write"),
@@ -229,6 +235,31 @@
229
235
  value: "setbymsg",
230
236
  label: node._("file.encoding.setbymsg")
231
237
  }).text(label).appendTo(encSel);
238
+ $("#node-input-filename").typedInput({
239
+ default: "msg",
240
+ types:[{ value: "str", label:"", icon:"red/images/typedInput/az.svg"}, "msg", "jsonata", "env"],
241
+ typeField: $("#node-input-filenameType")
242
+ });
243
+ if(typeof node.filenameType == 'undefined') {
244
+ //existing node AND filenameType is not set - inplace (compatible) upgrade to new typedInput
245
+ if(node.filename == "") { //was using empty value to denote msg.filename - set typedInput to match
246
+ node.filename = "filename";
247
+ node.filenameType = "msg";
248
+ $("#node-input-filename").typedInput("type", node.filenameType);
249
+ $("#node-input-filename").typedInput("value", node.filename);
250
+ } else if(/^\${[^}]+}$/.test(node.filename)) { //was using an ${ENV_VAR}
251
+ node.filenameType = "env";
252
+ node.filename = node.filename.replace(/\${([^}]+)}/g, function(match, name) {
253
+ return (name === undefined)?"":name;
254
+ });
255
+ $("#node-input-filename").typedInput("type", node.filenameType);
256
+ $("#node-input-filename").typedInput("value", node.filename);
257
+ } else { //was using a static filename - set typedInput type to str
258
+ node.filenameType = "str";
259
+ $("#node-input-filename").typedInput("type", node.filenameType);
260
+ $("#node-input-filename").typedInput("value", node.filename);
261
+ }
262
+ }
232
263
  encodings.forEach(function(item) {
233
264
  if(Array.isArray(item)) {
234
265
  var group = $("<optgroup/>", {
@@ -266,7 +297,8 @@
266
297
  category: 'storage',
267
298
  defaults: {
268
299
  name: {value:""},
269
- filename: {value:""},
300
+ filename: {value:"filename"},
301
+ filenameType: {value:"msg"},
270
302
  format: {value:"utf8"},
271
303
  chunk: {value:false},
272
304
  sendError: {value: false},
@@ -291,7 +323,10 @@
291
323
  },
292
324
  icon: "file-in.svg",
293
325
  label: function() {
294
- return this.name||this.filename||this._("file.label.read");
326
+ var fn = this.filename;
327
+ if(this.filenameType != "str" && this.filenameType != "env" ) { fn = ""; }
328
+ if(this.filenameType === "env") { fn = "env."+fn; }
329
+ return this.name||fn||this._("file.label.read");
295
330
  },
296
331
  paletteLabel: RED._("node-red:file.label.read"),
297
332
  labelStyle: function() {
@@ -305,6 +340,31 @@
305
340
  value: "none",
306
341
  label: label
307
342
  }).text(label).appendTo(encSel);
343
+ $("#node-input-filename").typedInput({
344
+ default: "msg",
345
+ types:[{ value: "str", label:"", icon:"red/images/typedInput/az.svg"}, "msg", "jsonata", "env"],
346
+ typeField: $("#node-input-filenameType")
347
+ });
348
+ if(typeof node.filenameType == 'undefined') {
349
+ //existing node AND filenameType is not set - inplace (compatible) upgrade to new typedInput
350
+ if(node.filename == "") { //was using empty value to denote msg.filename - set typedInput to match
351
+ node.filename = "filename";
352
+ node.filenameType = "msg";
353
+ $("#node-input-filename").typedInput("type", node.filenameType);
354
+ $("#node-input-filename").typedInput("value", node.filename);
355
+ } else if(/^\${[^}]+}$/.test(node.filename)) { //was using an ${ENV_VAR}
356
+ node.filenameType = "env";
357
+ node.filename = node.filename.replace(/\${([^}]+)}/g, function(match, name) {
358
+ return (name === undefined)?"":name;
359
+ });
360
+ $("#node-input-filename").typedInput("type", node.filenameType);
361
+ $("#node-input-filename").typedInput("value", node.filename);
362
+ } else { //was using a static filename - set typedInput type to str
363
+ node.filenameType = "str";
364
+ $("#node-input-filename").typedInput("type", node.filenameType);
365
+ $("#node-input-filename").typedInput("value", node.filename);
366
+ }
367
+ }
308
368
  encodings.forEach(function(item) {
309
369
  if(Array.isArray(item)) {
310
370
  var group = $("<optgroup/>", {
@@ -39,6 +39,7 @@ module.exports = function(RED) {
39
39
  // Write/delete a file
40
40
  RED.nodes.createNode(this,n);
41
41
  this.filename = n.filename;
42
+ this.filenameType = n.filenameType;
42
43
  this.appendNewline = n.appendNewline;
43
44
  this.overwriteFile = n.overwriteFile.toString();
44
45
  this.createDir = n.createDir || false;
@@ -50,7 +51,28 @@ module.exports = function(RED) {
50
51
  node.closeCallback = null;
51
52
 
52
53
  function processMsg(msg,nodeSend, done) {
53
- var filename = node.filename || msg.filename || "";
54
+ var filename = node.filename || "";
55
+ //Pre V3 compatibility - if filenameType is empty, do in place upgrade
56
+ if(typeof node.filenameType == 'undefined' || node.filenameType == "") {
57
+ //existing node AND filenameType is not set - inplace (compatible) upgrade
58
+ if(filename == "") { //was using empty value to denote msg.filename
59
+ node.filename = "filename";
60
+ node.filenameType = "msg";
61
+ } else { //was using a static filename - set typedInput type to str
62
+ node.filenameType = "str";
63
+ }
64
+ }
65
+
66
+ RED.util.evaluateNodeProperty(node.filename,node.filenameType,node,msg,(err,value) => {
67
+ if (err) {
68
+ node.error(err,msg);
69
+ return done();
70
+ } else {
71
+ filename = value;
72
+ }
73
+ });
74
+ filename = filename || "";
75
+ msg.filename = filename;
54
76
  var fullFilename = filename;
55
77
  if (filename && RED.settings.fileWorkingDirectory && !path.isAbsolute(filename)) {
56
78
  fullFilename = path.resolve(path.join(RED.settings.fileWorkingDirectory,filename));
@@ -158,7 +180,7 @@ module.exports = function(RED) {
158
180
  done();
159
181
  });
160
182
  }
161
- if (node.filename) {
183
+ if (node.filenameType === "str" || node.filenameType === "env") {
162
184
  // Static filename - write and reuse the stream next time
163
185
  node.wstream.write(buf, function() {
164
186
  nodeSend(msg);
@@ -256,6 +278,7 @@ module.exports = function(RED) {
256
278
  // Read a file
257
279
  RED.nodes.createNode(this,n);
258
280
  this.filename = n.filename;
281
+ this.filenameType = n.filenameType;
259
282
  this.format = n.format;
260
283
  this.chunk = false;
261
284
  this.encoding = n.encoding || "none";
@@ -270,8 +293,28 @@ module.exports = function(RED) {
270
293
  var node = this;
271
294
 
272
295
  this.on("input",function(msg, nodeSend, nodeDone) {
273
- var filename = (node.filename || msg.filename || "").replace(/\t|\r|\n/g,'');
296
+ var filename = node.filename || "";
297
+ //Pre V3 compatibility - if filenameType is empty, do in place upgrade
298
+ if(typeof node.filenameType == 'undefined' || node.filenameType == "") {
299
+ //existing node AND filenameType is not set - inplace (compatible) upgrade
300
+ if(filename == "") { //was using empty value to denote msg.filename
301
+ node.filename = "filename";
302
+ node.filenameType = "msg";
303
+ } else { //was using a static filename - set typedInput type to str
304
+ node.filenameType = "str";
305
+ }
306
+ }
307
+ RED.util.evaluateNodeProperty(node.filename,node.filenameType,node,msg,(err,value) => {
308
+ if (err) {
309
+ node.error(err,msg);
310
+ return done();
311
+ } else {
312
+ filename = (value || "").replace(/\t|\r|\n/g,'');
313
+ }
314
+ });
315
+ filename = filename || "";
274
316
  var fullFilename = filename;
317
+ var filePath = "";
275
318
  if (filename && RED.settings.fileWorkingDirectory && !path.isAbsolute(filename)) {
276
319
  fullFilename = path.resolve(path.join(RED.settings.fileWorkingDirectory,filename));
277
320
  }
@@ -36,7 +36,8 @@
36
36
  category: 'storage',
37
37
  defaults: {
38
38
  name: {value:""},
39
- files: {value:"",required:true},
39
+ files: {value:"",required:true,
40
+ label:RED._("node-red:watch.label.files")},
40
41
  recursive: {value:""}
41
42
  },
42
43
  color:"BurlyWood",
@@ -16,24 +16,9 @@
16
16
 
17
17
  module.exports = function(RED) {
18
18
  "use strict";
19
- var Notify = require("fs.notify");
20
- var fs = require("fs");
21
- var path = require("path");
22
-
23
- var getAllDirs = function (dir, filelist) {
24
- filelist = filelist || [];
25
- fs.readdirSync(dir).forEach(file => {
26
- try {
27
- if (fs.statSync(path.join(dir, file)).isDirectory() ) {
28
- filelist.push(path.join(dir, file));
29
- getAllDirs(path.join(dir, file), filelist);
30
- }
31
- } catch (error) {
32
- //should we raise an error?
33
- }
34
- });
35
- return filelist;
36
- }
19
+ const watch = require('node-watch')
20
+ const fs = require("fs")
21
+ const path = require("path")
37
22
 
38
23
  function WatchNode(n) {
39
24
  RED.nodes.createNode(this,n);
@@ -44,52 +29,45 @@ module.exports = function(RED) {
44
29
  this.files[f] = this.files[f].trim();
45
30
  }
46
31
  this.p = (this.files.length === 1) ? this.files[0] : JSON.stringify(this.files);
47
- var node = this;
32
+ const node = this;
48
33
 
49
- if (node.recursive) {
50
- for (var fi in node.files) {
51
- if (node.files.hasOwnProperty(fi)) {
52
- node.files = node.files.concat(getAllDirs( node.files[fi]));
53
- }
54
- }
55
- }
34
+ const watcher = watch(this.files, { recursive: this.recursive });
56
35
 
57
- var notifications = new Notify(node.files);
58
- notifications.on('change', function (file, event, fpath) {
59
- var stat;
36
+ watcher.on('change', function (event, fpath) {
37
+ const file = path.basename(fpath)
38
+ let stat;
60
39
  try {
61
- if (fs.statSync(fpath).isDirectory()) { fpath = path.join(fpath,file); }
62
40
  stat = fs.statSync(fpath);
63
41
  } catch(e) { }
64
- var type = "none";
65
- var msg = { payload:fpath, topic:node.p, file:file, filename:fpath };
42
+ let type = "none";
43
+ const msg = {
44
+ payload:fpath,
45
+ topic:node.p,
46
+ file:file,
47
+ filename:fpath,
48
+ event: event
49
+ };
66
50
  if (stat) {
67
51
  if (stat.isFile()) { type = "file"; msg.size = stat.size; }
68
52
  else if (stat.isBlockDevice()) { type = "blockdevice"; }
69
53
  else if (stat.isCharacterDevice()) { type = "characterdevice"; }
70
54
  else if (stat.isSocket()) { type = "socket"; }
71
55
  else if (stat.isFIFO()) { type = "fifo"; }
72
- else if (stat.isDirectory()) {
73
- type = "directory";
74
- if (node.recursive) {
75
- notifications.add([fpath]);
76
- notifications.add(getAllDirs(fpath));
77
- }
78
- }
56
+ else if (stat.isDirectory()) { type = "directory"; }
79
57
  else { type = "n/a"; }
80
58
  }
81
59
  msg.type = type;
82
60
  node.send(msg);
83
61
  });
84
62
 
85
- notifications.on('error', function (error, fpath) {
86
- var msg = { payload:fpath };
63
+ watcher.on('error', function (error) {
64
+ const msg = { payload: "" };
87
65
  node.error(error,msg);
88
66
  });
89
67
 
90
68
  this.close = function() {
91
- notifications.close();
69
+ watcher.close();
92
70
  }
93
71
  }
94
- RED.nodes.registerType("watch",WatchNode);
72
+ RED.nodes.registerType("watch", WatchNode);
95
73
  }
@@ -423,6 +423,7 @@
423
423
  "string": "Ein String",
424
424
  "base64": "Ein Base64-kodierter String",
425
425
  "auto": "Auto-Erkennung (string oder buffer)",
426
+ "auto-detect": "Auto-Erkennung (parsed JSON-Objekt, string oder buffer)",
426
427
  "json": "Ein analysiertes (parsed) JSON-Objekt"
427
428
  },
428
429
  "true": "wahr",