securemark 0.254.2 → 0.255.0
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 +4 -0
- package/dist/index.js +25 -4
- package/markdown.d.ts +1 -0
- package/package.json +1 -1
- package/src/parser/inline/html.test.ts +23 -21
- package/src/parser/inline/html.ts +25 -17
- package/src/parser/util.ts +13 -0
package/CHANGELOG.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! securemark v0.
|
|
1
|
+
/*! securemark v0.255.0 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
|
|
2
2
|
(function webpackUniversalModuleDefinition(root, factory) {
|
|
3
3
|
if(typeof exports === 'object' && typeof module === 'object')
|
|
4
4
|
module.exports = factory(require("DOMPurify"), require("Prism"));
|
|
@@ -6219,14 +6219,17 @@ const attrspecs = {
|
|
|
6219
6219
|
global_1.Object.setPrototypeOf(attrspecs, null);
|
|
6220
6220
|
global_1.Object.values(attrspecs).forEach(o => global_1.Object.setPrototypeOf(o, null));
|
|
6221
6221
|
exports.html = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('<', (0, combinator_1.validate)(/^<[a-z]+(?=[^\S\n]|>)/, (0, combinator_1.union)([(0, combinator_1.focus)('<wbr>', () => [[(0, dom_1.html)('wbr')], '']), (0, combinator_1.focus)( // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
|
|
6222
|
-
/^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/, source => [[source], '']), (0, combinator_1.match)(new RegExp(String.raw`^<(${TAGS.join('|')})(?=[^\S\n]|>)`), (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)(/^[^\S\n]*>/), true), (0,
|
|
6222
|
+
/^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/, source => [[source], '']), (0, combinator_1.match)(new RegExp(String.raw`^<(${TAGS.join('|')})(?=[^\S\n]|>)`), (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)(/^[^\S\n]*>/), true), (0, combinator_1.subsequence)([(0, combinator_1.focus)(/^[^\S\n]*\n/, (0, combinator_1.some)(inline_1.inline)), (0, combinator_1.some)((0, combinator_1.open)(/^\n?/, (0, combinator_1.some)(inline_1.inline, (0, util_1.blankWith)('\n', `</${tag}>`)), true))]), (0, source_1.str)(`</${tag}>`), true, ([as, bs = [], cs], rest) => [[elem(tag, as, bs, cs)], rest], ([as, bs = []], rest) => [[elem(tag, as, bs, [])], rest]), ([, tag]) => TAGS.indexOf(tag), [])), (0, combinator_1.match)(/^<([a-z]+)(?=[^\S\n]|>)/, (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)(/^[^\S\n]*>/), true), (0, combinator_1.subsequence)([(0, combinator_1.focus)(/^[^\S\n]*\n/, (0, combinator_1.some)(inline_1.inline)), (0, combinator_1.some)(inline_1.inline, `</${tag}>`)]), (0, source_1.str)(`</${tag}>`), true, ([as, bs = [], cs], rest) => [[elem(tag, as, bs, cs)], rest], ([as, bs = []], rest) => [[elem(tag, as, bs, [])], rest]), ([, tag]) => tag, new cache_1.Cache(10000)))])))));
|
|
6223
6223
|
exports.attribute = (0, combinator_1.union)([(0, source_1.str)(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|>)/)]); // https://developer.mozilla.org/en-US/docs/Web/HTML/Element
|
|
6224
6224
|
// [...document.querySelectorAll('tbody > tr > td:first-child')].map(el => el.textContent.slice(1, -1))
|
|
6225
6225
|
|
|
6226
6226
|
const TAGS = global_1.Object.freeze(["html", "base", "head", "link", "meta", "style", "title", "body", "address", "article", "aside", "footer", "header", "h1", "h2", "h3", "h4", "h5", "h6", "main", "nav", "section", "blockquote", "dd", "div", "dl", "dt", "figcaption", "figure", "hr", "li", "menu", "ol", "p", "pre", "ul", "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn", "em", "i", "kbd", "mark", "q", "rp", "rt", "ruby", "s", "samp", "small", "span", "strong", "sub", "sup", "time", "u", "var", "wbr", "area", "audio", "img", "map", "track", "video", "embed", "iframe", "object", "picture", "portal", "source", "svg", "math", "canvas", "noscript", "script", "del", "ins", "caption", "col", "colgroup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "button", "datalist", "fieldset", "form", "input", "label", "legend", "meter", "optgroup", "option", "output", "progress", "select", "textarea", "details", "dialog", "summary", "slot", "template", "acronym", "applet", "basefont", "bgsound", "big", "blink", "center", "content", "dir", "font", "frame", "frameset", "hgroup", "image", "keygen", "marquee", "menuitem", "nobr", "noembed", "noframes", "param", "plaintext", "rb", "rtc", "shadow", "spacer", "strike", "tt", "xmp"]);
|
|
6227
6227
|
|
|
6228
6228
|
function elem(tag, as, bs, cs) {
|
|
6229
|
-
if (!tags.includes(tag)) return invalid('tag', `Invalid HTML tag
|
|
6229
|
+
if (!tags.includes(tag)) return invalid('tag', `Invalid HTML tag name "${tag}"`, as, bs, cs);
|
|
6230
|
+
if (cs.length === 0) return invalid('tag', `Missing the closing HTML tag "</${tag}>"`, as, bs, cs);
|
|
6231
|
+
if (bs.length === 0) return invalid('content', `Missing the content`, as, bs, cs);
|
|
6232
|
+
if (!(0, util_1.isStartLooseNodes)(bs)) return invalid('content', `Missing the visible content in the same line`, as, bs, cs);
|
|
6230
6233
|
const attrs = attributes('html', [], attrspecs[tag], as.slice(1, -1));
|
|
6231
6234
|
return 'data-invalid-syntax' in attrs ? invalid('attribute', 'Invalid HTML attribute', as, bs, cs) : (0, dom_1.html)(tag, attrs, (0, dom_1.defrag)(bs));
|
|
6232
6235
|
}
|
|
@@ -7773,7 +7776,7 @@ exports.unescsource = (0, combinator_1.creator)(source => {
|
|
|
7773
7776
|
Object.defineProperty(exports, "__esModule", ({
|
|
7774
7777
|
value: true
|
|
7775
7778
|
}));
|
|
7776
|
-
exports.stringify = exports.trimBlankEnd = exports.trimBlankStart = exports.trimBlank = exports.isStartTightNodes = exports.startTight = exports.startLoose = exports.visualize = exports.blankWith = exports.regBlankStart = void 0;
|
|
7779
|
+
exports.stringify = exports.trimBlankEnd = exports.trimBlankStart = exports.trimBlank = exports.isStartTightNodes = exports.isStartLooseNodes = exports.startTight = exports.startLoose = exports.visualize = exports.blankWith = exports.regBlankStart = void 0;
|
|
7777
7780
|
|
|
7778
7781
|
const global_1 = __webpack_require__(4128);
|
|
7779
7782
|
|
|
@@ -7877,6 +7880,24 @@ const isStartTight = (0, memoize_1.reduce)((source, context, except) => {
|
|
|
7877
7880
|
}
|
|
7878
7881
|
}, (source, _, except = '') => `${source}\x1E${except}`);
|
|
7879
7882
|
|
|
7883
|
+
function isStartLooseNodes(nodes) {
|
|
7884
|
+
if (nodes.length === 0) return true;
|
|
7885
|
+
|
|
7886
|
+
for (let i = 0; i < nodes.length; ++i) {
|
|
7887
|
+
const node = nodes[i];
|
|
7888
|
+
if (isVisible(node)) return true;
|
|
7889
|
+
|
|
7890
|
+
if (typeof node === 'object') {
|
|
7891
|
+
if (node.tagName === 'BR') break;
|
|
7892
|
+
if (node.className === 'linebreak') break;
|
|
7893
|
+
}
|
|
7894
|
+
}
|
|
7895
|
+
|
|
7896
|
+
return false;
|
|
7897
|
+
}
|
|
7898
|
+
|
|
7899
|
+
exports.isStartLooseNodes = isStartLooseNodes;
|
|
7900
|
+
|
|
7880
7901
|
function isStartTightNodes(nodes) {
|
|
7881
7902
|
if (nodes.length === 0) return true;
|
|
7882
7903
|
return isVisible(nodes[0], 0);
|
package/markdown.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -7,12 +7,12 @@ describe('Unit: parser/inline/html', () => {
|
|
|
7
7
|
const parser = (source: string) => some(html)(source, {});
|
|
8
8
|
|
|
9
9
|
it('xss', () => {
|
|
10
|
-
assert.deepStrictEqual(inspect(parser('<script>')),
|
|
11
|
-
assert.deepStrictEqual(inspect(parser('<script>alert()<script>')),
|
|
10
|
+
assert.deepStrictEqual(inspect(parser('<script>')), [['<span class="invalid"><script></span>'], '']);
|
|
11
|
+
assert.deepStrictEqual(inspect(parser('<script>alert()<script>')), [['<span class="invalid"><script>alert<span class="paren">()</span><span class="invalid"><script></span></span>'], '']);
|
|
12
12
|
assert.deepStrictEqual(inspect(parser('<script>alert()</script>')), [['<span class="invalid"><script>alert<span class="paren">()</span></script></span>'], '']);
|
|
13
|
-
assert.deepStrictEqual(inspect(parser('<script src="\\""></script>')),
|
|
14
|
-
assert.deepStrictEqual(inspect(parser('<bdi onclick="alert()">')),
|
|
15
|
-
assert.deepStrictEqual(inspect(parser('<bdi onclick="alert()"></bdi>')),
|
|
13
|
+
assert.deepStrictEqual(inspect(parser('<script src="\\""></script>')), [['<span class="invalid"><script src="\\""></script></span>'], '']);
|
|
14
|
+
assert.deepStrictEqual(inspect(parser('<bdi onclick="alert()">')), [['<span class="invalid"><bdi onclick="alert()"></span>'], '']);
|
|
15
|
+
assert.deepStrictEqual(inspect(parser('<bdi onclick="alert()"></bdi>')), [['<span class="invalid"><bdi onclick="alert()"></bdi></span>'], '']);
|
|
16
16
|
assert.deepStrictEqual(inspect(parser('<bdi onclick="alert()">a</bdi>')), [['<span class="invalid"><bdi onclick="alert()">a</bdi></span>'], '']);
|
|
17
17
|
assert.deepStrictEqual(inspect(parser('<bdi><bdi onclick="alert()">a</bdi></bdi>')), [['<bdi><span class="invalid"><bdi onclick="alert()">a</bdi></span></bdi>'], '']);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser('<bdo dir="rtl\\"><">a</bdo>')), [['<span class="invalid"><bdo dir="rtl\\"><">a</bdo></span>'], '']);
|
|
@@ -25,17 +25,19 @@ describe('Unit: parser/inline/html', () => {
|
|
|
25
25
|
assert.deepStrictEqual(inspect(parser('<a,b>')), undefined);
|
|
26
26
|
assert.deepStrictEqual(inspect(parser('<a, b>')), undefined);
|
|
27
27
|
assert.deepStrictEqual(inspect(parser('<T>')), undefined);
|
|
28
|
-
assert.deepStrictEqual(inspect(parser('<bdi>z')),
|
|
29
|
-
assert.deepStrictEqual(inspect(parser('<bdi></bdi>')),
|
|
30
|
-
assert.deepStrictEqual(inspect(parser('<bdi> </bdi>')),
|
|
31
|
-
assert.deepStrictEqual(inspect(parser('<bdi
|
|
32
|
-
assert.deepStrictEqual(inspect(parser('<bdi
|
|
33
|
-
assert.deepStrictEqual(inspect(parser('<bdi
|
|
34
|
-
assert.deepStrictEqual(inspect(parser('<bdi
|
|
35
|
-
assert.deepStrictEqual(inspect(parser('<bdi
|
|
36
|
-
assert.deepStrictEqual(inspect(parser('<bdi
|
|
37
|
-
assert.deepStrictEqual(inspect(parser('<bdi>
|
|
38
|
-
assert.deepStrictEqual(inspect(parser('<bdi
|
|
28
|
+
assert.deepStrictEqual(inspect(parser('<bdi>z')), [['<span class="invalid"><bdi>z</span>'], '']);
|
|
29
|
+
assert.deepStrictEqual(inspect(parser('<bdi></bdi>')), [['<span class="invalid"><bdi></bdi></span>'], '']);
|
|
30
|
+
assert.deepStrictEqual(inspect(parser('<bdi> </bdi>')), [['<span class="invalid"><bdi> </bdi></span>'], '']);
|
|
31
|
+
assert.deepStrictEqual(inspect(parser('<bdi> \n</bdi>')), [['<span class="invalid"><bdi><br></bdi></span>'], '']);
|
|
32
|
+
assert.deepStrictEqual(inspect(parser('<bdi> \na</bdi>')), [['<span class="invalid"><bdi><br>a</bdi></span>'], '']);
|
|
33
|
+
assert.deepStrictEqual(inspect(parser('<bdi>\\ </bdi>')), [['<span class="invalid"><bdi> </bdi></span>'], '']);
|
|
34
|
+
assert.deepStrictEqual(inspect(parser('<bdi>	</bdi>')), [['<span class="invalid"><bdi>\t</bdi></span>'], '']);
|
|
35
|
+
assert.deepStrictEqual(inspect(parser('<bdi><wbr></bdi>')), [['<span class="invalid"><bdi><wbr></bdi></span>'], '']);
|
|
36
|
+
assert.deepStrictEqual(inspect(parser('<bdi>\n</bdi>')), [['<span class="invalid"><bdi><br></bdi></span>'], '']);
|
|
37
|
+
assert.deepStrictEqual(inspect(parser('<bdi>\na</bdi>')), [['<span class="invalid"><bdi><br>a</bdi></span>'], '']);
|
|
38
|
+
assert.deepStrictEqual(inspect(parser('<bdi>\\\na</bdi>')), [['<span class="invalid"><bdi><span class="linebreak"> </span>a</bdi></span>'], '']);
|
|
39
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a')), [['<span class="invalid"><bdi>a</span>'], '']);
|
|
40
|
+
assert.deepStrictEqual(inspect(parser('<bdi>a</BDO>')), [['<span class="invalid"><bdi>a</BDO></span>'], '']);
|
|
39
41
|
assert.deepStrictEqual(inspect(parser('<BDI>a</BDI>')), undefined);
|
|
40
42
|
assert.deepStrictEqual(inspect(parser('<BDI>a</bdo>')), undefined);
|
|
41
43
|
assert.deepStrictEqual(inspect(parser('</bdi>')), undefined);
|
|
@@ -43,11 +45,11 @@ describe('Unit: parser/inline/html', () => {
|
|
|
43
45
|
assert.deepStrictEqual(inspect(parser('<b><b><b>a</b></b></b>')), [['<span class="invalid"><b><span class="invalid"><b><span class="invalid"><b>a</b></span></b></span></b></span>'], '']);
|
|
44
46
|
assert.deepStrictEqual(inspect(parser('<bdi><bdi><bdi>a</bdi></bdi></bdi>')), [['<bdi><bdi><bdi>a</bdi></bdi></bdi>'], '']);
|
|
45
47
|
assert.deepStrictEqual(inspect(parser('<x a="*b*"')), undefined);
|
|
46
|
-
assert.deepStrictEqual(inspect(parser('<x a="*b*">')),
|
|
47
|
-
assert.deepStrictEqual(inspect(parser('<x a="*b*">c')),
|
|
48
|
+
assert.deepStrictEqual(inspect(parser('<x a="*b*">')), [['<span class="invalid"><x a="*b*"></span>'], '']);
|
|
49
|
+
assert.deepStrictEqual(inspect(parser('<x a="*b*">c')), [['<span class="invalid"><x a="*b*">c</span>'], '']);
|
|
48
50
|
assert.deepStrictEqual(inspect(parser('<bdi a="*b*"')), undefined);
|
|
49
|
-
assert.deepStrictEqual(inspect(parser('<bdi a="*b*">')),
|
|
50
|
-
assert.deepStrictEqual(inspect(parser('<bdi a="*b*">c')),
|
|
51
|
+
assert.deepStrictEqual(inspect(parser('<bdi a="*b*">')), [['<span class="invalid"><bdi a="*b*"></span>'], '']);
|
|
52
|
+
assert.deepStrictEqual(inspect(parser('<bdi a="*b*">c')), [['<span class="invalid"><bdi a="*b*">c</span>'], '']);
|
|
51
53
|
assert.deepStrictEqual(inspect(parser('<bdi a b="*" *c*')), undefined);
|
|
52
54
|
assert.deepStrictEqual(inspect(parser('<bdi a b="*" *c*>')), undefined);
|
|
53
55
|
assert.deepStrictEqual(inspect(parser('<bdi a b="*" *c*>d</bdi>')), undefined);
|
|
@@ -80,7 +82,7 @@ describe('Unit: parser/inline/html', () => {
|
|
|
80
82
|
});
|
|
81
83
|
|
|
82
84
|
it('escape', () => {
|
|
83
|
-
assert.deepStrictEqual(inspect(parser('<a>')),
|
|
85
|
+
assert.deepStrictEqual(inspect(parser('<a>')), [['<span class="invalid"><a></span>'], '']);
|
|
84
86
|
assert.deepStrictEqual(inspect(parser('<bdi><a>a</a></bdi>')), [['<bdi><span class="invalid"><a>a</a></span></bdi>'], '']);
|
|
85
87
|
assert.deepStrictEqual(inspect(parser('<bdi>a<a>b</a>c</bdi>')), [['<bdi>a<span class="invalid"><a>b</a></span>c</bdi>'], '']);
|
|
86
88
|
assert.deepStrictEqual(inspect(parser('<img>')), [['<img'], '>']);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { undefined, Object } from 'spica/global';
|
|
2
2
|
import { HTMLParser } from '../inline';
|
|
3
|
-
import { union, some, validate, focus, creator, surround, open, match, lazy } from '../../combinator';
|
|
3
|
+
import { union, subsequence, some, validate, focus, creator, surround, open, match, lazy } from '../../combinator';
|
|
4
4
|
import { inline } from '../inline';
|
|
5
5
|
import { str } from '../source';
|
|
6
|
-
import {
|
|
6
|
+
import { isStartLooseNodes, blankWith } from '../util';
|
|
7
7
|
import { html as h, defrag } from 'typed-dom/dom';
|
|
8
8
|
import { memoize } from 'spica/memoize';
|
|
9
9
|
import { Cache } from 'spica/cache';
|
|
@@ -32,25 +32,31 @@ export const html: HTMLParser = lazy(() => creator(validate('<', validate(/^<[a-
|
|
|
32
32
|
([, tag]) =>
|
|
33
33
|
surround<HTMLParser.TagParser, string>(surround(
|
|
34
34
|
str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
(
|
|
40
|
-
|
|
35
|
+
subsequence([
|
|
36
|
+
focus(/^[^\S\n]*\n/, some(inline)),
|
|
37
|
+
some(open(/^\n?/, some(inline, blankWith('\n', `</${tag}>`)), true)),
|
|
38
|
+
]),
|
|
39
|
+
str(`</${tag}>`), true,
|
|
40
|
+
([as, bs = [], cs], rest) =>
|
|
41
|
+
[[elem(tag, as, bs, cs)], rest],
|
|
42
|
+
([as, bs = []], rest) =>
|
|
43
|
+
[[elem(tag, as, bs, [])], rest]),
|
|
41
44
|
([, tag]) => TAGS.indexOf(tag), [])),
|
|
42
45
|
match(
|
|
43
|
-
|
|
46
|
+
/^<([a-z]+)(?=[^\S\n]|>)/,
|
|
44
47
|
memoize(
|
|
45
48
|
([, tag]) =>
|
|
46
49
|
surround<HTMLParser.TagParser, string>(surround(
|
|
47
50
|
str(`<${tag}`), some(attribute), str(/^[^\S\n]*>/), true),
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
(
|
|
53
|
-
|
|
51
|
+
subsequence([
|
|
52
|
+
focus(/^[^\S\n]*\n/, some(inline)),
|
|
53
|
+
some(inline, `</${tag}>`),
|
|
54
|
+
]),
|
|
55
|
+
str(`</${tag}>`), true,
|
|
56
|
+
([as, bs = [], cs], rest) =>
|
|
57
|
+
[[elem(tag, as, bs, cs)], rest],
|
|
58
|
+
([as, bs = []], rest) =>
|
|
59
|
+
[[elem(tag, as, bs, [])], rest]),
|
|
54
60
|
([, tag]) => tag,
|
|
55
61
|
new Cache(10000))),
|
|
56
62
|
])))));
|
|
@@ -204,8 +210,10 @@ const TAGS = Object.freeze([
|
|
|
204
210
|
function elem(tag: string, as: string[], bs: (HTMLElement | string)[], cs: string[]): HTMLElement {
|
|
205
211
|
assert(as.length > 0);
|
|
206
212
|
assert(as[0][0] === '<' && as[as.length - 1].slice(-1) === '>');
|
|
207
|
-
|
|
208
|
-
if (
|
|
213
|
+
if (!tags.includes(tag)) return invalid('tag', `Invalid HTML tag name "${tag}"`, as, bs, cs);
|
|
214
|
+
if (cs.length === 0) return invalid('tag', `Missing the closing HTML tag "</${tag}>"`, as, bs, cs);
|
|
215
|
+
if (bs.length === 0) return invalid('content', `Missing the content`, as, bs, cs);
|
|
216
|
+
if (!isStartLooseNodes(bs)) return invalid('content', `Missing the visible content in the same line`, as, bs, cs);
|
|
209
217
|
const attrs = attributes('html', [], attrspecs[tag], as.slice(1, -1));
|
|
210
218
|
return 'data-invalid-syntax' in attrs
|
|
211
219
|
? invalid('attribute', 'Invalid HTML attribute', as, bs, cs)
|
package/src/parser/util.ts
CHANGED
|
@@ -101,6 +101,19 @@ const isStartTight = reduce((source: string, context: MarkdownParser.Context, ex
|
|
|
101
101
|
return source[0].trimStart() !== '';
|
|
102
102
|
}
|
|
103
103
|
}, (source, _, except = '') => `${source}\x1E${except}`);
|
|
104
|
+
|
|
105
|
+
export function isStartLooseNodes(nodes: readonly (HTMLElement | string)[]): boolean {
|
|
106
|
+
if (nodes.length === 0) return true;
|
|
107
|
+
for (let i = 0; i < nodes.length; ++i) {
|
|
108
|
+
const node = nodes[i];
|
|
109
|
+
if (isVisible(node)) return true;
|
|
110
|
+
if (typeof node === 'object') {
|
|
111
|
+
if (node.tagName === 'BR') break;
|
|
112
|
+
if (node.className === 'linebreak') break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
104
117
|
export function isStartTightNodes(nodes: readonly (HTMLElement | string)[]): boolean {
|
|
105
118
|
if (nodes.length === 0) return true;
|
|
106
119
|
return isVisible(nodes[0], 0);
|