@limetech/lime-elements 40.5.2 → 40.5.4
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/CHANGELOG.md +14 -0
- package/dist/cjs/{_baseEach-Bygi-un6.js → _baseForOwn-8jMPwf3X.js} +1 -43
- package/dist/cjs/{_baseIsEqual-CQZKiKbe.js → _baseIsEqual-zzV9DKMv.js} +0 -1
- package/dist/cjs/{_baseIteratee-DDklwtn9.js → _baseIteratee-BIMRm4nq.js} +1 -3
- package/dist/cjs/_baseSet-DykMx7uF.js +92 -0
- package/dist/cjs/_getPrototype-CCLHkBHj.js +8 -0
- package/dist/cjs/{cloneDeep-hE40Q8aw.js → cloneDeep-D2immmwT.js} +16 -160
- package/dist/cjs/{difference-B543MGsY.js → isArrayLikeObject-U2Ddbw4n.js} +5 -93
- package/dist/cjs/{isEqual-Cd9J8a5-.js → isEqual-D3eFkhqd.js} +1 -1
- package/dist/cjs/limel-action-bar_3.cjs.entry.js +1 -1
- package/dist/cjs/limel-chip-set.cjs.entry.js +2 -2
- package/dist/cjs/limel-dialog.cjs.entry.js +2 -2
- package/dist/cjs/limel-file-dropzone_2.cjs.entry.js +45 -6
- package/dist/cjs/limel-file-viewer.cjs.entry.js +510 -70
- package/dist/cjs/limel-flatpickr-adapter.cjs.entry.js +73 -11
- package/dist/cjs/limel-form.cjs.entry.js +1857 -1919
- package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js +326 -262
- package/dist/cjs/limel-tab-bar.cjs.entry.js +83 -4
- package/dist/cjs/limel-table.cjs.entry.js +598 -199
- package/dist/collection/components/action-bar/action-bar-item/action-bar-item.css +1 -1
- package/dist/collection/components/form/fields/schema-field.js +8 -0
- package/dist/collection/components/form/form.js +1 -0
- package/dist/collection/components/form/form.test-schemas.js +47 -0
- package/dist/collection/components/form/templates/array-field-collapsible-item.js +38 -11
- package/dist/collection/components/table/table.css +1 -2
- package/dist/esm/{_baseEach-lvj-g1Zj.js → _baseForOwn-D5j7fHlv.js} +2 -42
- package/dist/esm/{_baseIsEqual-B6bIeexY.js → _baseIsEqual-CyF4P8uM.js} +2 -2
- package/dist/esm/{_baseIteratee-CEZRuq23.js → _baseIteratee-CpbczHY1.js} +3 -3
- package/dist/esm/_baseSet-Cw2IE6ZF.js +89 -0
- package/dist/esm/_getPrototype-B0kMNv1F.js +6 -0
- package/dist/esm/{_getTag-CMJL-6hk.js → _getTag-BiAaLWvH.js} +1 -1
- package/dist/esm/{cloneDeep-CL7UD3mR.js → cloneDeep-DAbOtbs8.js} +18 -154
- package/dist/esm/{difference-DND5tWiA.js → isArrayLikeObject-CJB8HbtH.js} +7 -91
- package/dist/esm/{isEmpty-DHgukT5k.js → isEmpty-BGVZ12Ea.js} +1 -1
- package/dist/esm/{isEqual-4tbMaaf_.js → isEqual-CE83yS8T.js} +1 -1
- package/dist/esm/limel-action-bar_3.entry.js +1 -1
- package/dist/esm/limel-chip-set.entry.js +3 -3
- package/dist/esm/limel-chip_2.entry.js +2 -2
- package/dist/esm/limel-dialog.entry.js +3 -3
- package/dist/esm/limel-file-dropzone_2.entry.js +45 -6
- package/dist/esm/limel-file-viewer.entry.js +510 -70
- package/dist/esm/limel-flatpickr-adapter.entry.js +69 -7
- package/dist/esm/limel-form.entry.js +1854 -1916
- package/dist/esm/limel-prosemirror-adapter.entry.js +327 -263
- package/dist/esm/limel-tab-bar.entry.js +83 -4
- package/dist/esm/limel-table.entry.js +595 -196
- package/dist/lime-elements/lime-elements.esm.js +1 -1
- package/dist/lime-elements/p-05afb316.entry.js +1 -0
- package/dist/lime-elements/p-0fba64eb.entry.js +16 -0
- package/dist/lime-elements/p-10594468.entry.js +1 -0
- package/dist/lime-elements/p-19344984.entry.js +13 -0
- package/dist/lime-elements/p-26c5c6b9.entry.js +1 -0
- package/dist/lime-elements/{p-11a96a8c.entry.js → p-36ce4fbc.entry.js} +1 -1
- package/dist/lime-elements/{p-955292eb.entry.js → p-4aae63bd.entry.js} +1 -1
- package/dist/lime-elements/{p-b8c76487.entry.js → p-5d974afa.entry.js} +1 -1
- package/dist/lime-elements/p-B6-3WXAv.js +1 -0
- package/dist/lime-elements/p-BQeIv4aA.js +1 -0
- package/dist/lime-elements/p-BY5NjPj8.js +1 -0
- package/dist/lime-elements/p-COdirXg1.js +1 -0
- package/dist/lime-elements/p-CW2-jhzP.js +1 -0
- package/dist/lime-elements/p-D2VFSWDU.js +1 -0
- package/dist/lime-elements/{p-D4RdItCq.js → p-DK0pjX-Y.js} +1 -1
- package/dist/lime-elements/p-ORUNaKm-.js +1 -0
- package/dist/lime-elements/{p-5b371d6b.entry.js → p-a71f6e87.entry.js} +4 -4
- package/dist/lime-elements/p-cef66e53.entry.js +1 -0
- package/dist/lime-elements/{p-02812dc8.entry.js → p-d3bd4b80.entry.js} +1 -1
- package/dist/lime-elements/p-nVemxylR.js +1 -0
- package/dist/lime-elements/p-pk5KIdKH.js +1 -0
- package/dist/types/components/form/form.test-schemas.d.ts +3 -0
- package/dist/types/components/form/templates/array-field-collapsible-item.d.ts +0 -1
- package/package.json +11 -11
- package/dist/cjs/_getAllKeysIn-BJsgUTAt.js +0 -119
- package/dist/cjs/_isIterateeCall-BUGt9SrH.js +0 -73
- package/dist/cjs/pickBy-D-X6S5kg.js +0 -201
- package/dist/esm/_getAllKeysIn-kT3KizYN.js +0 -114
- package/dist/esm/_isIterateeCall-Bb4gUZLK.js +0 -70
- package/dist/esm/pickBy-C4C3CM4m.js +0 -195
- package/dist/lime-elements/p-1e085bbe.entry.js +0 -13
- package/dist/lime-elements/p-56bkn1D-.js +0 -1
- package/dist/lime-elements/p-67f02b69.entry.js +0 -1
- package/dist/lime-elements/p-867681aa.entry.js +0 -1
- package/dist/lime-elements/p-9d3437ac.entry.js +0 -16
- package/dist/lime-elements/p-B8yl2MCT.js +0 -1
- package/dist/lime-elements/p-BAR6am-7.js +0 -1
- package/dist/lime-elements/p-BE1Efiuj.js +0 -1
- package/dist/lime-elements/p-BsX7ma3n.js +0 -1
- package/dist/lime-elements/p-CR7u-m0b.js +0 -1
- package/dist/lime-elements/p-CVzgpz9P.js +0 -1
- package/dist/lime-elements/p-D8H5afYg.js +0 -1
- package/dist/lime-elements/p-DtrvybFR.js +0 -1
- package/dist/lime-elements/p-f309ceb8.entry.js +0 -1
- package/dist/lime-elements/p-f3c108c9.entry.js +0 -1
- package/dist/lime-elements/p-mmkVLOjW.js +0 -1
|
@@ -238,12 +238,129 @@ function normalizeCharset(charset) {
|
|
|
238
238
|
return charset.replace(/^(?:x-ms-|x-|cs)/, '').replace(/[\s._-]+/g, '');
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
+
// Every label of the WHATWG Encoding Standard, https://encoding.spec.whatwg.org/encodings.json
|
|
242
|
+
//
|
|
243
|
+
// TextDecoder accepts these and nothing else, so a label outside the list is turned away
|
|
244
|
+
// without constructing a decoder for it. Letting the constructor throw instead costs
|
|
245
|
+
// several times the decode itself, and a message controls how often it happens: once
|
|
246
|
+
// per encoded word, twice when the label also fails after normalization.
|
|
247
|
+
const ENCODING_LABELS = new Set(
|
|
248
|
+
[
|
|
249
|
+
// UTF-8
|
|
250
|
+
'unicode-1-1-utf-8 unicode11utf8 unicode20utf8 utf-8 utf8 x-unicode20utf8',
|
|
251
|
+
// IBM866
|
|
252
|
+
'866 cp866 csibm866 ibm866',
|
|
253
|
+
// ISO-8859-2
|
|
254
|
+
'csisolatin2 iso-8859-2 iso-ir-101 iso8859-2 iso88592 iso_8859-2 iso_8859-2:1987 l2 latin2',
|
|
255
|
+
// ISO-8859-3
|
|
256
|
+
'csisolatin3 iso-8859-3 iso-ir-109 iso8859-3 iso88593 iso_8859-3 iso_8859-3:1988 l3 latin3',
|
|
257
|
+
// ISO-8859-4
|
|
258
|
+
'csisolatin4 iso-8859-4 iso-ir-110 iso8859-4 iso88594 iso_8859-4 iso_8859-4:1988 l4 latin4',
|
|
259
|
+
// ISO-8859-5
|
|
260
|
+
'csisolatincyrillic cyrillic iso-8859-5 iso-ir-144 iso8859-5 iso88595 iso_8859-5 iso_8859-5:1988',
|
|
261
|
+
// ISO-8859-6
|
|
262
|
+
'arabic asmo-708 csiso88596e csiso88596i csisolatinarabic ecma-114 iso-8859-6 iso-8859-6-e',
|
|
263
|
+
'iso-8859-6-i iso-ir-127 iso8859-6 iso88596 iso_8859-6 iso_8859-6:1987',
|
|
264
|
+
// ISO-8859-7
|
|
265
|
+
'csisolatingreek ecma-118 elot_928 greek greek8 iso-8859-7 iso-ir-126 iso8859-7 iso88597 iso_8859-7',
|
|
266
|
+
'iso_8859-7:1987 sun_eu_greek',
|
|
267
|
+
// ISO-8859-8
|
|
268
|
+
'csiso88598e csisolatinhebrew hebrew iso-8859-8 iso-8859-8-e iso-ir-138 iso8859-8 iso88598 iso_8859-8',
|
|
269
|
+
'iso_8859-8:1988 visual',
|
|
270
|
+
// ISO-8859-8-I
|
|
271
|
+
'csiso88598i iso-8859-8-i logical',
|
|
272
|
+
// ISO-8859-10
|
|
273
|
+
'csisolatin6 iso-8859-10 iso-ir-157 iso8859-10 iso885910 l6 latin6',
|
|
274
|
+
// ISO-8859-13
|
|
275
|
+
'iso-8859-13 iso8859-13 iso885913',
|
|
276
|
+
// ISO-8859-14
|
|
277
|
+
'iso-8859-14 iso8859-14 iso885914',
|
|
278
|
+
// ISO-8859-15
|
|
279
|
+
'csisolatin9 iso-8859-15 iso8859-15 iso885915 iso_8859-15 l9',
|
|
280
|
+
// ISO-8859-16
|
|
281
|
+
'iso-8859-16',
|
|
282
|
+
// KOI8-R
|
|
283
|
+
'cskoi8r koi koi8 koi8-r koi8_r',
|
|
284
|
+
// KOI8-U
|
|
285
|
+
'koi8-ru koi8-u',
|
|
286
|
+
// macintosh
|
|
287
|
+
'csmacintosh mac macintosh x-mac-roman',
|
|
288
|
+
// windows-874
|
|
289
|
+
'dos-874 iso-8859-11 iso8859-11 iso885911 tis-620 windows-874',
|
|
290
|
+
// windows-1250
|
|
291
|
+
'cp1250 windows-1250 x-cp1250',
|
|
292
|
+
// windows-1251
|
|
293
|
+
'cp1251 windows-1251 x-cp1251',
|
|
294
|
+
// windows-1252
|
|
295
|
+
'ansi_x3.4-1968 ascii cp1252 cp819 csisolatin1 ibm819 iso-8859-1 iso-ir-100 iso8859-1 iso88591',
|
|
296
|
+
'iso_8859-1 iso_8859-1:1987 l1 latin1 us-ascii windows-1252 x-cp1252',
|
|
297
|
+
// windows-1253
|
|
298
|
+
'cp1253 windows-1253 x-cp1253',
|
|
299
|
+
// windows-1254
|
|
300
|
+
'cp1254 csisolatin5 iso-8859-9 iso-ir-148 iso8859-9 iso88599 iso_8859-9 iso_8859-9:1989 l5 latin5',
|
|
301
|
+
'windows-1254 x-cp1254',
|
|
302
|
+
// windows-1255
|
|
303
|
+
'cp1255 windows-1255 x-cp1255',
|
|
304
|
+
// windows-1256
|
|
305
|
+
'cp1256 windows-1256 x-cp1256',
|
|
306
|
+
// windows-1257
|
|
307
|
+
'cp1257 windows-1257 x-cp1257',
|
|
308
|
+
// windows-1258
|
|
309
|
+
'cp1258 windows-1258 x-cp1258',
|
|
310
|
+
// x-mac-cyrillic
|
|
311
|
+
'x-mac-cyrillic x-mac-ukrainian',
|
|
312
|
+
// GBK
|
|
313
|
+
'chinese csgb2312 csiso58gb231280 gb2312 gb_2312 gb_2312-80 gbk iso-ir-58 x-gbk',
|
|
314
|
+
// gb18030
|
|
315
|
+
'gb18030',
|
|
316
|
+
// Big5
|
|
317
|
+
'big5 big5-hkscs cn-big5 csbig5 x-x-big5',
|
|
318
|
+
// EUC-JP
|
|
319
|
+
'cseucpkdfmtjapanese euc-jp x-euc-jp',
|
|
320
|
+
// ISO-2022-JP
|
|
321
|
+
'csiso2022jp iso-2022-jp',
|
|
322
|
+
// Shift_JIS
|
|
323
|
+
'csshiftjis ms932 ms_kanji shift-jis shift_jis sjis windows-31j x-sjis',
|
|
324
|
+
// EUC-KR
|
|
325
|
+
'cseuckr csksc56011987 euc-kr iso-ir-149 korean ks_c_5601-1987 ks_c_5601-1989 ksc5601 ksc_5601',
|
|
326
|
+
'windows-949',
|
|
327
|
+
// replacement
|
|
328
|
+
'csiso2022kr hz-gb-2312 iso-2022-cn iso-2022-cn-ext iso-2022-kr replacement',
|
|
329
|
+
// UTF-16BE
|
|
330
|
+
'unicodefffe utf-16be',
|
|
331
|
+
// UTF-16LE
|
|
332
|
+
'csunicode iso-10646-ucs-2 ucs-2 unicode unicodefeff utf-16 utf-16le',
|
|
333
|
+
// x-user-defined
|
|
334
|
+
'x-user-defined'
|
|
335
|
+
]
|
|
336
|
+
.join(' ')
|
|
337
|
+
.split(' ')
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
// Decoders by label, or null where the runtime refuses the label (the replacement
|
|
341
|
+
// encoding, or an encoding a runtime was built without). Only labels from the list above
|
|
342
|
+
// are stored, so the map can not grow past it however many labels a message makes up.
|
|
343
|
+
//
|
|
344
|
+
// Sharing a decoder is safe because nothing decodes with `stream: true`, so every
|
|
345
|
+
// decode() call starts from a fresh state.
|
|
346
|
+
const decoders = new Map();
|
|
347
|
+
|
|
241
348
|
function tryDecoder(charset) {
|
|
242
|
-
|
|
243
|
-
return new TextDecoder(charset);
|
|
244
|
-
} catch (err) {
|
|
349
|
+
if (!ENCODING_LABELS.has(charset)) {
|
|
245
350
|
return null;
|
|
246
351
|
}
|
|
352
|
+
|
|
353
|
+
if (!decoders.has(charset)) {
|
|
354
|
+
let decoder = null;
|
|
355
|
+
try {
|
|
356
|
+
decoder = new TextDecoder(charset);
|
|
357
|
+
} catch (err) {
|
|
358
|
+
// not supported by this runtime
|
|
359
|
+
}
|
|
360
|
+
decoders.set(charset, decoder);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return decoders.get(charset);
|
|
247
364
|
}
|
|
248
365
|
|
|
249
366
|
function getDecoder(charset) {
|
|
@@ -263,7 +380,7 @@ function getDecoder(charset) {
|
|
|
263
380
|
// differed from a supported label only by a prefix or a separator, eg. x-big5.
|
|
264
381
|
const alias = (codePage && codePageAliases.get(codePage[1])) || charsetAliases.get(normalized) || normalized;
|
|
265
382
|
|
|
266
|
-
return tryDecoder(alias) ||
|
|
383
|
+
return tryDecoder(alias) || tryDecoder('windows-1252');
|
|
267
384
|
}
|
|
268
385
|
|
|
269
386
|
/**
|
|
@@ -767,7 +884,22 @@ const headerDecoder = new TextDecoder('utf-8', { ignoreBOM: true });
|
|
|
767
884
|
// `from` header, and since the first occurrence of a header wins it outranked the real
|
|
768
885
|
// sender. Leaving the character in the key keeps the line visible without letting it
|
|
769
886
|
// collide with a genuine header.
|
|
770
|
-
|
|
887
|
+
//
|
|
888
|
+
// An index scan rather than `/^[ \t]+|[ \t]+$/g`, which retries the trailing branch at
|
|
889
|
+
// every position of a blank run that is followed by other text, so a single header with
|
|
890
|
+
// a long run of spaces in the middle took seconds to trim.
|
|
891
|
+
const isWsp = c => c === 0x20 || c === 0x09;
|
|
892
|
+
const trimWsp = str => {
|
|
893
|
+
let start = 0;
|
|
894
|
+
let end = str.length;
|
|
895
|
+
while (start < end && isWsp(str.charCodeAt(start))) {
|
|
896
|
+
start++;
|
|
897
|
+
}
|
|
898
|
+
while (end > start && isWsp(str.charCodeAt(end - 1))) {
|
|
899
|
+
end--;
|
|
900
|
+
}
|
|
901
|
+
return str.slice(start, end);
|
|
902
|
+
};
|
|
771
903
|
|
|
772
904
|
// Headers that decide how this part's body is read, see processHeaders
|
|
773
905
|
const CONTENT_HEADERS = new Set([
|
|
@@ -894,17 +1026,26 @@ class MimeNode {
|
|
|
894
1026
|
// A parameter value starts at `=` and ends at the `;` that begins the next one
|
|
895
1027
|
let inParameterValue = false;
|
|
896
1028
|
|
|
1029
|
+
// Whether result ends in SP or HTAB. Kept up to date on every append, because
|
|
1030
|
+
// testing `/[ \t]$/` against result flattens the whole string on each `(` and is
|
|
1031
|
+
// quadratic in the length of the header.
|
|
1032
|
+
let endsWithWsp = false;
|
|
1033
|
+
const append = c => {
|
|
1034
|
+
result += c;
|
|
1035
|
+
endsWithWsp = c === ' ' || c === '\t';
|
|
1036
|
+
};
|
|
1037
|
+
|
|
897
1038
|
// A comment may only appear where linear whitespace is allowed, so inside a
|
|
898
1039
|
// parameter value the parenthesis has to follow whitespace to open one. Outside
|
|
899
1040
|
// one, eg. after the type itself, anything goes.
|
|
900
|
-
const opensComment = () => !inParameterValue || !result.length ||
|
|
1041
|
+
const opensComment = () => !inParameterValue || !result.length || endsWithWsp;
|
|
901
1042
|
|
|
902
1043
|
for (let i = 0; i < str.length; i++) {
|
|
903
1044
|
const chr = str.charAt(i);
|
|
904
1045
|
|
|
905
1046
|
if (escaped) {
|
|
906
1047
|
if (depth === 0) {
|
|
907
|
-
|
|
1048
|
+
append(chr);
|
|
908
1049
|
}
|
|
909
1050
|
escaped = false;
|
|
910
1051
|
continue;
|
|
@@ -913,14 +1054,14 @@ class MimeNode {
|
|
|
913
1054
|
if (chr === '\\') {
|
|
914
1055
|
escaped = true;
|
|
915
1056
|
if (depth === 0) {
|
|
916
|
-
|
|
1057
|
+
append(chr);
|
|
917
1058
|
}
|
|
918
1059
|
continue;
|
|
919
1060
|
}
|
|
920
1061
|
|
|
921
1062
|
if (chr === '"' && depth === 0) {
|
|
922
1063
|
inQuote = !inQuote;
|
|
923
|
-
|
|
1064
|
+
append(chr);
|
|
924
1065
|
continue;
|
|
925
1066
|
}
|
|
926
1067
|
|
|
@@ -946,7 +1087,7 @@ class MimeNode {
|
|
|
946
1087
|
}
|
|
947
1088
|
|
|
948
1089
|
if (depth === 0) {
|
|
949
|
-
|
|
1090
|
+
append(chr);
|
|
950
1091
|
}
|
|
951
1092
|
}
|
|
952
1093
|
|
|
@@ -1103,30 +1244,61 @@ class MimeNode {
|
|
|
1103
1244
|
}
|
|
1104
1245
|
|
|
1105
1246
|
decodeFlowedText(str, delSp) {
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1247
|
+
// Pieces of the result, joined once at the end. Growing a single string and
|
|
1248
|
+
// calling endsWith() on it for every line flattens the whole paragraph again on
|
|
1249
|
+
// each line, which is quadratic in the length of a paragraph.
|
|
1250
|
+
// Empty pieces are never stored, so the last piece always holds the last
|
|
1251
|
+
// character of the result.
|
|
1252
|
+
const parts = [];
|
|
1253
|
+
// The unfolded line being built, ie. everything after the last hard line break,
|
|
1254
|
+
// starts at this index of parts and is this many characters long
|
|
1255
|
+
let lineStart = 0;
|
|
1256
|
+
let lineLength = 0;
|
|
1257
|
+
|
|
1258
|
+
const lines = str.split(/\r?\n/);
|
|
1259
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1260
|
+
let line = lines[i];
|
|
1261
|
+
|
|
1262
|
+
// remove whitespace stuffing before anything else
|
|
1263
|
+
// http://tools.ietf.org/html/rfc3676#section-4.4
|
|
1264
|
+
// doing it after the join leaves the stuffed space of a continuation line
|
|
1265
|
+
// sitting in the middle of the joined paragraph
|
|
1266
|
+
if (line.charAt(0) === ' ') {
|
|
1267
|
+
line = line.slice(1);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
if (i) {
|
|
1271
|
+
const last = parts.length ? parts[parts.length - 1] : '';
|
|
1272
|
+
|
|
1273
|
+
// soft linebreaks are added after space symbols, except for the signature
|
|
1274
|
+
// separator which is a line of its own
|
|
1275
|
+
const isSignature = lineLength === 3 && parts.slice(lineStart).join('') === '-- ';
|
|
1276
|
+
|
|
1277
|
+
if (last.endsWith(' ') && !isSignature) {
|
|
1278
|
+
if (delSp) {
|
|
1279
|
+
// delsp adds space to text to be able to fold it
|
|
1280
|
+
// these spaces can be removed once the text is unfolded
|
|
1281
|
+
if (last.length > 1) {
|
|
1282
|
+
parts[parts.length - 1] = last.slice(0, -1);
|
|
1122
1283
|
} else {
|
|
1123
|
-
|
|
1284
|
+
parts.pop();
|
|
1124
1285
|
}
|
|
1125
|
-
|
|
1126
|
-
return previousValue + '\n' + currentValue;
|
|
1286
|
+
lineLength--;
|
|
1127
1287
|
}
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1288
|
+
} else {
|
|
1289
|
+
parts.push('\n');
|
|
1290
|
+
lineStart = parts.length;
|
|
1291
|
+
lineLength = 0;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
if (line) {
|
|
1296
|
+
parts.push(line);
|
|
1297
|
+
lineLength += line.length;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
return parts.join('');
|
|
1130
1302
|
}
|
|
1131
1303
|
|
|
1132
1304
|
getTextContent() {
|
|
@@ -3567,31 +3739,251 @@ function textToHtml(str) {
|
|
|
3567
3739
|
return '<div>' + html + '</div>';
|
|
3568
3740
|
}
|
|
3569
3741
|
|
|
3742
|
+
// htmlToText strips tags with regexes like `<br\b[^>]*>` and `<!--.*?-->`. Handed to
|
|
3743
|
+
// String#replace as they are, the engine tries every candidate in turn and a candidate
|
|
3744
|
+
// that does not close scans on to the next `>`, the end of the line or the end of the
|
|
3745
|
+
// input before giving up, so an html part holding many unclosed `<`, `<a ` or `<!--`
|
|
3746
|
+
// took seconds to minutes to convert. The helpers below give the same results in linear
|
|
3747
|
+
// time.
|
|
3748
|
+
|
|
3749
|
+
// Characters that `.` does not match, so a `.*` can not run across them
|
|
3750
|
+
const LINE_TERMINATOR = /[\n\r\u2028\u2029]/g;
|
|
3751
|
+
|
|
3752
|
+
function lastLineTerminator(str) {
|
|
3753
|
+
return Math.max(str.lastIndexOf('\n'), str.lastIndexOf('\r'), str.lastIndexOf('\u2028'), str.lastIndexOf('\u2029'));
|
|
3754
|
+
}
|
|
3755
|
+
|
|
3756
|
+
/**
|
|
3757
|
+
* Returns a function that finds the next match of a global regex at or after a position.
|
|
3758
|
+
*
|
|
3759
|
+
* Callers only ever ask about positions that move forward, so a match stays the answer
|
|
3760
|
+
* until a position passes it, and no match stays the answer for good. That way every part
|
|
3761
|
+
* of the string is scanned once, however many times it is asked about.
|
|
3762
|
+
*
|
|
3763
|
+
* @param {String} str String to search
|
|
3764
|
+
* @param {RegExp} regex Global regex to look for
|
|
3765
|
+
* @return {Function} `pos => match` of the next match, or null if there is none
|
|
3766
|
+
*/
|
|
3767
|
+
function createFinder(str, regex) {
|
|
3768
|
+
let searchedFrom = Infinity;
|
|
3769
|
+
let found = null;
|
|
3770
|
+
|
|
3771
|
+
return pos => {
|
|
3772
|
+
if (pos < searchedFrom || (found && pos > found.index)) {
|
|
3773
|
+
regex.lastIndex = pos;
|
|
3774
|
+
found = regex.exec(str);
|
|
3775
|
+
searchedFrom = pos;
|
|
3776
|
+
}
|
|
3777
|
+
return found;
|
|
3778
|
+
};
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3781
|
+
/**
|
|
3782
|
+
* `str.replace(pattern, replacement)` for a pattern shaped like `<tag\b[^>]*>`.
|
|
3783
|
+
*
|
|
3784
|
+
* Such a pattern only fails at a candidate that has no `>` after it, so the text after
|
|
3785
|
+
* the last `>` can never match and is where every rescan happened. Leaving it out keeps
|
|
3786
|
+
* the native replace, and its speed, for everything else.
|
|
3787
|
+
*
|
|
3788
|
+
* @param {String} str String to process
|
|
3789
|
+
* @param {RegExp} pattern Global regex that ends on the first `>` after its start
|
|
3790
|
+
* @param {String} replacement Replacement text
|
|
3791
|
+
* @return {String} Processed string
|
|
3792
|
+
*/
|
|
3793
|
+
function replaceClosedTags(str, pattern, replacement) {
|
|
3794
|
+
const end = str.lastIndexOf('>') + 1;
|
|
3795
|
+
return str.slice(0, end).replace(pattern, replacement) + str.slice(end);
|
|
3796
|
+
}
|
|
3797
|
+
|
|
3798
|
+
/**
|
|
3799
|
+
* `str.replace(pattern, replacement)` for a tag pattern that may fail even when a `>`
|
|
3800
|
+
* follows, like `<a\b[^>]*href...>`, or that runs on past its `>`.
|
|
3801
|
+
*
|
|
3802
|
+
* A candidate with no `>` after it ends the search, since no later candidate can close
|
|
3803
|
+
* either. A candidate that does not match skips every other candidate before the same
|
|
3804
|
+
* `>`, since those see the same tag body and fail the same way.
|
|
3805
|
+
*
|
|
3806
|
+
* @param {String} str String to process
|
|
3807
|
+
* @param {RegExp} prefix Global regex for the start of a candidate, which can not contain `>`
|
|
3808
|
+
* @param {RegExp} pattern The whole pattern as a sticky regex
|
|
3809
|
+
* @param {Function} replacement Function given the match and its groups
|
|
3810
|
+
* @return {String} Processed string
|
|
3811
|
+
*/
|
|
3812
|
+
function replaceTags(str, prefix, pattern, replacement) {
|
|
3813
|
+
const parts = [];
|
|
3814
|
+
// everything before this index is already in parts
|
|
3815
|
+
let copied = 0;
|
|
3816
|
+
let searchFrom = 0;
|
|
3817
|
+
|
|
3818
|
+
while (true) {
|
|
3819
|
+
prefix.lastIndex = searchFrom;
|
|
3820
|
+
const candidate = prefix.exec(str);
|
|
3821
|
+
if (!candidate) {
|
|
3822
|
+
break;
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
const start = candidate.index;
|
|
3826
|
+
pattern.lastIndex = start;
|
|
3827
|
+
const match = pattern.exec(str);
|
|
3828
|
+
if (!match) {
|
|
3829
|
+
const gt = str.indexOf('>', start);
|
|
3830
|
+
if (gt < 0) {
|
|
3831
|
+
break;
|
|
3832
|
+
}
|
|
3833
|
+
searchFrom = gt + 1;
|
|
3834
|
+
continue;
|
|
3835
|
+
}
|
|
3836
|
+
|
|
3837
|
+
parts.push(str.slice(copied, start), replacement(...match));
|
|
3838
|
+
copied = searchFrom = start + match[0].length;
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3841
|
+
parts.push(str.slice(copied));
|
|
3842
|
+
return parts.join('');
|
|
3843
|
+
}
|
|
3844
|
+
|
|
3845
|
+
/**
|
|
3846
|
+
* `str.replace(pattern, replacement)` for a pattern like `<!--.*?-->` or
|
|
3847
|
+
* `<script\b[^>]*>.*?<\/script\b[^>]*>`, ie. an opener and a closer, either of which may
|
|
3848
|
+
* continue with `[^>]*>`, and a lazy `.*?` between them. The engine rescans to the end of
|
|
3849
|
+
* the line for every opener that is not closed.
|
|
3850
|
+
*
|
|
3851
|
+
* @param {String} str String to process
|
|
3852
|
+
* @param {RegExp} open Global regex for the opener
|
|
3853
|
+
* @param {Boolean} openToGt Whether the opener continues with `[^>]*>`
|
|
3854
|
+
* @param {RegExp} close Global regex for the closer
|
|
3855
|
+
* @param {Boolean} closeToGt Whether the closer continues with `[^>]*>`
|
|
3856
|
+
* @param {String} replacement Replacement text
|
|
3857
|
+
* @return {String} Processed string
|
|
3858
|
+
*/
|
|
3859
|
+
function replaceBlocks(str, open, openToGt, close, closeToGt, replacement) {
|
|
3860
|
+
const nextGt = createFinder(str, />/g);
|
|
3861
|
+
const nextClose = createFinder(str, close);
|
|
3862
|
+
const nextLineEnd = createFinder(str, LINE_TERMINATOR);
|
|
3863
|
+
const parts = [];
|
|
3864
|
+
let copied = 0;
|
|
3865
|
+
let searchFrom = 0;
|
|
3866
|
+
|
|
3867
|
+
while (true) {
|
|
3868
|
+
open.lastIndex = searchFrom;
|
|
3869
|
+
const opener = open.exec(str);
|
|
3870
|
+
if (!opener) {
|
|
3871
|
+
break;
|
|
3872
|
+
}
|
|
3873
|
+
|
|
3874
|
+
const start = opener.index;
|
|
3875
|
+
searchFrom = start + 1;
|
|
3876
|
+
|
|
3877
|
+
// where the lazy `.*?` starts
|
|
3878
|
+
let from = start + opener[0].length;
|
|
3879
|
+
if (openToGt) {
|
|
3880
|
+
const gt = nextGt(from);
|
|
3881
|
+
if (!gt) {
|
|
3882
|
+
// neither this opener nor any later one can close
|
|
3883
|
+
break;
|
|
3884
|
+
}
|
|
3885
|
+
from = gt.index + 1;
|
|
3886
|
+
}
|
|
3887
|
+
|
|
3888
|
+
const closer = nextClose(from);
|
|
3889
|
+
if (!closer) {
|
|
3890
|
+
// no closer follows this opener, so none follows a later one either
|
|
3891
|
+
break;
|
|
3892
|
+
}
|
|
3893
|
+
|
|
3894
|
+
// `.*?` can not reach the closer across a line terminator
|
|
3895
|
+
const lineEnd = nextLineEnd(from);
|
|
3896
|
+
if (lineEnd && lineEnd.index < closer.index) {
|
|
3897
|
+
continue;
|
|
3898
|
+
}
|
|
3899
|
+
|
|
3900
|
+
let end = closer.index + closer[0].length;
|
|
3901
|
+
if (closeToGt) {
|
|
3902
|
+
const gt = nextGt(end);
|
|
3903
|
+
if (!gt) {
|
|
3904
|
+
// this is the first closer after the opener, and any other one comes
|
|
3905
|
+
// later, so none of them is followed by `>`
|
|
3906
|
+
break;
|
|
3907
|
+
}
|
|
3908
|
+
end = gt.index + 1;
|
|
3909
|
+
}
|
|
3910
|
+
|
|
3911
|
+
parts.push(str.slice(copied, start), replacement);
|
|
3912
|
+
copied = searchFrom = end;
|
|
3913
|
+
}
|
|
3914
|
+
|
|
3915
|
+
parts.push(str.slice(copied));
|
|
3916
|
+
return parts.join('');
|
|
3917
|
+
}
|
|
3918
|
+
|
|
3919
|
+
/**
|
|
3920
|
+
* `str.replace(pattern, '')` for `^.*<tag\b[^>]*>`, ie. drops everything up to and
|
|
3921
|
+
* including the last such tag on the first line.
|
|
3922
|
+
*
|
|
3923
|
+
* @param {String} str String to process
|
|
3924
|
+
* @param {RegExp} prefix Global regex for the start of the tag, which can not contain `>`
|
|
3925
|
+
* @return {String} Processed string
|
|
3926
|
+
*/
|
|
3927
|
+
function stripThroughLastTag(str, prefix) {
|
|
3928
|
+
const lineEnd = str.search(LINE_TERMINATOR);
|
|
3929
|
+
// `.*` stops at the first line terminator, and `[^>]*>` needs a `>` after the tag
|
|
3930
|
+
const limit = Math.min(lineEnd < 0 ? str.length : lineEnd, str.lastIndexOf('>'));
|
|
3931
|
+
|
|
3932
|
+
let last = -1;
|
|
3933
|
+
prefix.lastIndex = 0;
|
|
3934
|
+
let match;
|
|
3935
|
+
while ((match = prefix.exec(str)) && match.index < limit) {
|
|
3936
|
+
last = match.index;
|
|
3937
|
+
}
|
|
3938
|
+
|
|
3939
|
+
return last < 0 ? str : str.slice(str.indexOf('>', last) + 1);
|
|
3940
|
+
}
|
|
3941
|
+
|
|
3942
|
+
/**
|
|
3943
|
+
* `str.replace(pattern, '')` for `<tag\b[^>]*>.*$`, ie. drops the first such tag whose
|
|
3944
|
+
* `>` is on the last line, and everything after it.
|
|
3945
|
+
*
|
|
3946
|
+
* @param {String} str String to process
|
|
3947
|
+
* @param {RegExp} prefix Global regex for the start of the tag, which can not contain `>`
|
|
3948
|
+
* @return {String} Processed string
|
|
3949
|
+
*/
|
|
3950
|
+
function stripFromFirstTag(str, prefix) {
|
|
3951
|
+
// `.*$` has to reach the end of the string without crossing a line terminator, so the
|
|
3952
|
+
// tag has to start after the last `>` that comes before the last line terminator
|
|
3953
|
+
const lineEnd = lastLineTerminator(str);
|
|
3954
|
+
prefix.lastIndex = lineEnd < 0 ? 0 : str.lastIndexOf('>', lineEnd) + 1;
|
|
3955
|
+
|
|
3956
|
+
const match = prefix.exec(str);
|
|
3957
|
+
return match && match.index < str.lastIndexOf('>') ? str.slice(0, match.index) : str;
|
|
3958
|
+
}
|
|
3959
|
+
|
|
3570
3960
|
function htmlToText(str) {
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
.replace(/\r?\n/g, '\u0001')
|
|
3574
|
-
.replace(/<\!\-\-.*?\-\->/gi, ' ')
|
|
3961
|
+
// we can't process tags on multiple lines so remove newlines first
|
|
3962
|
+
str = str.replace(/\r?\n/g, '\u0001');
|
|
3575
3963
|
|
|
3576
|
-
|
|
3577
|
-
.replace(/<\/?(p|div|table|tr|td|th)\b[^>]*>/gi, '\n\n')
|
|
3578
|
-
.replace(/<script\b[^>]*>.*?<\/script\b[^>]*>/gi, ' ')
|
|
3579
|
-
.replace(/^.*<body\b[^>]*>/i, '')
|
|
3580
|
-
.replace(/^.*<\/head\b[^>]*>/i, '')
|
|
3581
|
-
.replace(/^.*<\!doctype\b[^>]*>/i, '')
|
|
3582
|
-
.replace(/<\/body\b[^>]*>.*$/i, '')
|
|
3583
|
-
.replace(/<\/html\b[^>]*>.*$/i, '')
|
|
3964
|
+
str = replaceBlocks(str, /<!--/g, false, /-->/g, false, ' ');
|
|
3584
3965
|
|
|
3585
|
-
|
|
3966
|
+
str = replaceClosedTags(str, /<br\b[^>]*>/gi, '\n');
|
|
3967
|
+
str = replaceClosedTags(str, /<\/?(p|div|table|tr|td|th)\b[^>]*>/gi, '\n\n');
|
|
3968
|
+
str = replaceBlocks(str, /<script\b/gi, true, /<\/script\b/gi, true, ' ');
|
|
3969
|
+
str = stripThroughLastTag(str, /<body\b/gi);
|
|
3970
|
+
str = stripThroughLastTag(str, /<\/head\b/gi);
|
|
3971
|
+
str = stripThroughLastTag(str, /<!doctype\b/gi);
|
|
3972
|
+
str = stripFromFirstTag(str, /<\/body\b/gi);
|
|
3973
|
+
str = stripFromFirstTag(str, /<\/html\b/gi);
|
|
3586
3974
|
|
|
3587
|
-
|
|
3975
|
+
str = replaceTags(str, /<a\b/gi, /<a\b[^>]*href\s*=\s*["']?([^\s"']+)[^>]*>/iy, (match, href) => ` (${href}) `);
|
|
3588
3976
|
|
|
3589
|
-
|
|
3977
|
+
str = replaceClosedTags(str, /<\/?(span|em|i|strong|b|u|a)\b[^>]*>/gi, '');
|
|
3590
3978
|
|
|
3591
|
-
|
|
3979
|
+
// the whitespace after the tag may run on past the last `>`
|
|
3980
|
+
str = replaceTags(str, /<li\b/gi, /<li\b[^>]*>[\n\u0001\s]*/iy, () => '* ');
|
|
3592
3981
|
|
|
3593
|
-
|
|
3982
|
+
str = replaceClosedTags(str, /<hr\b[^>]*>/g, '\n-------------\n');
|
|
3594
3983
|
|
|
3984
|
+
str = replaceClosedTags(str, /<[^>]*>/g, ' ');
|
|
3985
|
+
|
|
3986
|
+
str = str
|
|
3595
3987
|
// convert linebreak placeholders back to newlines
|
|
3596
3988
|
.replace(/\u0001/g, '\n')
|
|
3597
3989
|
|
|
@@ -3911,6 +4303,41 @@ function findAddressInText(text) {
|
|
|
3911
4303
|
return null;
|
|
3912
4304
|
}
|
|
3913
4305
|
|
|
4306
|
+
/**
|
|
4307
|
+
* Checks whether text holds an angle bracket address, ie. `<` and `>` around a run
|
|
4308
|
+
* without other angle brackets that has an '@' with at least one character on each side.
|
|
4309
|
+
*
|
|
4310
|
+
* Same answer as `/<[^<>]+@[^<>]+>/.test(text)`, which backtracks quadratically on a `<`
|
|
4311
|
+
* followed by a long run of '@' and no closing `>`.
|
|
4312
|
+
*
|
|
4313
|
+
* @param {String} text Text to check
|
|
4314
|
+
* @return {Boolean} true if an angle bracket address is present
|
|
4315
|
+
*/
|
|
4316
|
+
function hasAngleAddress(text) {
|
|
4317
|
+
// position right after the `<` that opened the current bracket, or -1
|
|
4318
|
+
let start = -1;
|
|
4319
|
+
// first '@' inside the bracket that has a character before it, or -1
|
|
4320
|
+
let at = -1;
|
|
4321
|
+
|
|
4322
|
+
for (let i = 0; i < text.length; i++) {
|
|
4323
|
+
const chr = text.charAt(i);
|
|
4324
|
+
if (chr === '<') {
|
|
4325
|
+
start = i + 1;
|
|
4326
|
+
at = -1;
|
|
4327
|
+
} else if (chr === '>') {
|
|
4328
|
+
if (at >= 0 && at < i - 1) {
|
|
4329
|
+
return true;
|
|
4330
|
+
}
|
|
4331
|
+
start = -1;
|
|
4332
|
+
at = -1;
|
|
4333
|
+
} else if (chr === '@' && start >= 0 && at < 0 && i > start) {
|
|
4334
|
+
at = i;
|
|
4335
|
+
}
|
|
4336
|
+
}
|
|
4337
|
+
|
|
4338
|
+
return false;
|
|
4339
|
+
}
|
|
4340
|
+
|
|
3914
4341
|
/**
|
|
3915
4342
|
* Converts tokens for a single address into an address object
|
|
3916
4343
|
*
|
|
@@ -4004,7 +4431,9 @@ function _handleAddress(tokens, depth) {
|
|
|
4004
4431
|
parsedGroup.forEach(member => {
|
|
4005
4432
|
if (member.group) {
|
|
4006
4433
|
// Nested group detected - flatten it by adding its members directly
|
|
4007
|
-
|
|
4434
|
+
for (const nestedMember of member.group) {
|
|
4435
|
+
groupMembers.push(nestedMember);
|
|
4436
|
+
}
|
|
4008
4437
|
} else {
|
|
4009
4438
|
groupMembers.push(member);
|
|
4010
4439
|
}
|
|
@@ -4038,11 +4467,13 @@ function _handleAddress(tokens, depth) {
|
|
|
4038
4467
|
const found = findAddressInText(data.text[i]);
|
|
4039
4468
|
if (found) {
|
|
4040
4469
|
data.address = [found.value];
|
|
4041
|
-
// the address and the whitespace around it collapse to one space
|
|
4470
|
+
// the address and the whitespace around it collapse to one space.
|
|
4471
|
+
// trimEnd rather than `/\s+$/`, which retries at every position of
|
|
4472
|
+
// a whitespace run that is not at the end and is quadratic in it.
|
|
4042
4473
|
data.text[i] = (
|
|
4043
|
-
data.text[i].substring(0, found.index).
|
|
4474
|
+
data.text[i].substring(0, found.index).trimEnd() +
|
|
4044
4475
|
' ' +
|
|
4045
|
-
data.text[i].substring(found.index + found.length).
|
|
4476
|
+
data.text[i].substring(found.index + found.length).trimStart()
|
|
4046
4477
|
).trim();
|
|
4047
4478
|
break;
|
|
4048
4479
|
}
|
|
@@ -4076,7 +4507,7 @@ function _handleAddress(tokens, depth) {
|
|
|
4076
4507
|
// Security: only re-parse if decoded text contains angle-bracket addresses.
|
|
4077
4508
|
// Without this, a bare encoded email (e.g. =?utf-8?B?dGVzdEBldmlsLmNv?=)
|
|
4078
4509
|
// would be fabricated into an address from attacker-controlled input.
|
|
4079
|
-
if (
|
|
4510
|
+
if (hasAngleAddress(decodedText)) {
|
|
4080
4511
|
const parsedSubAddresses = addressParser(decodedText);
|
|
4081
4512
|
if (parsedSubAddresses && parsedSubAddresses.length) {
|
|
4082
4513
|
return parsedSubAddresses;
|
|
@@ -4260,7 +4691,7 @@ function addressParser(str, options) {
|
|
|
4260
4691
|
|
|
4261
4692
|
let addresses = [];
|
|
4262
4693
|
let address = [];
|
|
4263
|
-
|
|
4694
|
+
const parsedAddresses = [];
|
|
4264
4695
|
|
|
4265
4696
|
tokens.forEach(token => {
|
|
4266
4697
|
if (token.type === 'operator' && (token.value === ',' || token.value === ';')) {
|
|
@@ -4277,12 +4708,13 @@ function addressParser(str, options) {
|
|
|
4277
4708
|
addresses.push(address);
|
|
4278
4709
|
}
|
|
4279
4710
|
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4711
|
+
// Appended in place. Rebuilding the list with concat() for every address copies
|
|
4712
|
+
// everything parsed so far and is quadratic in the number of addresses.
|
|
4713
|
+
for (const tokens of addresses) {
|
|
4714
|
+
for (const address of _handleAddress(tokens, depth)) {
|
|
4715
|
+
parsedAddresses.push(address);
|
|
4284
4716
|
}
|
|
4285
|
-
}
|
|
4717
|
+
}
|
|
4286
4718
|
|
|
4287
4719
|
if (options.flatten) {
|
|
4288
4720
|
let addresses = [];
|
|
@@ -4686,9 +5118,15 @@ class PostalMime {
|
|
|
4686
5118
|
.trim();
|
|
4687
5119
|
}
|
|
4688
5120
|
|
|
4689
|
-
// Enforce into unicode
|
|
4690
|
-
|
|
4691
|
-
|
|
5121
|
+
// Enforce into unicode, ending in exactly one newline. The trailing
|
|
5122
|
+
// newlines are counted rather than replaced with `/\n*$/`, which
|
|
5123
|
+
// retries at every newline of a run that does not end the text.
|
|
5124
|
+
const decodedText = node.getTextContent().replace(/\r?\n/g, '\n');
|
|
5125
|
+
let end = decodedText.length;
|
|
5126
|
+
while (end > 0 && decodedText.charCodeAt(end - 1) === 0x0a) {
|
|
5127
|
+
end--;
|
|
5128
|
+
}
|
|
5129
|
+
attachment.content = textEncoder.encode(decodedText.slice(0, end) + '\n');
|
|
4692
5130
|
break;
|
|
4693
5131
|
}
|
|
4694
5132
|
|
|
@@ -4939,15 +5377,17 @@ class PostalMime {
|
|
|
4939
5377
|
}
|
|
4940
5378
|
|
|
4941
5379
|
for (const key of ['to', 'cc', 'bcc', 'reply-to']) {
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
5380
|
+
// Appended in place, concat() copies the whole list for every header
|
|
5381
|
+
const addresses = [];
|
|
5382
|
+
for (const entry of this.root.headers) {
|
|
5383
|
+
if (entry.key === key && entry.value) {
|
|
5384
|
+
for (const address of addressParser(entry.value)) {
|
|
5385
|
+
addresses.push(address);
|
|
5386
|
+
}
|
|
5387
|
+
}
|
|
5388
|
+
}
|
|
4949
5389
|
|
|
4950
|
-
if (addresses
|
|
5390
|
+
if (addresses.length) {
|
|
4951
5391
|
const camelKey = toCamelCase(key);
|
|
4952
5392
|
message[camelKey] = addresses;
|
|
4953
5393
|
}
|