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