securemark 0.217.0 → 0.218.3
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 +17 -0
- package/dist/securemark.js +166 -143
- package/markdown.d.ts +7 -6
- package/package-lock.json +7 -7
- package/package.json +1 -1
- package/src/parser/api/bind.test.ts +5 -5
- package/src/parser/api/bind.ts +2 -2
- package/src/parser/api/parse.test.ts +18 -0
- package/src/parser/api/parse.ts +2 -2
- package/src/parser/block/blockquote.test.ts +1 -1
- package/src/parser/block/extension/example.test.ts +1 -1
- package/src/parser/block/extension/fig.ts +1 -1
- package/src/parser/block/extension/table.ts +2 -2
- package/src/parser/block/heading.test.ts +10 -12
- package/src/parser/block/ilist.ts +8 -1
- package/src/parser/block/paragraph.test.ts +7 -0
- package/src/parser/function/footnote.test.ts +16 -16
- package/src/parser/function/footnote.ts +1 -1
- package/src/parser/inline/annotation.test.ts +5 -2
- package/src/parser/inline/annotation.ts +4 -5
- package/src/parser/inline/comment.ts +1 -1
- package/src/parser/inline/deletion.test.ts +1 -0
- package/src/parser/inline/deletion.ts +2 -1
- package/src/parser/inline/emphasis.ts +5 -5
- package/src/parser/inline/emstrong.ts +4 -4
- package/src/parser/inline/extension/index.test.ts +25 -7
- package/src/parser/inline/extension/index.ts +17 -14
- package/src/parser/inline/extension/indexee.ts +1 -1
- package/src/parser/inline/extension/indexer.test.ts +2 -0
- package/src/parser/inline/extension/indexer.ts +5 -3
- package/src/parser/inline/extension/placeholder.test.ts +10 -10
- package/src/parser/inline/extension/placeholder.ts +13 -9
- package/src/parser/inline/html.test.ts +2 -1
- package/src/parser/inline/html.ts +16 -22
- package/src/parser/inline/insertion.test.ts +1 -0
- package/src/parser/inline/insertion.ts +2 -1
- package/src/parser/inline/link.test.ts +3 -7
- package/src/parser/inline/link.ts +9 -9
- package/src/parser/inline/mark.test.ts +1 -0
- package/src/parser/inline/mark.ts +2 -2
- package/src/parser/inline/math.ts +2 -2
- package/src/parser/inline/media.test.ts +3 -7
- package/src/parser/inline/media.ts +5 -5
- package/src/parser/inline/reference.test.ts +24 -9
- package/src/parser/inline/reference.ts +9 -9
- package/src/parser/inline/ruby.test.ts +2 -1
- package/src/parser/inline/ruby.ts +12 -10
- package/src/parser/inline/strong.ts +5 -5
- package/src/parser/segment.ts +11 -17
- package/src/parser/source/escapable.ts +1 -0
- package/src/parser/source/str.ts +3 -5
- package/src/parser/source/text.ts +8 -6
- package/src/parser/source/unescapable.ts +1 -0
- package/src/parser/util.ts +85 -60
|
@@ -27,19 +27,13 @@ describe('Unit: parser/inline/media', () => {
|
|
|
27
27
|
assert.deepStrictEqual(inspect(parser('![]{ }')), undefined);
|
|
28
28
|
assert.deepStrictEqual(inspect(parser('![]{ }')), undefined);
|
|
29
29
|
assert.deepStrictEqual(inspect(parser('![]{ }')), undefined);
|
|
30
|
-
assert.deepStrictEqual(inspect(parser('![]{/ }')), [['<a href="/" target="_blank"><img class="media invalid" data-src="/" alt=""></a>'], '']);
|
|
31
|
-
assert.deepStrictEqual(inspect(parser('![]{/ /}')), [['<a href="/" target="_blank"><img class="media invalid" data-src="/" alt=""></a>'], '']);
|
|
32
30
|
assert.deepStrictEqual(inspect(parser('![]]{/}')), undefined);
|
|
33
31
|
assert.deepStrictEqual(inspect(parser('![]{{}')), undefined);
|
|
34
32
|
assert.deepStrictEqual(inspect(parser('![]{{a}}')), undefined);
|
|
35
|
-
assert.deepStrictEqual(inspect(parser('![]{a }')), [['<a href="a" target="_blank"><img class="media invalid" data-src="a" alt=""></a>'], '']);
|
|
36
33
|
assert.deepStrictEqual(inspect(parser('![]{a\nb}')), undefined);
|
|
37
34
|
assert.deepStrictEqual(inspect(parser('![]{a\\\nb}')), undefined);
|
|
38
35
|
assert.deepStrictEqual(inspect(parser('![]{ a}')), undefined);
|
|
39
|
-
assert.deepStrictEqual(inspect(parser('![]{ a }')), [['<a href="a" target="_blank"><img class="media invalid" data-src="a" alt=""></a>'], '']);
|
|
40
36
|
assert.deepStrictEqual(inspect(parser('![]{ a\n}')), undefined);
|
|
41
|
-
assert.deepStrictEqual(inspect(parser('![]{\ta }')), undefined);
|
|
42
|
-
assert.deepStrictEqual(inspect(parser('![]{\ta\t}')), undefined);
|
|
43
37
|
assert.deepStrictEqual(inspect(parser('![ ]{#}')), undefined);
|
|
44
38
|
assert.deepStrictEqual(inspect(parser('![ ]{#}')), undefined);
|
|
45
39
|
assert.deepStrictEqual(inspect(parser('![\\ ]{#}')), undefined);
|
|
@@ -48,7 +42,6 @@ describe('Unit: parser/inline/media', () => {
|
|
|
48
42
|
assert.deepStrictEqual(inspect(parser('![ a]{#}')), undefined);
|
|
49
43
|
assert.deepStrictEqual(inspect(parser('![ a ]{#}')), undefined);
|
|
50
44
|
assert.deepStrictEqual(inspect(parser('![\\ a ]{#}')), undefined);
|
|
51
|
-
assert.deepStrictEqual(inspect(parser('![a ]{#}')), undefined);
|
|
52
45
|
assert.deepStrictEqual(inspect(parser('![a\nb]{#}')), undefined);
|
|
53
46
|
assert.deepStrictEqual(inspect(parser('![a\\\nb]{#}')), undefined);
|
|
54
47
|
assert.deepStrictEqual(inspect(parser('![]{ttp://host}')), [['<img class="media invalid" data-src="ttp://host" alt="">'], '']);
|
|
@@ -62,13 +55,16 @@ describe('Unit: parser/inline/media', () => {
|
|
|
62
55
|
it('basic', () => {
|
|
63
56
|
assert.deepStrictEqual(inspect(parser('![]{b}')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
|
|
64
57
|
assert.deepStrictEqual(inspect(parser('![]{b }')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
|
|
58
|
+
assert.deepStrictEqual(inspect(parser('![]{b }')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
|
|
65
59
|
assert.deepStrictEqual(inspect(parser('![]{ b }')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
|
|
60
|
+
assert.deepStrictEqual(inspect(parser('![]{ b }')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
|
|
66
61
|
assert.deepStrictEqual(inspect(parser('![]{\\}')), [['<a href="\\" target="_blank"><img class="media" data-src="\\" alt=""></a>'], '']);
|
|
67
62
|
assert.deepStrictEqual(inspect(parser('![]{\\ }')), [['<a href="\\" target="_blank"><img class="media" data-src="\\" alt=""></a>'], '']);
|
|
68
63
|
assert.deepStrictEqual(inspect(parser('![]{\\b}')), [['<a href="\\b" target="_blank"><img class="media" data-src="\\b" alt=""></a>'], '']);
|
|
69
64
|
assert.deepStrictEqual(inspect(parser('![]{./b}')), [['<a href="./b" target="_blank"><img class="media" data-src="./b" alt=""></a>'], '']);
|
|
70
65
|
assert.deepStrictEqual(inspect(parser('![]{^/b}')), [[`<a href="/b" target="_blank"><img class="media" data-src="/b" alt=""></a>`], '']);
|
|
71
66
|
assert.deepStrictEqual(inspect(parser('![a ]{b}')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="a"></a>'], '']);
|
|
67
|
+
assert.deepStrictEqual(inspect(parser('![a ]{b}')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="a"></a>'], '']);
|
|
72
68
|
assert.deepStrictEqual(inspect(parser('![a b]{c}')), [['<a href="c" target="_blank"><img class="media" data-src="c" alt="a b"></a>'], '']);
|
|
73
69
|
assert.deepStrictEqual(inspect(parser('!{b}')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt=""></a>'], '']);
|
|
74
70
|
assert.deepStrictEqual(inspect(parser('!{ ][ }')), [['<a href="][" target="_blank"><img class="media" data-src="][" alt=""></a>'], '']);
|
|
@@ -6,7 +6,7 @@ import { link, uri, option as linkoption, resolve } from './link';
|
|
|
6
6
|
import { attributes } from './html';
|
|
7
7
|
import { htmlentity } from './htmlentity';
|
|
8
8
|
import { txt, str } from '../source';
|
|
9
|
-
import {
|
|
9
|
+
import { verifyStartTight } from '../util';
|
|
10
10
|
import { html, define } from 'typed-dom';
|
|
11
11
|
import { ReadonlyURL } from 'spica/url';
|
|
12
12
|
import { unshift, push, join } from 'spica/array';
|
|
@@ -25,10 +25,10 @@ export const media: MediaParser = lazy(() => creator(10, bind(verify(fmap(open(
|
|
|
25
25
|
guard(context => context.syntax?.inline?.media ?? true,
|
|
26
26
|
tails([
|
|
27
27
|
dup(surround(/^\[(?!\\?\s)/, some(union([htmlentity, bracket, txt]), ']', /^\\?\n/), ']', true)),
|
|
28
|
-
dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^
|
|
28
|
+
dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]?}/)),
|
|
29
29
|
])))),
|
|
30
30
|
([as, bs]) => bs ? [[join(as)], bs] : [[''], as]),
|
|
31
|
-
([[text]]) =>
|
|
31
|
+
([[text]]) => verifyStartTight([text || '-'])),
|
|
32
32
|
([[text], params], rest, context) => {
|
|
33
33
|
const INSECURE_URI = params.shift()!;
|
|
34
34
|
assert(INSECURE_URI === INSECURE_URI.trim());
|
|
@@ -60,8 +60,8 @@ const bracket: MediaParser.TextParser.BracketParser = lazy(() => union([
|
|
|
60
60
|
]));
|
|
61
61
|
|
|
62
62
|
const option: MediaParser.ParameterParser.OptionParser = union([
|
|
63
|
-
fmap(str(/^
|
|
64
|
-
fmap(str(/^
|
|
63
|
+
fmap(str(/^[^\S\n]+[1-9][0-9]*x[1-9][0-9]*(?=[^\S\n]|})/), ([opt]) => [` width="${opt.slice(1).split('x')[0]}"`, ` height="${opt.slice(1).split('x')[1]}"`]),
|
|
64
|
+
fmap(str(/^[^\S\n]+[1-9][0-9]*:[1-9][0-9]*(?=[^\S\n]|})/), ([opt]) => [` aspect-ratio="${opt.slice(1).split(':').join('/')}"`]),
|
|
65
65
|
linkoption,
|
|
66
66
|
]);
|
|
67
67
|
|
|
@@ -20,11 +20,6 @@ describe('Unit: parser/inline/reference', () => {
|
|
|
20
20
|
assert.deepStrictEqual(inspect(parser('[[\na]]')), undefined);
|
|
21
21
|
assert.deepStrictEqual(inspect(parser('[[\\ a]]')), undefined);
|
|
22
22
|
assert.deepStrictEqual(inspect(parser('[[\\\na]]')), undefined);
|
|
23
|
-
assert.deepStrictEqual(inspect(parser('[[a ]]')), undefined);
|
|
24
|
-
assert.deepStrictEqual(inspect(parser('[[^a ]]')), undefined);
|
|
25
|
-
assert.deepStrictEqual(inspect(parser('[[^a| b ]]')), undefined);
|
|
26
|
-
assert.deepStrictEqual(inspect(parser('[[^a| ]]')), undefined);
|
|
27
|
-
assert.deepStrictEqual(inspect(parser('[[^a| b]]')), undefined);
|
|
28
23
|
assert.deepStrictEqual(inspect(parser('[[a\n]]')), undefined);
|
|
29
24
|
assert.deepStrictEqual(inspect(parser('[[a\\\n]]')), undefined);
|
|
30
25
|
assert.deepStrictEqual(inspect(parser('[[a\nb]]')), undefined);
|
|
@@ -40,7 +35,11 @@ describe('Unit: parser/inline/reference', () => {
|
|
|
40
35
|
|
|
41
36
|
it('basic', () => {
|
|
42
37
|
assert.deepStrictEqual(inspect(parser('[[a]]')), [['<sup class="reference">a</sup>'], '']);
|
|
43
|
-
assert.deepStrictEqual(inspect(parser('[[a ]]')), [['<sup class="reference">a
|
|
38
|
+
assert.deepStrictEqual(inspect(parser('[[a ]]')), [['<sup class="reference">a</sup>'], '']);
|
|
39
|
+
assert.deepStrictEqual(inspect(parser('[[a ]]')), [['<sup class="reference">a</sup>'], '']);
|
|
40
|
+
assert.deepStrictEqual(inspect(parser('[[a ]]')), [['<sup class="reference">a</sup>'], '']);
|
|
41
|
+
assert.deepStrictEqual(inspect(parser('[[a <wbr>]]')), [['<sup class="reference">a</sup>'], '']);
|
|
42
|
+
assert.deepStrictEqual(inspect(parser('[[a [# b #]]]')), [['<sup class="reference">a <sup class="comment" title="b"></sup></sup>'], '']);
|
|
44
43
|
assert.deepStrictEqual(inspect(parser('[[ab]]')), [['<sup class="reference">ab</sup>'], '']);
|
|
45
44
|
});
|
|
46
45
|
|
|
@@ -57,18 +56,28 @@ describe('Unit: parser/inline/reference', () => {
|
|
|
57
56
|
it('abbr', () => {
|
|
58
57
|
assert.deepStrictEqual(inspect(parser('[[^]]')), [['<sup class="reference invalid">^</sup>'], '']);
|
|
59
58
|
assert.deepStrictEqual(inspect(parser('[[^a]]')), [['<sup class="reference" data-abbr="a"></sup>'], '']);
|
|
59
|
+
assert.deepStrictEqual(inspect(parser('[[^a,]]')), [['<sup class="reference" data-abbr="a,"></sup>'], '']);
|
|
60
|
+
assert.deepStrictEqual(inspect(parser('[[^a, ]]')), [['<sup class="reference" data-abbr="a,"></sup>'], '']);
|
|
60
61
|
assert.deepStrictEqual(inspect(parser('[[^a ]]')), [['<sup class="reference" data-abbr="a"></sup>'], '']);
|
|
62
|
+
assert.deepStrictEqual(inspect(parser('[[^a ]]')), [['<sup class="reference invalid">^a</sup>'], '']);
|
|
61
63
|
assert.deepStrictEqual(inspect(parser('[[^a b]]')), [['<sup class="reference" data-abbr="a b"></sup>'], '']);
|
|
62
64
|
assert.deepStrictEqual(inspect(parser('[[^a b]]')), [['<sup class="reference invalid">^a b</sup>'], '']);
|
|
63
65
|
assert.deepStrictEqual(inspect(parser('[[^a|]]')), [['<sup class="reference" data-abbr="a"></sup>'], '']);
|
|
66
|
+
assert.deepStrictEqual(inspect(parser('[[^a,|]]')), [['<sup class="reference" data-abbr="a,"></sup>'], '']);
|
|
64
67
|
assert.deepStrictEqual(inspect(parser('[[^a |]]')), [['<sup class="reference" data-abbr="a"></sup>'], '']);
|
|
65
68
|
assert.deepStrictEqual(inspect(parser('[[^a|b]]')), [['<sup class="reference invalid">^a|b</sup>'], '']);
|
|
66
69
|
assert.deepStrictEqual(inspect(parser('[[^a| ]]')), [['<sup class="reference" data-abbr="a"></sup>'], '']);
|
|
67
70
|
assert.deepStrictEqual(inspect(parser('[[^a| b]]')), [['<sup class="reference" data-abbr="a">b</sup>'], '']);
|
|
68
|
-
assert.deepStrictEqual(inspect(parser('[[^a| b ]]')), [['<sup class="reference" data-abbr="a">b
|
|
71
|
+
assert.deepStrictEqual(inspect(parser('[[^a| b ]]')), [['<sup class="reference" data-abbr="a">b</sup>'], '']);
|
|
72
|
+
assert.deepStrictEqual(inspect(parser('[[^a| b ]]')), [['<sup class="reference" data-abbr="a">b</sup>'], '']);
|
|
73
|
+
assert.deepStrictEqual(inspect(parser('[[^a| ]]')), [['<sup class="reference" data-abbr="a"></sup>'], '']);
|
|
74
|
+
assert.deepStrictEqual(inspect(parser('[[^a| b]]')), [['<sup class="reference" data-abbr="a">b</sup>'], '']);
|
|
75
|
+
assert.deepStrictEqual(inspect(parser('[[^a| <wbr>]]')), [['<sup class="reference invalid">^a|</sup>'], '']);
|
|
76
|
+
assert.deepStrictEqual(inspect(parser('[[^a| <wbr>b]]')), [['<sup class="reference invalid">^a| <wbr>b</sup>'], '']);
|
|
77
|
+
assert.deepStrictEqual(inspect(parser('[[^a| ^b]]')), [['<sup class="reference" data-abbr="a">^b</sup>'], '']);
|
|
69
78
|
assert.deepStrictEqual(inspect(parser('[[^1]]')), [['<sup class="reference invalid">^1</sup>'], '']);
|
|
70
79
|
assert.deepStrictEqual(inspect(parser('[[^1a]]')), [['<sup class="reference" data-abbr="1a"></sup>'], '']);
|
|
71
|
-
assert.deepStrictEqual(inspect(parser('[[^1 ]]')), [['<sup class="reference invalid">^1
|
|
80
|
+
assert.deepStrictEqual(inspect(parser('[[^1 ]]')), [['<sup class="reference invalid">^1</sup>'], '']);
|
|
72
81
|
assert.deepStrictEqual(inspect(parser('[[^1 a]]')), [['<sup class="reference" data-abbr="1 a"></sup>'], '']);
|
|
73
82
|
assert.deepStrictEqual(inspect(parser('[[^1|]]')), [['<sup class="reference invalid">^1|</sup>'], '']);
|
|
74
83
|
assert.deepStrictEqual(inspect(parser('[[^1 |]]')), [['<sup class="reference invalid">^1 |</sup>'], '']);
|
|
@@ -76,9 +85,15 @@ describe('Unit: parser/inline/reference', () => {
|
|
|
76
85
|
assert.deepStrictEqual(inspect(parser('[[^Xyz 2020]]')), [['<sup class="reference" data-abbr="Xyz 2020"></sup>'], '']);
|
|
77
86
|
assert.deepStrictEqual(inspect(parser('[[^Xyz, 2020, p1-2]]')), [['<sup class="reference" data-abbr="Xyz, 2020, p1-2"></sup>'], '']);
|
|
78
87
|
assert.deepStrictEqual(inspect(parser('[[^X. Y., Z et al., 2020, p1-2]]')), [['<sup class="reference" data-abbr="X. Y., Z et al., 2020, p1-2"></sup>'], '']);
|
|
88
|
+
assert.deepStrictEqual(inspect(parser(`[[^A's, Aces']]`)), [[`<sup class="reference" data-abbr="A's, Aces'"></sup>`], '']);
|
|
79
89
|
assert.deepStrictEqual(inspect(parser('[[^^]]')), [['<sup class="reference invalid">^^</sup>'], '']);
|
|
80
90
|
assert.deepStrictEqual(inspect(parser('[[\\^]]')), [['<sup class="reference">^</sup>'], '']);
|
|
81
|
-
assert.deepStrictEqual(inspect(parser('[[^
|
|
91
|
+
assert.deepStrictEqual(inspect(parser('[[^ ]]')), [['<sup class="reference invalid">^</sup>'], '']);
|
|
92
|
+
assert.deepStrictEqual(inspect(parser('[[^ a]]')), [['<sup class="reference invalid">^ a</sup>'], '']);
|
|
93
|
+
assert.deepStrictEqual(inspect(parser('[[^ |]]')), [['<sup class="reference invalid">^ |</sup>'], '']);
|
|
94
|
+
assert.deepStrictEqual(inspect(parser('[[^ |b]]')), [['<sup class="reference invalid">^ |b</sup>'], '']);
|
|
95
|
+
assert.deepStrictEqual(inspect(parser('[[^ | ]]')), [['<sup class="reference invalid">^ |</sup>'], '']);
|
|
96
|
+
assert.deepStrictEqual(inspect(parser('[[^ | b]]')), [['<sup class="reference invalid">^ | b</sup>'], '']);
|
|
82
97
|
});
|
|
83
98
|
|
|
84
99
|
});
|
|
@@ -3,10 +3,10 @@ import { ReferenceParser } from '../inline';
|
|
|
3
3
|
import { union, subsequence, some, validate, verify, focus, guard, context, creator, surround, lazy, fmap } from '../../combinator';
|
|
4
4
|
import { inline } from '../inline';
|
|
5
5
|
import { str } from '../source';
|
|
6
|
-
import { startTight,
|
|
6
|
+
import { startTight, isStartTight, trimEnd, stringify } from '../util';
|
|
7
7
|
import { html, defrag } from 'typed-dom';
|
|
8
8
|
|
|
9
|
-
export const reference: ReferenceParser = lazy(() => creator(validate('[[', ']]', '\n', fmap(
|
|
9
|
+
export const reference: ReferenceParser = lazy(() => creator(validate('[[', ']]', '\n', fmap(surround(
|
|
10
10
|
'[[',
|
|
11
11
|
guard(context => context.syntax?.inline?.reference ?? true,
|
|
12
12
|
startTight(
|
|
@@ -23,23 +23,23 @@ export const reference: ReferenceParser = lazy(() => creator(validate('[[', ']]'
|
|
|
23
23
|
subsequence([
|
|
24
24
|
abbr,
|
|
25
25
|
focus('^', c => [['', c], '']),
|
|
26
|
-
|
|
26
|
+
some(inline, ']', /^\\?\n/),
|
|
27
27
|
])))),
|
|
28
28
|
']]'),
|
|
29
|
-
|
|
30
|
-
ns => [html('sup', attributes(ns), defrag(ns))]))));
|
|
29
|
+
ns => [html('sup', attributes(ns), trimEnd(defrag(ns)))]))));
|
|
31
30
|
|
|
32
|
-
const abbr: ReferenceParser.AbbrParser = creator(fmap(surround(
|
|
31
|
+
const abbr: ReferenceParser.AbbrParser = creator(fmap(verify(surround(
|
|
33
32
|
'^',
|
|
34
|
-
union([str(/^(?![0-9]+\s?[|\]])[0-9A-Za-z]+(?:(
|
|
35
|
-
|
|
33
|
+
union([str(/^(?![0-9]+\s?[|\]])[0-9A-Za-z]+(?:(?:-|(?=\W)(?!'\d)'?(?!\.\d)\.?(?!,\S),? ?)[0-9A-Za-z]+)*(?:-|'?\.?,? ?)?/)]),
|
|
34
|
+
/^\|?(?=]])|^\|[^\S\n]+/),
|
|
35
|
+
(_, rest, context) => isStartTight(rest, context)),
|
|
36
36
|
([source]) => [html('abbr', source)]));
|
|
37
37
|
|
|
38
38
|
function attributes(ns: (string | HTMLElement)[]): Record<string, string | undefined> {
|
|
39
39
|
return typeof ns[0] === 'object' && ns[0].tagName === 'ABBR'
|
|
40
40
|
? {
|
|
41
41
|
class: 'reference',
|
|
42
|
-
'data-abbr': stringify([ns.shift()!]),
|
|
42
|
+
'data-abbr': stringify([ns.shift()!]).trimEnd(),
|
|
43
43
|
}
|
|
44
44
|
: ns[0] === ''
|
|
45
45
|
? {
|
|
@@ -16,7 +16,6 @@ describe('Unit: parser/inline/ruby', () => {
|
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('[	 a](b)')), undefined);
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('[a]()')), undefined);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('[a]( )')), undefined);
|
|
19
|
-
assert.deepStrictEqual(inspect(parser('[a ](b)')), undefined);
|
|
20
19
|
assert.deepStrictEqual(inspect(parser('[a\nb](c)')), undefined);
|
|
21
20
|
assert.deepStrictEqual(inspect(parser('[a](b\nc)')), undefined);
|
|
22
21
|
assert.deepStrictEqual(inspect(parser(' [a](b)')), undefined);
|
|
@@ -29,6 +28,8 @@ describe('Unit: parser/inline/ruby', () => {
|
|
|
29
28
|
assert.deepStrictEqual(inspect(parser('[A ](a)')), [['<ruby>A<rp>(</rp><rt>a</rt><rp>)</rp></ruby>'], '']);
|
|
30
29
|
assert.deepStrictEqual(inspect(parser('[A ](a b)')), [['<ruby>A<rp>(</rp><rt>a b</rt><rp>)</rp></ruby>'], '']);
|
|
31
30
|
assert.deepStrictEqual(inspect(parser('[A ](a b )')), [['<ruby>A<rp>(</rp><rt>a b</rt><rp>)</rp></ruby>'], '']);
|
|
31
|
+
assert.deepStrictEqual(inspect(parser('[A ](a)')), [['<ruby>A<rp>(</rp><rt>a</rt><rp>)</rp><rt></rt></ruby>'], '']);
|
|
32
|
+
assert.deepStrictEqual(inspect(parser('[A \\ ](a)')), [['<ruby>A<rp>(</rp><rt>a</rt><rp>)</rp> <rt></rt></ruby>'], '']);
|
|
32
33
|
assert.deepStrictEqual(inspect(parser('[AB](a)')), [['<ruby>AB<rp>(</rp><rt>a</rt><rp>)</rp></ruby>'], '']);
|
|
33
34
|
assert.deepStrictEqual(inspect(parser('[AB](a )')), [['<ruby>A<rp>(</rp><rt>a</rt><rp>)</rp>B<rt></rt></ruby>'], '']);
|
|
34
35
|
assert.deepStrictEqual(inspect(parser('[AB]( b)')), [['<ruby>A<rt></rt>B<rp>(</rp><rt>b</rt><rp>)</rp></ruby>'], '']);
|
|
@@ -4,7 +4,7 @@ import { eval, exec } from '../../combinator/data/parser';
|
|
|
4
4
|
import { sequence, validate, verify, focus, creator, surround, lazy, bind } from '../../combinator';
|
|
5
5
|
import { htmlentity } from './htmlentity';
|
|
6
6
|
import { text as txt } from '../source';
|
|
7
|
-
import {
|
|
7
|
+
import { verifyStartTight } from '../util';
|
|
8
8
|
import { html, defrag } from 'typed-dom';
|
|
9
9
|
import { unshift, push, join } from 'spica/array';
|
|
10
10
|
|
|
@@ -14,32 +14,34 @@ export const ruby: RubyParser = lazy(() => creator(bind(verify(
|
|
|
14
14
|
surround('[', focus(/^(?:\\[^\n]|[^\[\]\n])+(?=]\()/, text), ']'),
|
|
15
15
|
surround('(', focus(/^(?:\\[^\n]|[^\(\)\n])+(?=\))/, text), ')'),
|
|
16
16
|
])),
|
|
17
|
-
([texts]) =>
|
|
17
|
+
([texts]) => verifyStartTight(texts)),
|
|
18
18
|
([texts, rubies], rest) => {
|
|
19
|
-
texts[texts.length - 1]
|
|
20
|
-
|
|
19
|
+
const tail = typeof texts[texts.length - 1] === 'object'
|
|
20
|
+
? [texts.pop()!]
|
|
21
|
+
: [];
|
|
22
|
+
tail.length === 0 && texts[texts.length - 1] === '' && texts.pop();
|
|
21
23
|
switch (true) {
|
|
22
24
|
case rubies.length <= texts.length:
|
|
23
|
-
return [[html('ruby', defrag(texts
|
|
25
|
+
return [[html('ruby', defrag(push(texts
|
|
24
26
|
.reduce((acc, _, i) =>
|
|
25
27
|
push(acc, unshift([texts[i]],
|
|
26
28
|
i < rubies.length && rubies[i]
|
|
27
29
|
? [html('rp', '('), html('rt', rubies[i]), html('rp', ')')]
|
|
28
30
|
: [html('rt')]))
|
|
29
|
-
, [])))], rest];
|
|
31
|
+
, []), tail)))], rest];
|
|
30
32
|
case texts.length === 1 && [...texts[0]].length >= rubies.length:
|
|
31
|
-
return [[html('ruby', defrag([...texts[0]]
|
|
33
|
+
return [[html('ruby', defrag(push([...texts[0]]
|
|
32
34
|
.reduce((acc, _, i, texts) =>
|
|
33
35
|
push(acc, unshift([texts[i]],
|
|
34
36
|
i < rubies.length && rubies[i]
|
|
35
37
|
? [html('rp', '('), html('rt', rubies[i]), html('rp', ')')]
|
|
36
38
|
: [html('rt')]))
|
|
37
|
-
, [])))], rest];
|
|
39
|
+
, []), tail)))], rest];
|
|
38
40
|
default:
|
|
39
41
|
assert(rubies.length > 0);
|
|
40
|
-
return [[html('ruby', defrag(unshift(
|
|
42
|
+
return [[html('ruby', defrag(push(unshift(
|
|
41
43
|
[join(texts, ' ')],
|
|
42
|
-
[html('rp', '('), html('rt', join(rubies, ' ').trim()), html('rp', ')')])))
|
|
44
|
+
[html('rp', '('), html('rt', join(rubies, ' ').trim()), html('rp', ')')]), tail)))
|
|
43
45
|
], rest];
|
|
44
46
|
}
|
|
45
47
|
})));
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { StrongParser } from '../inline';
|
|
2
|
-
import { union, some, creator, surround, lazy } from '../../combinator';
|
|
2
|
+
import { union, some, creator, surround, close, lazy } from '../../combinator';
|
|
3
3
|
import { inline } from '../inline';
|
|
4
4
|
import { emphasis } from './emphasis';
|
|
5
5
|
import { str } from '../source';
|
|
6
|
-
import { startTight,
|
|
6
|
+
import { startTight, verifyEndTight, trimEndBR } from '../util';
|
|
7
7
|
import { html, defrag } from 'typed-dom';
|
|
8
8
|
import { unshift } from 'spica/array';
|
|
9
9
|
|
|
10
|
-
export const strong: StrongParser = lazy(() => creator(surround(
|
|
11
|
-
str('**',
|
|
10
|
+
export const strong: StrongParser = lazy(() => creator(surround(close(
|
|
11
|
+
str('**'), /^(?!\*)/),
|
|
12
12
|
startTight(some(union([emphasis, some(inline, '*'), str('*')]), '**')),
|
|
13
13
|
str('**'), false,
|
|
14
14
|
([as, bs, cs], rest) =>
|
|
15
|
-
|
|
15
|
+
verifyEndTight(bs)
|
|
16
16
|
? [[html('strong', defrag(trimEndBR(bs)))], rest]
|
|
17
17
|
: [unshift(as, bs), cs[0] + rest],
|
|
18
18
|
([as, bs], rest) => [unshift(as, bs), rest])));
|
package/src/parser/segment.ts
CHANGED
|
@@ -10,20 +10,20 @@ import { contentline, emptyline } from './source';
|
|
|
10
10
|
|
|
11
11
|
import SegmentParser = MarkdownParser.SegmentParser;
|
|
12
12
|
|
|
13
|
-
const
|
|
14
|
-
export const
|
|
13
|
+
export const MAX_SEGMENT_SIZE = 100_000; // 100,000 bytes (Max value size of FDB)
|
|
14
|
+
export const MAX_INPUT_SIZE = MAX_SEGMENT_SIZE * 10;
|
|
15
15
|
|
|
16
16
|
const parser: SegmentParser = union([
|
|
17
17
|
heading,
|
|
18
18
|
codeblock,
|
|
19
19
|
mathblock,
|
|
20
20
|
extension,
|
|
21
|
-
some(contentline,
|
|
22
|
-
some(emptyline,
|
|
21
|
+
some(contentline, MAX_SEGMENT_SIZE * 2),
|
|
22
|
+
some(emptyline, MAX_SEGMENT_SIZE * 2),
|
|
23
23
|
]);
|
|
24
24
|
|
|
25
25
|
export function* segment(source: string): Generator<string, undefined, undefined> {
|
|
26
|
-
if (!validate(source)) return yield `\0Too large input over ${
|
|
26
|
+
if (!validate(source, MAX_INPUT_SIZE)) return yield `\0Too large input over ${MAX_INPUT_SIZE.toLocaleString('en')} bytes.\n${source.slice(0, 1001)}`;
|
|
27
27
|
assert(source.length < Number.MAX_SAFE_INTEGER);
|
|
28
28
|
while (source !== '') {
|
|
29
29
|
const result = parser(source, {})!;
|
|
@@ -34,21 +34,15 @@ export function* segment(source: string): Generator<string, undefined, undefined
|
|
|
34
34
|
assert(segs.join('') === source.slice(0, source.length - rest.length));
|
|
35
35
|
for (let i = 0; i < segs.length; ++i) {
|
|
36
36
|
const seg = segs[i];
|
|
37
|
-
|
|
38
|
-
? yield
|
|
39
|
-
: yield seg
|
|
37
|
+
validate(source, MAX_SEGMENT_SIZE)
|
|
38
|
+
? yield seg
|
|
39
|
+
: yield `\0Too large segment over ${MAX_SEGMENT_SIZE.toLocaleString('en')} bytes.\n${seg}`
|
|
40
40
|
}
|
|
41
41
|
source = rest;
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function validate(source: string): boolean {
|
|
46
|
-
return source.length <=
|
|
47
|
-
&& new Blob([source]).size <=
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function prepare(source: string): string {
|
|
51
|
-
return validate(source)
|
|
52
|
-
? source
|
|
53
|
-
: source.slice(0, INPUT_SIZE_LIMIT + 1);
|
|
45
|
+
export function validate(source: string, size: number): boolean {
|
|
46
|
+
return source.length <= size / 2
|
|
47
|
+
|| source.length <= size && new Blob([source]).size <= size;
|
|
54
48
|
}
|
|
@@ -4,6 +4,7 @@ import { creator } from '../../combinator';
|
|
|
4
4
|
const separator = /[\s\x00-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]/;
|
|
5
5
|
|
|
6
6
|
export const escsource: EscapableSourceParser = creator(source => {
|
|
7
|
+
assert(source[0] !== '\x7F');
|
|
7
8
|
if (source === '') return;
|
|
8
9
|
const i = source.search(separator);
|
|
9
10
|
switch (i) {
|
package/src/parser/source/str.ts
CHANGED
|
@@ -3,22 +3,20 @@ import { StrParser } from '../source';
|
|
|
3
3
|
import { Parser, Context } from '../../combinator/data/parser';
|
|
4
4
|
import { creator } from '../../combinator';
|
|
5
5
|
|
|
6
|
-
export function str(pattern: string | RegExp,
|
|
7
|
-
export function str(pattern: string | RegExp,
|
|
6
|
+
export function str(pattern: string | RegExp, mustConsume?: boolean): StrParser;
|
|
7
|
+
export function str(pattern: string | RegExp, mustConsume = true): Parser<string, Context<StrParser>, []> {
|
|
8
8
|
assert(pattern);
|
|
9
9
|
return typeof pattern === 'string'
|
|
10
10
|
? creator(source => {
|
|
11
11
|
if (source === '') return;
|
|
12
12
|
return source.slice(0, pattern.length) === pattern
|
|
13
|
-
&& !(not && source.slice(pattern.length, pattern.length + not.length) === not)
|
|
14
13
|
? [[pattern], source.slice(pattern.length)]
|
|
15
14
|
: undefined;
|
|
16
15
|
})
|
|
17
16
|
: creator(source => {
|
|
18
17
|
if (source === '') return;
|
|
19
18
|
const m = source.match(pattern);
|
|
20
|
-
return m && m[0].length > 0
|
|
21
|
-
&& !(not && source.slice(m[0].length, m[0].length + not.length) === not)
|
|
19
|
+
return m && (!mustConsume || m[0].length > 0)
|
|
22
20
|
? [[m[0]], source.slice(m[0].length)]
|
|
23
21
|
: undefined;
|
|
24
22
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { undefined } from 'spica/global';
|
|
2
2
|
import { TextParser, TxtParser, LinebreakParser } from '../source';
|
|
3
|
-
import {
|
|
4
|
-
import { union, focus, creator, fmap } from '../../combinator';
|
|
3
|
+
import { union, focus, creator } from '../../combinator';
|
|
5
4
|
import { str } from './str';
|
|
6
5
|
import { html } from 'typed-dom';
|
|
7
6
|
|
|
@@ -18,6 +17,9 @@ export const text: TextParser = creator((source, context) => {
|
|
|
18
17
|
return [[source], ''];
|
|
19
18
|
case 0:
|
|
20
19
|
switch (source[0]) {
|
|
20
|
+
case '\x7F':
|
|
21
|
+
assert(source[1] === '\\');
|
|
22
|
+
return [[], source.slice(1)];
|
|
21
23
|
case '\\':
|
|
22
24
|
switch (source[1]) {
|
|
23
25
|
case undefined:
|
|
@@ -66,11 +68,11 @@ export const text: TextParser = creator((source, context) => {
|
|
|
66
68
|
|
|
67
69
|
export const txt: TxtParser = union([
|
|
68
70
|
text,
|
|
69
|
-
]) as
|
|
71
|
+
]) as TxtParser;
|
|
70
72
|
|
|
71
|
-
export const linebreak: LinebreakParser =
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
export const linebreak: LinebreakParser = focus('\n', union([
|
|
74
|
+
text,
|
|
75
|
+
])) as LinebreakParser;
|
|
74
76
|
|
|
75
77
|
export function isAlphanumeric(char: string): boolean {
|
|
76
78
|
assert(char.length === 1);
|
|
@@ -3,6 +3,7 @@ import { creator } from '../../combinator';
|
|
|
3
3
|
import { separator, nonWhitespace, nonAlphanumeric, isAlphanumeric } from './text';
|
|
4
4
|
|
|
5
5
|
export const unescsource: UnescapableSourceParser = creator(source => {
|
|
6
|
+
assert(source[0] !== '\x7F');
|
|
6
7
|
if (source === '') return;
|
|
7
8
|
const i = source.search(separator);
|
|
8
9
|
switch (i) {
|
package/src/parser/util.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { undefined } from 'spica/global';
|
|
2
2
|
import { MarkdownParser } from '../../markdown';
|
|
3
3
|
import { Parser, eval } from '../combinator/data/parser';
|
|
4
|
-
import { union, verify, convert } from '../combinator';
|
|
4
|
+
import { union, some, verify, clear, convert, trim } from '../combinator';
|
|
5
5
|
import { comment } from './inline/comment';
|
|
6
6
|
import { htmlentity } from './inline/htmlentity';
|
|
7
|
-
import {
|
|
7
|
+
import { linebreak, unescsource, str } from './source';
|
|
8
|
+
import { push, pop } from 'spica/array';
|
|
8
9
|
|
|
9
10
|
// https://dev.w3.org/html5/html-author/charref
|
|
10
11
|
const invisibleHTMLEntityNames = [
|
|
@@ -41,22 +42,20 @@ const invisibleHTMLEntityNames = [
|
|
|
41
42
|
'InvisibleComma',
|
|
42
43
|
'ic',
|
|
43
44
|
];
|
|
44
|
-
const blankline = new RegExp(String.raw`^(?!\n|$)(?:\\?\s|&(?:${invisibleHTMLEntityNames.join('|')})
|
|
45
|
+
const blankline = new RegExp(String.raw`^(?!\n|$)(?:\\?\s|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr>)*\\?(?:\n|$)`, 'gm');
|
|
45
46
|
|
|
46
|
-
export function visualize<P extends Parser<HTMLElement | string>>(parser: P
|
|
47
|
-
export function visualize<T extends HTMLElement | string>(parser: Parser<T
|
|
47
|
+
export function visualize<P extends Parser<HTMLElement | string>>(parser: P): P;
|
|
48
|
+
export function visualize<T extends HTMLElement | string>(parser: Parser<T>): Parser<T> {
|
|
48
49
|
return justify(union([
|
|
49
50
|
verify(parser, (ns, rest, context) => !rest && hasVisible(ns, context)),
|
|
50
|
-
(
|
|
51
|
+
trim(some(union([clear(str('\x7F\\')), linebreak, unescsource]))),
|
|
51
52
|
]));
|
|
52
53
|
}
|
|
53
54
|
function justify<P extends Parser<unknown>>(parser: P): P;
|
|
54
55
|
function justify<T>(parser: Parser<T>): Parser<T> {
|
|
55
|
-
return convert(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return line.replace(/[\\&]/g, '\\$&');
|
|
59
|
-
}
|
|
56
|
+
return convert(
|
|
57
|
+
source => source.replace(blankline, line => line.replace(/[\\&<]/g, '\x7F\\$&')),
|
|
58
|
+
parser);
|
|
60
59
|
}
|
|
61
60
|
function hasVisible(
|
|
62
61
|
nodes: readonly (HTMLElement | string)[],
|
|
@@ -77,70 +76,71 @@ function hasVisible(
|
|
|
77
76
|
|
|
78
77
|
export function startTight<P extends Parser<unknown>>(parser: P): P;
|
|
79
78
|
export function startTight<T>(parser: Parser<T>): Parser<T> {
|
|
80
|
-
return (source, context) =>
|
|
81
|
-
|
|
82
|
-
switch (source[0]) {
|
|
83
|
-
case ' ':
|
|
84
|
-
case ' ':
|
|
85
|
-
case '\t':
|
|
86
|
-
case '\n':
|
|
87
|
-
return;
|
|
88
|
-
case '&':
|
|
89
|
-
switch (true) {
|
|
90
|
-
case source.length > 2
|
|
91
|
-
&& source[1] !== ' '
|
|
92
|
-
&& eval(htmlentity(source, context))?.[0].trimStart() == '':
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
break;
|
|
96
|
-
case '[':
|
|
97
|
-
switch (true) {
|
|
98
|
-
case source.length >= 7
|
|
99
|
-
&& source[1] === '#'
|
|
100
|
-
&& !!comment(source, context):
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
break;
|
|
104
|
-
case '<':
|
|
105
|
-
switch (true) {
|
|
106
|
-
case source.length >= 5
|
|
107
|
-
&& source[1] === 'w'
|
|
108
|
-
&& source.slice(0, 5) === '<wbr>':
|
|
109
|
-
case source.length >= 4
|
|
110
|
-
&& source[1] === 'b'
|
|
111
|
-
&& source.slice(0, 4) === '<br>':
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
return (source[0] === '\\' ? source[1] : source[0])?.trimStart()
|
|
79
|
+
return (source, context) =>
|
|
80
|
+
isStartTight(source, context)
|
|
117
81
|
? parser(source, context)
|
|
118
82
|
: undefined;
|
|
119
|
-
}
|
|
120
83
|
}
|
|
121
84
|
|
|
122
|
-
export function isStartTight(
|
|
85
|
+
export function isStartTight(source: string, context: MarkdownParser.Context): boolean {
|
|
86
|
+
if (source === '') return true;
|
|
87
|
+
switch (source[0]) {
|
|
88
|
+
case ' ':
|
|
89
|
+
case ' ':
|
|
90
|
+
case '\t':
|
|
91
|
+
case '\n':
|
|
92
|
+
return false;
|
|
93
|
+
case '\\':
|
|
94
|
+
return source[1]?.trimStart() !== '';
|
|
95
|
+
case '&':
|
|
96
|
+
switch (true) {
|
|
97
|
+
case source.length > 2
|
|
98
|
+
&& source[1] !== ' '
|
|
99
|
+
&& eval(htmlentity(source, context))?.[0].trimStart() == '':
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
case '<':
|
|
104
|
+
switch (true) {
|
|
105
|
+
case source.length >= 5
|
|
106
|
+
&& source[1] === 'w'
|
|
107
|
+
&& source.slice(0, 5) === '<wbr>':
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
case '[':
|
|
112
|
+
switch (true) {
|
|
113
|
+
case source.length >= 7
|
|
114
|
+
&& source[1] === '#'
|
|
115
|
+
&& !!comment(source, context):
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return true;
|
|
119
|
+
default:
|
|
120
|
+
return source[0].trimStart() !== '';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export function verifyStartTight(nodes: readonly (HTMLElement | string)[]): boolean {
|
|
123
124
|
if (nodes.length === 0) return true;
|
|
124
|
-
return isVisible(nodes[0]
|
|
125
|
+
return isVisible(nodes[0]);
|
|
125
126
|
}
|
|
126
|
-
export function
|
|
127
|
+
export function verifyEndTight(nodes: readonly (HTMLElement | string)[]): boolean {
|
|
127
128
|
if (nodes.length === 0) return true;
|
|
128
129
|
const last = nodes.length - 1;
|
|
129
130
|
return typeof nodes[last] === 'string' && (nodes[last] as string).length > 1
|
|
130
|
-
? isVisible(nodes[last],
|
|
131
|
-
isVisible(nodes[last],
|
|
132
|
-
: isVisible(nodes[last],
|
|
133
|
-
isVisible(nodes[last - 1],
|
|
131
|
+
? isVisible(nodes[last], -1) ||
|
|
132
|
+
isVisible(nodes[last], -2)
|
|
133
|
+
: isVisible(nodes[last], -1) || last === 0 ||
|
|
134
|
+
isVisible(nodes[last - 1], -1);
|
|
134
135
|
}
|
|
135
|
-
function isVisible(node: HTMLElement | string
|
|
136
|
-
assert(offset >= 0);
|
|
136
|
+
function isVisible(node: HTMLElement | string, position = 0): boolean {
|
|
137
137
|
if (!node) return false;
|
|
138
138
|
switch (typeof node) {
|
|
139
139
|
case 'string':
|
|
140
|
-
|
|
140
|
+
assert(node.length + position >= 0);
|
|
141
|
+
const char = node[position >= 0 ? position : node.length + position];
|
|
141
142
|
assert(char);
|
|
142
143
|
switch (char) {
|
|
143
|
-
case '':
|
|
144
144
|
case ' ':
|
|
145
145
|
case '\t':
|
|
146
146
|
case '\n':
|
|
@@ -163,6 +163,31 @@ function isVisible(node: HTMLElement | string | undefined, dir: 'start' | 'end',
|
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
export function trimEnd(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
|
|
167
|
+
assert(verifyStartTight(nodes));
|
|
168
|
+
const skip = nodes.length > 0 &&
|
|
169
|
+
typeof nodes[nodes.length - 1] === 'object' &&
|
|
170
|
+
nodes[nodes.length - 1]['className'] === 'indexer'
|
|
171
|
+
? [nodes.pop()!]
|
|
172
|
+
: [];
|
|
173
|
+
for (
|
|
174
|
+
let last = nodes[0];
|
|
175
|
+
nodes.length > 0 &&
|
|
176
|
+
!isVisible(last = nodes[nodes.length - 1], -1) &&
|
|
177
|
+
!(typeof last === 'object' && last.className === 'comment');
|
|
178
|
+
) {
|
|
179
|
+
assert(nodes.length > 0);
|
|
180
|
+
if (typeof last === 'string') {
|
|
181
|
+
const pos = last.trimEnd().length;
|
|
182
|
+
if (pos > 0) {
|
|
183
|
+
nodes[nodes.length - 1] = last.slice(0, pos);
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
nodes.pop();
|
|
188
|
+
}
|
|
189
|
+
return push(nodes, skip);
|
|
190
|
+
}
|
|
166
191
|
export function trimEndBR<T extends HTMLElement | string>(nodes: T[]): T[];
|
|
167
192
|
export function trimEndBR(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
|
|
168
193
|
if (nodes.length === 0) return nodes;
|