@node-red/nodes 3.1.8 → 4.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/core/common/21-debug.html +1 -1
- package/core/common/21-debug.js +2 -1
- package/core/common/lib/debug/debug-utils.js +2 -1
- package/core/function/15-change.js +6 -1
- package/core/network/22-websocket.html +206 -2
- package/core/network/22-websocket.js +37 -0
- package/core/network/31-tcpin.js +52 -16
- package/core/parsers/70-CSV.html +39 -4
- package/core/parsers/70-CSV.js +612 -260
- package/core/parsers/70-HTML.html +14 -1
- package/core/parsers/70-HTML.js +11 -0
- package/core/parsers/lib/csv/index.js +324 -0
- package/core/sequence/17-split.html +13 -5
- package/core/sequence/17-split.js +54 -34
- package/locales/de/messages.json +2 -1
- package/locales/en-US/messages.json +15 -5
- package/locales/en-US/network/31-tcpin.html +4 -0
- package/locales/en-US/parsers/70-CSV.html +4 -1
- package/locales/fr/common/91-global-config.html +3 -0
- package/locales/fr/messages.json +18 -5
- package/locales/fr/network/31-tcpin.html +4 -0
- package/locales/fr/parsers/70-CSV.html +3 -1
- package/locales/ja/common/91-global-config.html +1 -1
- package/locales/ja/messages.json +14 -4
- package/locales/ja/network/31-tcpin.html +2 -0
- package/locales/ja/parsers/70-CSV.html +3 -1
- package/locales/ko/messages.json +2 -1
- package/locales/pt-BR/messages.json +2 -1
- package/locales/ru/messages.json +2 -1
- package/locales/zh-CN/messages.json +2 -1
- package/locales/zh-TW/messages.json +2 -1
- package/package.json +1 -1
|
@@ -378,7 +378,7 @@
|
|
|
378
378
|
return { id: id, label: RED.nodes.workspace(id).label } //flow id + name
|
|
379
379
|
} else {
|
|
380
380
|
const instanceNode = RED.nodes.node(id)
|
|
381
|
-
const pathLabel = (instanceNode.name || RED.nodes.subflow(instanceNode.type.substring(8)).
|
|
381
|
+
const pathLabel = (instanceNode.name || RED.nodes.subflow(instanceNode.type.substring(8))?.name || instanceNode.type)
|
|
382
382
|
return { id: id, label: pathLabel }
|
|
383
383
|
}
|
|
384
384
|
})
|
package/core/common/21-debug.js
CHANGED
|
@@ -5,6 +5,7 @@ module.exports = function(RED) {
|
|
|
5
5
|
const fs = require("fs-extra");
|
|
6
6
|
const path = require("path");
|
|
7
7
|
var debuglength = RED.settings.debugMaxLength || 1000;
|
|
8
|
+
var statuslength = RED.settings.debugStatusLength || 32;
|
|
8
9
|
var useColors = RED.settings.debugUseColors || false;
|
|
9
10
|
util.inspect.styles.boolean = "red";
|
|
10
11
|
const { hasOwnProperty } = Object.prototype;
|
|
@@ -164,7 +165,7 @@ module.exports = function(RED) {
|
|
|
164
165
|
}
|
|
165
166
|
}
|
|
166
167
|
|
|
167
|
-
if (st.length >
|
|
168
|
+
if (st.length > statuslength) { st = st.substr(0,statuslength) + "..."; }
|
|
168
169
|
|
|
169
170
|
var newStatus = {fill:fill, shape:shape, text:st};
|
|
170
171
|
if (JSON.stringify(newStatus) !== node.oldState) { // only send if we have to
|
|
@@ -512,7 +512,8 @@ RED.debug = (function() {
|
|
|
512
512
|
hideKey: false,
|
|
513
513
|
path: path,
|
|
514
514
|
sourceId: sourceNode&&sourceNode.id,
|
|
515
|
-
rootPath: path
|
|
515
|
+
rootPath: path,
|
|
516
|
+
nodeSelector: config.messageSourceClick,
|
|
516
517
|
});
|
|
517
518
|
// Do this in a separate step so the element functions aren't stripped
|
|
518
519
|
debugMessage.appendTo(el);
|
|
@@ -117,7 +117,7 @@ module.exports = function(RED) {
|
|
|
117
117
|
});
|
|
118
118
|
return
|
|
119
119
|
} else if (rule.tot === 'date') {
|
|
120
|
-
value =
|
|
120
|
+
value = RED.util.evaluateNodeProperty(rule.to, rule.tot, node)
|
|
121
121
|
} else if (rule.tot === 'jsonata') {
|
|
122
122
|
RED.util.evaluateJSONataExpression(rule.to,msg, (err, value) => {
|
|
123
123
|
if (err) {
|
|
@@ -234,6 +234,11 @@ module.exports = function(RED) {
|
|
|
234
234
|
RED.util.setMessageProperty(msg,property,value);
|
|
235
235
|
} else {
|
|
236
236
|
current = current.replace(fromRE,value);
|
|
237
|
+
if (rule.tot === "bool" && current === ""+value) {
|
|
238
|
+
// If the target type is boolean, and the replace call has resulted in "true"/"false",
|
|
239
|
+
// convert to boolean type (which 'value' already is)
|
|
240
|
+
current = value
|
|
241
|
+
}
|
|
237
242
|
RED.util.setMessageProperty(msg,property,current);
|
|
238
243
|
}
|
|
239
244
|
} else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') {
|
|
@@ -40,6 +40,99 @@
|
|
|
40
40
|
|
|
41
41
|
(function() {
|
|
42
42
|
|
|
43
|
+
const headerTypes = [
|
|
44
|
+
/*
|
|
45
|
+
{ value: "Accept", label: "Accept", hasValue: false },
|
|
46
|
+
{ value: "Accept-Encoding", label: "Accept-Encoding", hasValue: false },
|
|
47
|
+
{ value: "Accept-Language", label: "Accept-Language", hasValue: false },
|
|
48
|
+
*/
|
|
49
|
+
{ value: "Authorization", label: "Authorization", hasValue: false },
|
|
50
|
+
/*
|
|
51
|
+
{ value: "Content-Type", label: "Content-Type", hasValue: false },
|
|
52
|
+
{ value: "Cache-Control", label: "Cache-Control", hasValue: false },
|
|
53
|
+
*/
|
|
54
|
+
{ value: "User-Agent", label: "User-Agent", hasValue: false },
|
|
55
|
+
/*
|
|
56
|
+
{ value: "Location", label: "Location", hasValue: false },
|
|
57
|
+
*/
|
|
58
|
+
{ value: "other", label: RED._("node-red:httpin.label.other"),
|
|
59
|
+
hasValue: true, icon: "red/images/typedInput/az.svg" },
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
const headerOptions = {};
|
|
63
|
+
const defaultOptions = [
|
|
64
|
+
{ value: "other", label: RED._("node-red:httpin.label.other"),
|
|
65
|
+
hasValue: true, icon: "red/images/typedInput/az.svg" },
|
|
66
|
+
"env",
|
|
67
|
+
];
|
|
68
|
+
/*
|
|
69
|
+
headerOptions["accept"] = [
|
|
70
|
+
{ value: "text/plain", label: "text/plain", hasValue: false },
|
|
71
|
+
{ value: "text/html", label: "text/html", hasValue: false },
|
|
72
|
+
{ value: "application/json", label: "application/json", hasValue: false },
|
|
73
|
+
{ value: "application/xml", label: "application/xml", hasValue: false },
|
|
74
|
+
...defaultOptions,
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
headerOptions["accept-encoding"] = [
|
|
78
|
+
{ value: "gzip", label: "gzip", hasValue: false },
|
|
79
|
+
{ value: "deflate", label: "deflate", hasValue: false },
|
|
80
|
+
{ value: "compress", label: "compress", hasValue: false },
|
|
81
|
+
{ value: "br", label: "br", hasValue: false },
|
|
82
|
+
{ value: "gzip, deflate", label: "gzip, deflate", hasValue: false },
|
|
83
|
+
{ value: "gzip, deflate, br", label: "gzip, deflate, br", hasValue: false },
|
|
84
|
+
...defaultOptions,
|
|
85
|
+
];
|
|
86
|
+
headerOptions["accept-language"] = [
|
|
87
|
+
{ value: "*", label: "*", hasValue: false },
|
|
88
|
+
{ value: "en-GB, en-US, en;q=0.9", label: "en-GB, en-US, en;q=0.9", hasValue: false },
|
|
89
|
+
{ value: "de-AT, de-DE;q=0.9, en;q=0.5", label: "de-AT, de-DE;q=0.9, en;q=0.5", hasValue: false },
|
|
90
|
+
{ value: "es-mx,es,en;q=0.5", label: "es-mx,es,en;q=0.5", hasValue: false },
|
|
91
|
+
{ value: "fr-CH, fr;q=0.9, en;q=0.8", label: "fr-CH, fr;q=0.9, en;q=0.8", hasValue: false },
|
|
92
|
+
{ value: "zh-CN, zh-TW; q = 0.9, zh-HK; q = 0.8, zh; q = 0.7, en; q = 0.6", label: "zh-CN, zh-TW; q = 0.9, zh-HK; q = 0.8, zh; q = 0.7, en; q = 0.6", hasValue: false },
|
|
93
|
+
{ value: "ja-JP, jp", label: "ja-JP, jp", hasValue: false },
|
|
94
|
+
...defaultOptions,
|
|
95
|
+
];
|
|
96
|
+
headerOptions["content-type"] = [
|
|
97
|
+
{ value: "text/css", label: "text/css", hasValue: false },
|
|
98
|
+
{ value: "text/plain", label: "text/plain", hasValue: false },
|
|
99
|
+
{ value: "text/html", label: "text/html", hasValue: false },
|
|
100
|
+
{ value: "application/json", label: "application/json", hasValue: false },
|
|
101
|
+
{ value: "application/octet-stream", label: "application/octet-stream", hasValue: false },
|
|
102
|
+
{ value: "application/pdf", label: "application/pdf", hasValue: false },
|
|
103
|
+
{ value: "application/xml", label: "application/xml", hasValue: false },
|
|
104
|
+
{ value: "application/zip", label: "application/zip", hasValue: false },
|
|
105
|
+
{ value: "multipart/form-data", label: "multipart/form-data", hasValue: false },
|
|
106
|
+
{ value: "audio/aac", label: "audio/aac", hasValue: false },
|
|
107
|
+
{ value: "audio/ac3", label: "audio/ac3", hasValue: false },
|
|
108
|
+
{ value: "audio/basic", label: "audio/basic", hasValue: false },
|
|
109
|
+
{ value: "audio/mp4", label: "audio/mp4", hasValue: false },
|
|
110
|
+
{ value: "audio/ogg", label: "audio/ogg", hasValue: false },
|
|
111
|
+
{ value: "image/bmp", label: "image/bmp", hasValue: false },
|
|
112
|
+
{ value: "image/gif", label: "image/gif", hasValue: false },
|
|
113
|
+
{ value: "image/jpeg", label: "image/jpeg", hasValue: false },
|
|
114
|
+
{ value: "image/png", label: "image/png", hasValue: false },
|
|
115
|
+
{ value: "image/tiff", label: "image/tiff", hasValue: false },
|
|
116
|
+
...defaultOptions,
|
|
117
|
+
];
|
|
118
|
+
headerOptions["cache-control"] = [
|
|
119
|
+
{ value: "max-age=0", label: "max-age=0", hasValue: false },
|
|
120
|
+
{ value: "max-age=86400", label: "max-age=86400", hasValue: false },
|
|
121
|
+
{ value: "no-cache", label: "no-cache", hasValue: false },
|
|
122
|
+
...defaultOptions,
|
|
123
|
+
];
|
|
124
|
+
*/
|
|
125
|
+
headerOptions["user-agent"] = [
|
|
126
|
+
{ value: "Mozilla/5.0", label: "Mozilla/5.0", hasValue: false },
|
|
127
|
+
...defaultOptions,
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
function getHeaderOptions(headerName) {
|
|
131
|
+
const lc = (headerName || "").toLowerCase();
|
|
132
|
+
let opts = headerOptions[lc];
|
|
133
|
+
return opts || defaultOptions;
|
|
134
|
+
}
|
|
135
|
+
|
|
43
136
|
function ws_oneditprepare() {
|
|
44
137
|
$("#websocket-client-row").hide();
|
|
45
138
|
$("#node-input-mode").on("change", function() {
|
|
@@ -192,7 +285,8 @@
|
|
|
192
285
|
value: "",
|
|
193
286
|
label:RED._("node-red:websocket.sendheartbeat"),
|
|
194
287
|
validate: RED.validators.number(/*blank allowed*/true) },
|
|
195
|
-
subprotocol: {value:"",required: false}
|
|
288
|
+
subprotocol: {value:"",required: false},
|
|
289
|
+
headers: { value: [] }
|
|
196
290
|
},
|
|
197
291
|
inputs:0,
|
|
198
292
|
outputs:0,
|
|
@@ -200,6 +294,9 @@
|
|
|
200
294
|
return this.path;
|
|
201
295
|
},
|
|
202
296
|
oneditprepare: function() {
|
|
297
|
+
|
|
298
|
+
const node = this;
|
|
299
|
+
|
|
203
300
|
$("#node-config-input-path").on("change keyup paste",function() {
|
|
204
301
|
$(".node-config-row-tls").toggle(/^wss:/i.test($(this).val()))
|
|
205
302
|
});
|
|
@@ -214,14 +311,114 @@
|
|
|
214
311
|
if (!heartbeatActive) {
|
|
215
312
|
$("#node-config-input-hb").val("");
|
|
216
313
|
}
|
|
314
|
+
|
|
315
|
+
const hasMatch = function (arr, value) {
|
|
316
|
+
return arr.some(function (ht) {
|
|
317
|
+
return ht.value === value
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const headerList = $("#node-input-headers-container").css('min-height', '150px').css('min-width', '450px').editableList({
|
|
322
|
+
addItem: function (container, i, header) {
|
|
323
|
+
const row = $('<div/>').css({
|
|
324
|
+
overflow: 'hidden',
|
|
325
|
+
whiteSpace: 'nowrap',
|
|
326
|
+
display: 'flex'
|
|
327
|
+
}).appendTo(container);
|
|
328
|
+
const propertNameCell = $('<div/>').css({ 'flex-grow': 1 }).appendTo(row);
|
|
329
|
+
const propertyName = $('<input/>', { class: "node-input-header-name", type: "text", style: "width: 100%" })
|
|
330
|
+
.appendTo(propertNameCell)
|
|
331
|
+
.typedInput({ types: headerTypes });
|
|
332
|
+
|
|
333
|
+
const propertyValueCell = $('<div/>').css({ 'flex-grow': 1, 'margin-left': '10px' }).appendTo(row);
|
|
334
|
+
const propertyValue = $('<input/>', { class: "node-input-header-value", type: "text", style: "width: 100%" })
|
|
335
|
+
.appendTo(propertyValueCell)
|
|
336
|
+
.typedInput({
|
|
337
|
+
types: getHeaderOptions(header.keyType)
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
const setup = function(_header) {
|
|
341
|
+
const headerTypeIsAPreset = function(h) {return hasMatch(headerTypes, h) };
|
|
342
|
+
const headerValueIsAPreset = function(h, v) {return hasMatch(getHeaderOptions(h), v) };
|
|
343
|
+
|
|
344
|
+
const {keyType, keyValue, valueType, valueValue} = header;
|
|
345
|
+
|
|
346
|
+
if(keyType == "other") {
|
|
347
|
+
propertyName.typedInput('type', keyType);
|
|
348
|
+
propertyName.typedInput('value', keyValue);
|
|
349
|
+
} else if (headerTypeIsAPreset(keyType)) {
|
|
350
|
+
propertyName.typedInput('type', keyType);
|
|
351
|
+
} else {
|
|
352
|
+
propertyName.typedInput('type', "other");
|
|
353
|
+
propertyName.typedInput('value', keyValue);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if(valueType == "other" || valueType == "env" ) {
|
|
357
|
+
propertyValue.typedInput('type', valueType);
|
|
358
|
+
propertyValue.typedInput('value', valueValue);
|
|
359
|
+
} else if (headerValueIsAPreset(propertyName.typedInput('type'), valueType)) {
|
|
360
|
+
propertyValue.typedInput('type', valueType);
|
|
361
|
+
} else {
|
|
362
|
+
propertyValue.typedInput('type', "other");
|
|
363
|
+
propertyValue.typedInput('value', valueValue);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
setup(header);
|
|
367
|
+
|
|
368
|
+
propertyName.on('change', function (event) {
|
|
369
|
+
propertyValue.typedInput('types', getHeaderOptions(propertyName.typedInput('type')));
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
},
|
|
373
|
+
sortable: true,
|
|
374
|
+
removable: true
|
|
375
|
+
});
|
|
376
|
+
if (node.headers) {
|
|
377
|
+
for (let index = 0; index < node.headers.length; index++) {
|
|
378
|
+
const element = node.headers[index];
|
|
379
|
+
headerList.editableList('addItem', node.headers[index]);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
217
382
|
},
|
|
218
383
|
oneditsave: function() {
|
|
384
|
+
|
|
385
|
+
const node = this;
|
|
386
|
+
|
|
219
387
|
if (!/^wss:/i.test($("#node-config-input-path").val())) {
|
|
220
388
|
$("#node-config-input-tls").val("_ADD_");
|
|
221
389
|
}
|
|
222
390
|
if (!$("#node-config-input-hb-cb").prop("checked")) {
|
|
223
391
|
$("#node-config-input-hb").val("0");
|
|
224
392
|
}
|
|
393
|
+
|
|
394
|
+
const headers = $("#node-input-headers-container").editableList('items');
|
|
395
|
+
|
|
396
|
+
node.headers = [];
|
|
397
|
+
headers.each(function(i) {
|
|
398
|
+
const header = $(this);
|
|
399
|
+
const keyType = header.find(".node-input-header-name").typedInput('type');
|
|
400
|
+
const keyValue = header.find(".node-input-header-name").typedInput('value');
|
|
401
|
+
const valueType = header.find(".node-input-header-value").typedInput('type');
|
|
402
|
+
const valueValue = header.find(".node-input-header-value").typedInput('value');
|
|
403
|
+
node.headers.push({
|
|
404
|
+
keyType, keyValue, valueType, valueValue
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
});
|
|
408
|
+
},
|
|
409
|
+
oneditresize: function(size) {
|
|
410
|
+
const dlg = $("#dialog-form");
|
|
411
|
+
const expandRow = dlg.find('.node-input-headers-container-row');
|
|
412
|
+
let height = dlg.height() - 5;
|
|
413
|
+
if(expandRow && expandRow.length){
|
|
414
|
+
const siblingRows = dlg.find('> .form-row:not(.node-input-headers-container-row)');
|
|
415
|
+
for (let i = 0; i < siblingRows.size(); i++) {
|
|
416
|
+
const cr = $(siblingRows[i]);
|
|
417
|
+
if(cr.is(":visible"))
|
|
418
|
+
height -= cr.outerHeight(true);
|
|
419
|
+
}
|
|
420
|
+
$("#node-input-headers-container").editableList('height',height);
|
|
421
|
+
}
|
|
225
422
|
}
|
|
226
423
|
});
|
|
227
424
|
|
|
@@ -299,8 +496,15 @@
|
|
|
299
496
|
<span data-i18n="inject.seconds"></span>
|
|
300
497
|
</span>
|
|
301
498
|
</div>
|
|
499
|
+
<div class="form-row" style="margin-bottom:0;">
|
|
500
|
+
<label><i class="fa fa-list"></i> <span data-i18n="httpin.label.headers"></span></label>
|
|
501
|
+
</div>
|
|
502
|
+
<div class="form-row node-input-headers-container-row">
|
|
503
|
+
<ol id="node-input-headers-container"></ol>
|
|
504
|
+
</div>
|
|
302
505
|
<div class="form-tips">
|
|
303
506
|
<p><span data-i18n="[html]websocket.tip.url1"></span></p>
|
|
304
|
-
<span data-i18n="[html]websocket.tip.url2"></span>
|
|
507
|
+
<p><span data-i18n="[html]websocket.tip.url2"></span></p>
|
|
508
|
+
<span data-i18n="[html]websocket.tip.headers"></span>
|
|
305
509
|
</div>
|
|
306
510
|
</script>
|
|
@@ -58,6 +58,7 @@ module.exports = function(RED) {
|
|
|
58
58
|
node.isServer = !/^ws{1,2}:\/\//i.test(node.path);
|
|
59
59
|
node.closing = false;
|
|
60
60
|
node.tls = n.tls;
|
|
61
|
+
node.upgradeHeaders = n.headers
|
|
61
62
|
|
|
62
63
|
if (n.hb) {
|
|
63
64
|
var heartbeat = parseInt(n.hb);
|
|
@@ -96,6 +97,42 @@ module.exports = function(RED) {
|
|
|
96
97
|
tlsNode.addTLSOptions(options);
|
|
97
98
|
}
|
|
98
99
|
}
|
|
100
|
+
|
|
101
|
+
// We need to check if undefined, to guard against previous installs, that will not have had this property set (applies to 3.1.x setups)
|
|
102
|
+
// Else this will be breaking potentially
|
|
103
|
+
if(node.upgradeHeaders !== undefined && node.upgradeHeaders.length > 0){
|
|
104
|
+
options.headers = {};
|
|
105
|
+
for(let i = 0;i<node.upgradeHeaders.length;i++){
|
|
106
|
+
const header = node.upgradeHeaders[i];
|
|
107
|
+
const keyType = header.keyType;
|
|
108
|
+
const keyValue = header.keyValue;
|
|
109
|
+
const valueType = header.valueType;
|
|
110
|
+
const valueValue = header.valueValue;
|
|
111
|
+
|
|
112
|
+
const headerName = keyType === 'other' ? keyValue : keyType;
|
|
113
|
+
let headerValue;
|
|
114
|
+
|
|
115
|
+
switch(valueType){
|
|
116
|
+
case 'other':
|
|
117
|
+
headerValue = valueValue;
|
|
118
|
+
break;
|
|
119
|
+
|
|
120
|
+
case 'env':
|
|
121
|
+
headerValue = RED.util.evaluateNodeProperty(valueValue,valueType,node);
|
|
122
|
+
break;
|
|
123
|
+
|
|
124
|
+
default:
|
|
125
|
+
headerValue = valueType;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if(headerName && headerValue){
|
|
130
|
+
options.headers[headerName] = headerValue
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
99
136
|
var socket = new ws(node.path,node.subprotocol,options);
|
|
100
137
|
socket.setMaxListeners(0);
|
|
101
138
|
node.server = socket; // keep for closing
|
package/core/network/31-tcpin.js
CHANGED
|
@@ -411,23 +411,33 @@ module.exports = function(RED) {
|
|
|
411
411
|
if (msg._session && msg._session.type == "tcp") {
|
|
412
412
|
var client = connectionPool[msg._session.id];
|
|
413
413
|
if (client) {
|
|
414
|
-
if (
|
|
415
|
-
client.
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
414
|
+
if (msg?.reset === true) {
|
|
415
|
+
client.destroy();
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
if (Buffer.isBuffer(msg.payload)) {
|
|
419
|
+
client.write(msg.payload);
|
|
420
|
+
} else if (typeof msg.payload === "string" && node.base64) {
|
|
421
|
+
client.write(Buffer.from(msg.payload,'base64'));
|
|
422
|
+
} else {
|
|
423
|
+
client.write(Buffer.from(""+msg.payload));
|
|
424
|
+
}
|
|
420
425
|
}
|
|
421
426
|
}
|
|
422
427
|
}
|
|
423
428
|
else {
|
|
424
429
|
for (var i in connectionPool) {
|
|
425
|
-
if (
|
|
426
|
-
connectionPool[i].
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
430
|
+
if (msg?.reset === true) {
|
|
431
|
+
connectionPool[i].destroy();
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
if (Buffer.isBuffer(msg.payload)) {
|
|
435
|
+
connectionPool[i].write(msg.payload);
|
|
436
|
+
} else if (typeof msg.payload === "string" && node.base64) {
|
|
437
|
+
connectionPool[i].write(Buffer.from(msg.payload,'base64'));
|
|
438
|
+
} else {
|
|
439
|
+
connectionPool[i].write(Buffer.from(""+msg.payload));
|
|
440
|
+
}
|
|
431
441
|
}
|
|
432
442
|
}
|
|
433
443
|
}
|
|
@@ -547,13 +557,34 @@ module.exports = function(RED) {
|
|
|
547
557
|
|
|
548
558
|
this.on("input", function(msg, nodeSend, nodeDone) {
|
|
549
559
|
var i = 0;
|
|
550
|
-
if ((!Buffer.isBuffer(msg.payload)) && (typeof msg.payload !== "string")) {
|
|
560
|
+
if (msg.payload !== undefined && (!Buffer.isBuffer(msg.payload)) && (typeof msg.payload !== "string")) {
|
|
551
561
|
msg.payload = msg.payload.toString();
|
|
552
562
|
}
|
|
553
563
|
|
|
554
564
|
var host = node.server || msg.host;
|
|
555
565
|
var port = node.port || msg.port;
|
|
556
566
|
|
|
567
|
+
if (node.out === "sit" && msg?.reset) {
|
|
568
|
+
if (msg.reset === true) { // kill all connections
|
|
569
|
+
for (var cl in clients) {
|
|
570
|
+
if (clients[cl].hasOwnProperty("client")) {
|
|
571
|
+
clients[cl].client.destroy();
|
|
572
|
+
delete clients[cl];
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (typeof(msg.reset) === "string" && msg.reset.includes(":")) { // just kill connection host:port
|
|
577
|
+
if (clients.hasOwnProperty(msg.reset) && clients[msg.reset].hasOwnProperty("client")) {
|
|
578
|
+
clients[msg.reset].client.destroy();
|
|
579
|
+
delete clients[msg.reset];
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
const cc = Object.keys(clients).length;
|
|
583
|
+
node.status({fill:"green",shape:cc===0?"ring":"dot",text:RED._("tcpin.status.connections",{count:cc})});
|
|
584
|
+
if ((host === undefined || port === undefined) && !msg.hasOwnProperty("payload")) { return; }
|
|
585
|
+
if (!msg.hasOwnProperty("payload")) { return; }
|
|
586
|
+
}
|
|
587
|
+
|
|
557
588
|
// Store client information independently
|
|
558
589
|
// the clients object will have:
|
|
559
590
|
// clients[id].client, clients[id].msg, clients[id].timeout
|
|
@@ -621,13 +652,16 @@ module.exports = function(RED) {
|
|
|
621
652
|
clients[connection_id].connecting = true;
|
|
622
653
|
clients[connection_id].client.connect(connOpts, function() {
|
|
623
654
|
//node.log(RED._("tcpin.errors.client-connected"));
|
|
624
|
-
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
|
655
|
+
// node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
|
656
|
+
node.status({fill:"green",shape:"dot",text:RED._("tcpin.status.connections",{count:Object.keys(clients).length})});
|
|
625
657
|
if (clients[connection_id] && clients[connection_id].client) {
|
|
626
658
|
clients[connection_id].connected = true;
|
|
627
659
|
clients[connection_id].connecting = false;
|
|
628
660
|
let event;
|
|
629
661
|
while (event = dequeue(clients[connection_id].msgQueue)) {
|
|
630
|
-
|
|
662
|
+
if (event.msg.payload !== undefined) {
|
|
663
|
+
clients[connection_id].client.write(event.msg.payload);
|
|
664
|
+
}
|
|
631
665
|
event.nodeDone();
|
|
632
666
|
}
|
|
633
667
|
if (node.out === "time" && node.splitc < 0) {
|
|
@@ -823,7 +857,9 @@ module.exports = function(RED) {
|
|
|
823
857
|
else if (!clients[connection_id].connecting && clients[connection_id].connected) {
|
|
824
858
|
if (clients[connection_id] && clients[connection_id].client) {
|
|
825
859
|
let event = dequeue(clients[connection_id].msgQueue)
|
|
826
|
-
|
|
860
|
+
if (event.msg.payload !== undefined ) {
|
|
861
|
+
clients[connection_id].client.write(event.msg.payload);
|
|
862
|
+
}
|
|
827
863
|
event.nodeDone();
|
|
828
864
|
}
|
|
829
865
|
}
|
package/core/parsers/70-CSV.html
CHANGED
|
@@ -17,7 +17,20 @@
|
|
|
17
17
|
</select>
|
|
18
18
|
<input style="width:40px;" type="text" id="node-input-sep" pattern=".">
|
|
19
19
|
</div>
|
|
20
|
-
|
|
20
|
+
<div class="form-row">
|
|
21
|
+
<label><i class="fa fa-code"></i> <span data-i18n="csv.label.spec"></span></label>
|
|
22
|
+
<div style="display: inline-grid;width: 70%;">
|
|
23
|
+
<select style="width:100%" id="csv-option-spec">
|
|
24
|
+
<option value="rfc" data-i18n="csv.spec.rfc"></option>
|
|
25
|
+
<option value="" data-i18n="csv.spec.legacy"></option>
|
|
26
|
+
</select>
|
|
27
|
+
<div>
|
|
28
|
+
<div class="form-tips csv-lecacy-warning" data-i18n="node-red:csv.spec.legacy_warning"
|
|
29
|
+
style="width: calc(100% - 18px); margin-top: 4px; max-width: unset;">
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
21
34
|
<div class="form-row">
|
|
22
35
|
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
|
23
36
|
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
|
@@ -60,10 +73,10 @@
|
|
|
60
73
|
<div class="form-row" style="padding-left:20px;">
|
|
61
74
|
<label></label>
|
|
62
75
|
<label style="width:auto; margin-right:10px;" for="node-input-ret"><span data-i18n="csv.label.newline"></span></label>
|
|
63
|
-
<select style="width:
|
|
76
|
+
<select style="width:calc(70% - 108px);" id="node-input-ret">
|
|
77
|
+
<option value='\r\n' data-i18n="csv.newline.windows"></option>
|
|
64
78
|
<option value='\n' data-i18n="csv.newline.linux"></option>
|
|
65
79
|
<option value='\r' data-i18n="csv.newline.mac"></option>
|
|
66
|
-
<option value='\r\n' data-i18n="csv.newline.windows"></option>
|
|
67
80
|
</select>
|
|
68
81
|
</div>
|
|
69
82
|
</script>
|
|
@@ -75,6 +88,7 @@
|
|
|
75
88
|
color:"#DEBD5C",
|
|
76
89
|
defaults: {
|
|
77
90
|
name: {value:""},
|
|
91
|
+
spec: {value:"rfc"},
|
|
78
92
|
sep: {
|
|
79
93
|
value:',', required:true,
|
|
80
94
|
label:RED._("node-red:csv.label.separator"),
|
|
@@ -83,7 +97,7 @@
|
|
|
83
97
|
hdrin: {value:""},
|
|
84
98
|
hdrout: {value:"none"},
|
|
85
99
|
multi: {value:"one",required:true},
|
|
86
|
-
ret: {value:'\\n'},
|
|
100
|
+
ret: {value:'\\r\\n'}, // default to CRLF (RFC4180 Sec 2.1: "Each record is located on a separate line, delimited by a line break (CRLF)")
|
|
87
101
|
temp: {value:""},
|
|
88
102
|
skip: {value:"0"},
|
|
89
103
|
strings: {value:true},
|
|
@@ -123,6 +137,27 @@
|
|
|
123
137
|
$("#node-input-sep").hide();
|
|
124
138
|
}
|
|
125
139
|
});
|
|
140
|
+
|
|
141
|
+
$("#csv-option-spec").on("change", function() {
|
|
142
|
+
if ($("#csv-option-spec").val() == "rfc") {
|
|
143
|
+
$(".form-tips.csv-lecacy-warning").hide();
|
|
144
|
+
} else {
|
|
145
|
+
$(".form-tips.csv-lecacy-warning").show();
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
// new nodes will have `spec` set to "rfc" (default), but existing nodes will either not have
|
|
149
|
+
// a spec value or it will be empty - we need to maintain the legacy behaviour for existing
|
|
150
|
+
// flows but default to rfc for new nodes
|
|
151
|
+
let spec = !this.spec ? "" : "rfc"
|
|
152
|
+
$("#csv-option-spec").val(spec).trigger("change")
|
|
153
|
+
},
|
|
154
|
+
oneditsave: function() {
|
|
155
|
+
const specFormVal = $("#csv-option-spec").val() || '' // empty === legacy
|
|
156
|
+
const spectNodeVal = this.spec || '' // empty === legacy, null/undefined means in-place node upgrade (keep as is)
|
|
157
|
+
if (specFormVal !== spectNodeVal) {
|
|
158
|
+
// only update the flow value if changed (avoid marking the node dirty unnecessarily)
|
|
159
|
+
this.spec = specFormVal
|
|
160
|
+
}
|
|
126
161
|
}
|
|
127
162
|
});
|
|
128
163
|
</script>
|