securemark 0.224.1 → 0.226.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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.226.0
4
+
5
+ - Improve math parser.
6
+
7
+ ## 0.225.2
8
+
9
+ - Refactoring.
10
+
11
+ ## 0.225.1
12
+
13
+ - Refactoring.
14
+
15
+ ## 0.225.0
16
+
17
+ - Refine some parsers to disallow trailing whitespace.
18
+
3
19
  ## 0.224.1
4
20
 
5
21
  - Refactoring.
package/design.md CHANGED
@@ -69,7 +69,7 @@
69
69
 
70
70
  ### 自己完結性
71
71
 
72
- ドキュメントはすべて初期構成の閲覧環境で閲覧できなければならない。
72
+ ドキュメントはすべて初期(共通)構成の閲覧環境で閲覧できなければならない。
73
73
  よって閲覧に拡張機能等の追加が必要となってはならない
74
74
 
75
75
  ゆえにドキュメントはサードパーティツールなど自己完結性を損なうツールの使用をサポートしない。
@@ -139,7 +139,7 @@
139
139
 
140
140
  オートリンクは原則としてコピー&ペーストによる引用等を経た際にその解釈が変わってはならない。
141
141
 
142
- ゆえにハッシュタグ構文は構文が表示文字列と一致する構文であるオートリンク構文としてのみ構文化され、表示文字列からの構文の範囲の特定が不可能である通常の構文を持たない。
142
+ ゆえにハッシュタグ構文は構文が表示文字列と一致する構文であるオートリンク構文としてのみ構文化され、表示文字列からの構文の範囲の特定が不可能となる通常の構文を持たない。
143
143
 
144
144
  ### 自動附番
145
145
 
@@ -148,12 +148,6 @@
148
148
  ゆえに参照箇所に実体を記述する注釈構文および識別に文字列を使用する図表構文を採用し、その表示方法は任意とする。
149
149
  脚注構文は附番が手作業となり参照と実体の対応の管理が困難であるため不採用とし注釈構文により生成可能とする。
150
150
 
151
- ### 羅列的知識への非依存
152
-
153
- 構文はその使用のために羅列的知識を求めてはならない。
154
-
155
- ゆえに構文はHTMLエンティティの参照の有効性を検査しない。
156
-
157
151
  ### テンプレート対応
158
152
 
159
153
  ソーステキストは自身をテンプレートとして別のソーステキストを生成するための構文を使用できなければならない。
@@ -277,7 +271,7 @@ CodeMirrorが素では速いがVimModeでは数万文字程度でも耐え難く
277
271
  ### バックトラック
278
272
 
279
273
  SecuremarkのAnnotation構文に典型的であるようにスコープの生成規則を変える構文が存在する場合文脈の相違から解析結果を再利用不能な(`αA'β | αAB`)バックトラックが再帰的に生じ、このような言語を線形時間で解析することは基本的に不可能であり直ちに文脈を確定する任意の構文または記号の前に文脈の差異が生じない(`αAβ | αAB`)場合のみ再利用可能なバックトラックにより線形時間で解析できると思われる。
280
- このため線形時間で解析可能な文法に制約されるCommonMarkがこの制約を破らずこのような文脈不確定文法を導入することは不可能であり一例としてSecuremarkにおけるAnnotation構文のような文脈独立の構文とHTML構文のような構文の再帰または特定の包含関係での使用の禁止等の再帰制御の両立ができないという拡張性の限界が存在する。なおLink構文は自身を始めとするリンクを生成する構文を包含できないためリンクを生成するAnnotation構文との関係においてこの問題を生じない。
274
+ このため線形時間で解析可能な文法に制約されるCommonMarkがこの制約を破らずこのような文脈不確定文法を導入することは不可能であるという拡張性の限界が存在する。
281
275
 
282
276
  ### 標準化
283
277
 
@@ -285,8 +279,8 @@ Markdownのように自然言語と自身の文法を分離する汎用構造が
285
279
  このためCommonMarkは拡張構文を標準化した次期標準仕様との互換性を確保する役には立たずその有用性は構文の拡張を考慮しない範囲での効率的な実装方法の例示およびテストケースの集積による個別実装の支援ならびにその結果としての限定的互換性にとどまる。
286
280
  さらにCommonMarkは前述の解析時間の制約から拡張性が低く、高度な構文や機能の実装可能性を考慮していないことから拡張仕様において準拠すべき技術的正当性もない。
287
281
 
288
- よってMarkdownの標準化は後方互換性確保が不可能であることから発展性がなくスナップショット以上の意味を持たない。
289
- MarkdownはGFMのように最初から高機能で完成度の高い拡張不要な独自実装のほうが標準としての互換性を確保でき、構文に曖昧さがない形式言語と異なり最小限の標準仕様を策定し拡張していく通常の標準化方法が適さない特殊な言語である。
282
+ よってMarkdownの標準化は後方互換性確保が不可能であることから発展性がなくスナップショット以上の技術的意味を持たない。
283
+ MarkdownはGFMのように最初から高機能で完成度の高い拡張不要な独自実装のほうが標準としての互換性を確保でき、構文に曖昧さがない形式言語と異なりまず最小限の標準仕様を策定しのちに拡張していく通常の標準化方法が適さない特殊な言語である。
290
284
 
291
285
  ### トランスクルージョン
292
286
 
@@ -1,4 +1,4 @@
1
- /*! securemark v0.224.1 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED */
1
+ /*! securemark v0.226.0 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED */
2
2
  require = function () {
3
3
  function r(e, n, t) {
4
4
  function o(i, f) {
@@ -6966,7 +6966,7 @@ require = function () {
6966
6966
  const typed_dom_1 = _dereq_('typed-dom');
6967
6967
  const array_1 = _dereq_('spica/array');
6968
6968
  exports.deletion = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.surround)((0, source_1.str)('~~'), (0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, '~~')]), (0, source_1.str)('~~'), false, ([, bs], rest) => [
6969
- [(0, typed_dom_1.html)('del', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))],
6969
+ [(0, typed_dom_1.html)('del', (0, typed_dom_1.defrag)((0, util_1.trimNodeEndBR)(bs)))],
6970
6970
  rest
6971
6971
  ], ([as, bs], rest) => [
6972
6972
  (0, array_1.unshift)(as, bs),
@@ -6998,7 +6998,7 @@ require = function () {
6998
6998
  strong_1.strong,
6999
6999
  (0, combinator_1.some)(inline_1.inline, '*')
7000
7000
  ]))), (0, source_1.str)('*'), false, ([as, bs, cs], rest) => (0, util_1.isEndTightNodes)(bs) ? [
7001
- [(0, typed_dom_1.html)('em', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))],
7001
+ [(0, typed_dom_1.html)('em', (0, typed_dom_1.defrag)(bs))],
7002
7002
  rest
7003
7003
  ] : [
7004
7004
  (0, array_1.unshift)(as, bs),
@@ -7039,41 +7039,41 @@ require = function () {
7039
7039
  switch (cs[0]) {
7040
7040
  case '*':
7041
7041
  return (_a = (0, combinator_1.bind)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, '**')]), (ds, rest) => rest.slice(0, 2) === '**' && (0, util_1.isEndTightNodes)(ds) ? [
7042
- [(0, typed_dom_1.html)('strong', (0, array_1.unshift)([(0, typed_dom_1.html)('em', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))], (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(ds))))],
7042
+ [(0, typed_dom_1.html)('strong', (0, array_1.unshift)([(0, typed_dom_1.html)('em', (0, typed_dom_1.defrag)(bs))], (0, typed_dom_1.defrag)(ds)))],
7043
7043
  rest.slice(2)
7044
7044
  ] : [
7045
7045
  (0, array_1.unshift)([
7046
7046
  '**',
7047
- (0, typed_dom_1.html)('em', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))
7047
+ (0, typed_dom_1.html)('em', (0, typed_dom_1.defrag)(bs))
7048
7048
  ], ds),
7049
7049
  rest
7050
7050
  ])(rest, context)) !== null && _a !== void 0 ? _a : [
7051
7051
  [
7052
7052
  '**',
7053
- (0, typed_dom_1.html)('em', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))
7053
+ (0, typed_dom_1.html)('em', (0, typed_dom_1.defrag)(bs))
7054
7054
  ],
7055
7055
  rest
7056
7056
  ];
7057
7057
  case '**':
7058
7058
  return (_b = (0, combinator_1.bind)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, '*')]), (ds, rest) => rest.slice(0, 1) === '*' && (0, util_1.isEndTightNodes)(ds) ? [
7059
- [(0, typed_dom_1.html)('em', (0, array_1.unshift)([(0, typed_dom_1.html)('strong', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))], (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(ds))))],
7059
+ [(0, typed_dom_1.html)('em', (0, array_1.unshift)([(0, typed_dom_1.html)('strong', (0, typed_dom_1.defrag)(bs))], (0, typed_dom_1.defrag)(ds)))],
7060
7060
  rest.slice(1)
7061
7061
  ] : [
7062
7062
  (0, array_1.unshift)([
7063
7063
  '*',
7064
- (0, typed_dom_1.html)('strong', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))
7064
+ (0, typed_dom_1.html)('strong', (0, typed_dom_1.defrag)(bs))
7065
7065
  ], ds),
7066
7066
  rest
7067
7067
  ])(rest, context)) !== null && _b !== void 0 ? _b : [
7068
7068
  [
7069
7069
  '*',
7070
- (0, typed_dom_1.html)('strong', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))
7070
+ (0, typed_dom_1.html)('strong', (0, typed_dom_1.defrag)(bs))
7071
7071
  ],
7072
7072
  rest
7073
7073
  ];
7074
7074
  case '***':
7075
7075
  return [
7076
- [(0, typed_dom_1.html)('em', [(0, typed_dom_1.html)('strong', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))])],
7076
+ [(0, typed_dom_1.html)('em', [(0, typed_dom_1.html)('strong', (0, typed_dom_1.defrag)(bs))])],
7077
7077
  rest
7078
7078
  ];
7079
7079
  }
@@ -7441,11 +7441,11 @@ require = function () {
7441
7441
  return {};
7442
7442
  }
7443
7443
  })(), (0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), `</${ tag }>`)), `</${ tag }>`), (0, source_1.str)(`</${ tag }>`), false, ([as, bs, cs], rest, context) => [
7444
- [elem(tag, as, (0, util_1.trimEndBR)((0, typed_dom_1.defrag)(bs)), cs, context)],
7444
+ [elem(tag, as, (0, util_1.trimNodeEndBR)((0, typed_dom_1.defrag)(bs)), cs, context)],
7445
7445
  rest
7446
7446
  ])), ([, tag]) => tag)),
7447
7447
  (0, combinator_1.match)(/^(?=<([a-z]+)(?=[^\S\n]|>))/, (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.validate)(`<${ tag }`, `</${ tag }>`, (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${ tag }`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)('>'), true), (0, util_1.startLoose)((0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), `</${ tag }>`), `</${ tag }>`), (0, source_1.str)(`</${ tag }>`), false, ([as, bs, cs], rest) => [
7448
- [elem(tag, as, (0, util_1.trimEndBR)((0, typed_dom_1.defrag)(bs)), cs, {})],
7448
+ [elem(tag, as, (0, util_1.trimNodeEndBR)((0, typed_dom_1.defrag)(bs)), cs, {})],
7449
7449
  rest
7450
7450
  ], ([as, bs], rest) => as.length === 1 ? [
7451
7451
  (0, array_1.unshift)(as, bs),
@@ -7561,7 +7561,7 @@ require = function () {
7561
7561
  const typed_dom_1 = _dereq_('typed-dom');
7562
7562
  const array_1 = _dereq_('spica/array');
7563
7563
  exports.insertion = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.surround)((0, source_1.str)('++'), (0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, '++')]), (0, source_1.str)('++'), false, ([, bs], rest) => [
7564
- [(0, typed_dom_1.html)('ins', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))],
7564
+ [(0, typed_dom_1.html)('ins', (0, typed_dom_1.defrag)((0, util_1.trimNodeEndBR)(bs)))],
7565
7565
  rest
7566
7566
  ], ([as, bs], rest) => [
7567
7567
  (0, array_1.unshift)(as, bs),
@@ -7733,7 +7733,7 @@ require = function () {
7733
7733
  const typed_dom_1 = _dereq_('typed-dom');
7734
7734
  const array_1 = _dereq_('spica/array');
7735
7735
  exports.mark = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.surround)((0, source_1.str)('=='), (0, util_1.startTight)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, '==')])), (0, source_1.str)('=='), false, ([as, bs, cs], rest) => (0, util_1.isEndTightNodes)(bs) ? [
7736
- [(0, typed_dom_1.html)('mark', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))],
7736
+ [(0, typed_dom_1.html)('mark', (0, typed_dom_1.defrag)(bs))],
7737
7737
  rest
7738
7738
  ] : [
7739
7739
  (0, array_1.unshift)(as, bs),
@@ -7763,8 +7763,8 @@ require = function () {
7763
7763
  const typed_dom_1 = _dereq_('typed-dom');
7764
7764
  const disallowedCommand = /\\(?:begin|tiny|huge|large)(?![0-9a-z])/i;
7765
7765
  exports.math = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('$', '$', '\n', (0, combinator_1.rewrite)((0, combinator_1.union)([
7766
- (0, combinator_1.surround)('$', bracket, '$'),
7767
- (0, combinator_1.surround)('$', (0, combinator_1.verify)((0, source_1.str)(/^(?=[\\^_[(|]|[A-Za-z][0-9A-Za-z]*'*[ ~]?(?:\$|([\\^_(|:=<>])(?!\1)))(?:\\\$|[\x20-\x23\x25-\x7E])*/), util_1.isEndTightNodes), /^\$(?![0-9A-Za-z])/)
7766
+ (0, combinator_1.surround)('$', (0, combinator_1.verify)((0, source_1.str)(/^(?![\s{}#$%&]|\d+(?:[,.]\d+)*(?:[\s,.!?()[\]{}]|[^\x00-\x7F])|-[\da-z]|[a-z]+-)(?:\\\$|[\x20-\x23\x25-\x7E])*/i), util_1.isEndTightNodes), /^\$(?![0-9a-z])/i),
7767
+ (0, combinator_1.surround)('$', bracket, '$')
7768
7768
  ]), (source, {
7769
7769
  caches: {math: cache} = {}
7770
7770
  }) => {
@@ -7786,7 +7786,7 @@ require = function () {
7786
7786
  }))));
7787
7787
  const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.surround)('{', (0, combinator_1.some)((0, combinator_1.union)([
7788
7788
  bracket,
7789
- (0, combinator_1.some)(source_1.escsource, /^[{}]/)
7789
+ (0, combinator_1.some)(source_1.escsource, /^(?:[{}]|\\?\n)/)
7790
7790
  ])), '}', true)));
7791
7791
  },
7792
7792
  {
@@ -8051,11 +8051,6 @@ require = function () {
8051
8051
  const acc = [''];
8052
8052
  while (source !== '') {
8053
8053
  switch (source[0]) {
8054
- case ' ':
8055
- case '\u3000':
8056
- acc.push('');
8057
- source = source.slice(1);
8058
- continue;
8059
8054
  case '&': {
8060
8055
  const result = (0, htmlentity_1.htmlentity)(source, context);
8061
8056
  acc[acc.length - 1] += (0, parser_1.eval)(result, [source[0]])[0];
@@ -8063,6 +8058,11 @@ require = function () {
8063
8058
  continue;
8064
8059
  }
8065
8060
  default: {
8061
+ if (source[0].trimStart() === '') {
8062
+ acc.push('');
8063
+ source = source.slice(1);
8064
+ continue;
8065
+ }
8066
8066
  const result = (0, source_1.text)(source, context);
8067
8067
  acc[acc.length - 1] += (_a = (0, parser_1.eval)(result)[0]) !== null && _a !== void 0 ? _a : source.slice(0, source.length - (0, parser_1.exec)(result).length);
8068
8068
  source = (0, parser_1.exec)(result);
@@ -8123,7 +8123,7 @@ require = function () {
8123
8123
  (0, combinator_1.some)(inline_1.inline, '*'),
8124
8124
  (0, source_1.str)('*')
8125
8125
  ]), '**')), (0, source_1.str)('**'), false, ([as, bs, cs], rest) => (0, util_1.isEndTightNodes)(bs) ? [
8126
- [(0, typed_dom_1.html)('strong', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))],
8126
+ [(0, typed_dom_1.html)('strong', (0, typed_dom_1.defrag)(bs))],
8127
8127
  rest
8128
8128
  ] : [
8129
8129
  (0, array_1.unshift)(as, bs),
@@ -8642,6 +8642,7 @@ require = function () {
8642
8642
  Object.defineProperty(exports, '__esModule', { value: true });
8643
8643
  exports.escsource = void 0;
8644
8644
  const combinator_1 = _dereq_('../../combinator');
8645
+ const text_1 = _dereq_('./text');
8645
8646
  const separator = /[\s\x00-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]/;
8646
8647
  exports.escsource = (0, combinator_1.creator)(source => {
8647
8648
  if (source === '')
@@ -8661,9 +8662,11 @@ require = function () {
8661
8662
  source.slice(2)
8662
8663
  ];
8663
8664
  default:
8665
+ const b = source[0] !== '\n' && source[0].trimStart() === '';
8666
+ const i = b ? source.search(text_1.nonWhitespace) : 1;
8664
8667
  return [
8665
- [source.slice(0, 1)],
8666
- source.slice(1)
8668
+ [source.slice(0, i)],
8669
+ source.slice(i)
8667
8670
  ];
8668
8671
  }
8669
8672
  default:
@@ -8674,7 +8677,10 @@ require = function () {
8674
8677
  }
8675
8678
  });
8676
8679
  },
8677
- { '../../combinator': 30 }
8680
+ {
8681
+ '../../combinator': 30,
8682
+ './text': 135
8683
+ }
8678
8684
  ],
8679
8685
  133: [
8680
8686
  function (_dereq_, module, exports) {
@@ -8886,7 +8892,7 @@ require = function () {
8886
8892
  function (_dereq_, module, exports) {
8887
8893
  'use strict';
8888
8894
  Object.defineProperty(exports, '__esModule', { value: true });
8889
- exports.stringify = exports.trimEndBR = exports.trimNodeEnd = exports.trimNode = exports.isEndTightNodes = exports.isStartTightNodes = exports.startTight = exports.isStartLoose = exports.startLoose = exports.visualize = void 0;
8895
+ exports.stringify = exports.trimNodeEndBR = exports.trimNodeEnd = exports.trimNode = exports.isEndTightNodes = exports.isStartTightNodes = exports.startTight = exports.isStartLoose = exports.startLoose = exports.visualize = void 0;
8890
8896
  const global_1 = _dereq_('spica/global');
8891
8897
  const parser_1 = _dereq_('../combinator/data/parser');
8892
8898
  const combinator_1 = _dereq_('../combinator');
@@ -9020,17 +9026,21 @@ require = function () {
9020
9026
  function isEndTightNodes(nodes) {
9021
9027
  if (nodes.length === 0)
9022
9028
  return true;
9023
- const last = nodes.length - 1;
9024
- return typeof nodes[last] === 'string' && nodes[last].length > 1 ? isVisible(nodes[last], -1) || isVisible(nodes[last], -2) : isVisible(nodes[last], -1) || last === 0 || isVisible(nodes[last - 1], -1);
9029
+ for (let i = nodes.length; i--;) {
9030
+ const node = nodes[i];
9031
+ if (typeof node === 'object' && node.className === 'comment')
9032
+ continue;
9033
+ return isVisible(node, -1);
9034
+ }
9035
+ return false;
9025
9036
  }
9026
9037
  exports.isEndTightNodes = isEndTightNodes;
9027
- function isVisible(node, position) {
9028
- if (!node)
9029
- return false;
9038
+ function isVisible(node, strpos) {
9030
9039
  switch (typeof node) {
9031
9040
  case 'string':
9032
- const char = position === global_1.undefined ? node : node[position >= 0 ? position : node.length + position];
9041
+ const char = node && strpos !== global_1.undefined ? node[strpos >= 0 ? strpos : node.length + strpos] : node;
9033
9042
  switch (char) {
9043
+ case '':
9034
9044
  case ' ':
9035
9045
  case '\t':
9036
9046
  case '\n':
@@ -9057,26 +9067,31 @@ require = function () {
9057
9067
  }
9058
9068
  exports.trimNode = trimNode;
9059
9069
  function trimNodeStart(nodes) {
9060
- const skip = nodes.length > 0 && typeof nodes[nodes.length - 1] === 'object' && nodes[nodes.length - 1]['className'] === 'indexer' ? [nodes.pop()] : [];
9061
- for (let first = nodes[0]; nodes.length > 0 && !isVisible(first, 0) && !(typeof first === 'object' && first.className === 'comment');) {
9062
- if (typeof first === 'string') {
9063
- const pos = first.length - first.trimStart().length;
9070
+ for (let node = nodes[0]; nodes.length > 0 && !isVisible(node = nodes[0], 0);) {
9071
+ if (nodes.length === 1 && typeof node === 'object' && node.className === 'indexer')
9072
+ break;
9073
+ if (typeof node === 'object' && node.className === 'comment')
9074
+ break;
9075
+ if (typeof node === 'string') {
9076
+ const pos = node.length - node.trimStart().length;
9064
9077
  if (pos > 0) {
9065
- nodes[0] = first.slice(pos);
9078
+ nodes[0] = node.slice(pos);
9066
9079
  break;
9067
9080
  }
9068
9081
  }
9069
- nodes.pop();
9082
+ nodes.shift();
9070
9083
  }
9071
- return (0, array_1.push)(nodes, skip);
9084
+ return nodes;
9072
9085
  }
9073
9086
  function trimNodeEnd(nodes) {
9074
9087
  const skip = nodes.length > 0 && typeof nodes[nodes.length - 1] === 'object' && nodes[nodes.length - 1]['className'] === 'indexer' ? [nodes.pop()] : [];
9075
- for (let last = nodes[0]; nodes.length > 0 && !isVisible(last = nodes[nodes.length - 1], -1) && !(typeof last === 'object' && last.className === 'comment');) {
9076
- if (typeof last === 'string') {
9077
- const pos = last.trimEnd().length;
9088
+ for (let node = nodes[0]; nodes.length > 0 && !isVisible(node = nodes[nodes.length - 1], -1);) {
9089
+ if (typeof node === 'object' && node.className === 'comment')
9090
+ break;
9091
+ if (typeof node === 'string') {
9092
+ const pos = node.trimEnd().length;
9078
9093
  if (pos > 0) {
9079
- nodes[nodes.length - 1] = last.slice(0, pos);
9094
+ nodes[nodes.length - 1] = node.slice(0, pos);
9080
9095
  break;
9081
9096
  }
9082
9097
  }
@@ -9085,13 +9100,13 @@ require = function () {
9085
9100
  return (0, array_1.push)(nodes, skip);
9086
9101
  }
9087
9102
  exports.trimNodeEnd = trimNodeEnd;
9088
- function trimEndBR(nodes) {
9103
+ function trimNodeEndBR(nodes) {
9089
9104
  if (nodes.length === 0)
9090
9105
  return nodes;
9091
9106
  const node = nodes[nodes.length - 1];
9092
9107
  return typeof node === 'object' && node.tagName === 'BR' ? (0, array_1.pop)(nodes)[0] : nodes;
9093
9108
  }
9094
- exports.trimEndBR = trimEndBR;
9109
+ exports.trimNodeEndBR = trimNodeEndBR;
9095
9110
  function stringify(nodes) {
9096
9111
  let acc = '';
9097
9112
  for (let i = 0; i < nodes.length; ++i) {
package/markdown.d.ts CHANGED
@@ -988,6 +988,7 @@ export namespace MarkdownParser {
988
988
  Inline<'math'>,
989
989
  Parser<HTMLElement, Context, [
990
990
  SourceParser.StrParser,
991
+ MathParser.BracketParser,
991
992
  ]> {
992
993
  }
993
994
  export namespace MathParser {
package/package-lock.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.224.1",
3
+ "version": "0.226.0",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.224.1",
3
+ "version": "0.226.0",
4
4
  "description": "Secure markdown renderer working on browsers for user input data.",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/falsandtru/securemark",
@@ -23,6 +23,7 @@ export function focus<T>(scope: string | RegExp, parser: Parser<T>): Parser<T> {
23
23
  };
24
24
  }
25
25
 
26
+ //export function rewrite<T, C extends Ctx, D extends Parser<unknown, C>[]>(scope: Parser<unknown, C, D>, parser: Parser<T, C, never>): Parser<T, C, D>;
26
27
  export function rewrite<P extends Parser<unknown>>(scope: Parser<unknown, Context<P>>, parser: P): P;
27
28
  export function rewrite<T>(scope: Parser<unknown>, parser: Parser<T>): Parser<T> {
28
29
  assert(scope);
@@ -60,9 +60,6 @@ describe('Unit: parser/api/parse', () => {
60
60
  });
61
61
 
62
62
  it('linebreak', () => {
63
- assert.deepStrictEqual(
64
- [...parse('*a\n*\nb').children].map(el => el.outerHTML),
65
- ['<p><em>a</em><br>b</p>']);
66
63
  assert.deepStrictEqual(
67
64
  [...parse('\\ ').children].map(el => el.outerHTML),
68
65
  ['<p>\\</p>']);
@@ -32,8 +32,6 @@ describe('Unit: parser/inline/deletion', () => {
32
32
  assert.deepStrictEqual(inspect(parser('~~a\\\nb~~')), [['<del>a<span class="linebreak"> </span>b</del>'], '']);
33
33
  assert.deepStrictEqual(inspect(parser('~~\\~~~')), [['<del>~</del>'], '']);
34
34
  assert.deepStrictEqual(inspect(parser('~~a~~~')), [['<del>a</del>'], '~']);
35
- assert.deepStrictEqual(inspect(parser('~~~a~~')), [['<del>~a</del>'], '']);
36
- assert.deepStrictEqual(inspect(parser('~~~a~~~')), [['<del>~a</del>'], '~']);
37
35
  });
38
36
 
39
37
  it('nest', () => {
@@ -2,7 +2,7 @@ import { DeletionParser } from '../inline';
2
2
  import { union, some, creator, surround, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { str } from '../source';
5
- import { trimEndBR } from '../util';
5
+ import { trimNodeEndBR } from '../util';
6
6
  import { html, defrag } from 'typed-dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
@@ -10,5 +10,5 @@ export const deletion: DeletionParser = lazy(() => creator(surround(
10
10
  str('~~'),
11
11
  union([some(inline, '~~')]),
12
12
  str('~~'), false,
13
- ([, bs], rest) => [[html('del', defrag(trimEndBR(bs)))], rest],
13
+ ([, bs], rest) => [[html('del', defrag(trimNodeEndBR(bs)))], rest],
14
14
  ([as, bs], rest) => [unshift(as, bs), rest])));
@@ -9,9 +9,13 @@ describe('Unit: parser/inline/emphasis', () => {
9
9
  it('invalid', () => {
10
10
  assert.deepStrictEqual(inspect(parser('*')), undefined);
11
11
  assert.deepStrictEqual(inspect(parser('*a')), [['*', 'a'], '']);
12
- assert.deepStrictEqual(inspect(parser('*a *')), [['*', 'a', ' '], '*']);
12
+ assert.deepStrictEqual(inspect(parser('*a *')), [['*', 'a', ' '], '*']);
13
+ assert.deepStrictEqual(inspect(parser('*a\n*')), [['*', 'a', '<br>'], '*']);
14
+ assert.deepStrictEqual(inspect(parser('*a\\ *')), [['*', 'a', ' '], '*']);
15
+ assert.deepStrictEqual(inspect(parser('*a\\\n*')), [['*', 'a', '<span class="linebreak"> </span>'], '*']);
13
16
  assert.deepStrictEqual(inspect(parser('*a**b')), [['*', 'a', '**', 'b'], '']);
14
17
  assert.deepStrictEqual(inspect(parser('*a**b*')), [['*', 'a', '**', 'b', '*'], '']);
18
+ assert.deepStrictEqual(inspect(parser('*a [# b #]*')), [['*', 'a', ' ', '<sup class="comment" title="b"></sup>'], '*']);
15
19
  assert.deepStrictEqual(inspect(parser('* *')), undefined);
16
20
  assert.deepStrictEqual(inspect(parser('* a*')), undefined);
17
21
  assert.deepStrictEqual(inspect(parser('* a *')), undefined);
@@ -28,17 +32,16 @@ describe('Unit: parser/inline/emphasis', () => {
28
32
 
29
33
  it('basic', () => {
30
34
  assert.deepStrictEqual(inspect(parser('*a*')), [['<em>a</em>'], '']);
31
- assert.deepStrictEqual(inspect(parser('*a *')), [['<em>a </em>'], '']);
32
- assert.deepStrictEqual(inspect(parser('*a\n*')), [['<em>a</em>'], '']);
33
- assert.deepStrictEqual(inspect(parser('*a\\\n*')), [['<em>a<span class="linebreak"> </span></em>'], '']);
34
35
  assert.deepStrictEqual(inspect(parser('*ab*')), [['<em>ab</em>'], '']);
35
36
  assert.deepStrictEqual(inspect(parser('*a\nb*')), [['<em>a<br>b</em>'], '']);
36
37
  assert.deepStrictEqual(inspect(parser('*a\\\nb*')), [['<em>a<span class="linebreak"> </span>b</em>'], '']);
37
38
  assert.deepStrictEqual(inspect(parser('*a**')), [['<em>a</em>'], '*']);
38
- assert.deepStrictEqual(inspect(parser('*a**b**c*')), [['<em>a<strong>b</strong>c</em>'], '']);
39
39
  });
40
40
 
41
41
  it('nest', () => {
42
+ assert.deepStrictEqual(inspect(parser('*a**b**c*')), [['<em>a<strong>b</strong>c</em>'], '']);
43
+ assert.deepStrictEqual(inspect(parser('*a**b**c*d')), [['<em>a<strong>b</strong>c</em>'], 'd']);
44
+ assert.deepStrictEqual(inspect(parser('*a[# b #]*')), [['<em>a<sup class="comment" title="b"></sup></em>'], '']);
42
45
  assert.deepStrictEqual(inspect(parser('*`a`*')), [['<em><code data-src="`a`">a</code></em>'], '']);
43
46
  assert.deepStrictEqual(inspect(parser('*<small>*')), [['<em>&lt;small&gt;</em>'], '']);
44
47
  assert.deepStrictEqual(inspect(parser('*(*a*)*')), [['<em><span class="paren">(<em>a</em>)</span></em>'], '']);
@@ -3,7 +3,7 @@ import { union, some, creator, surround, close, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { strong } from './strong';
5
5
  import { str } from '../source';
6
- import { startTight, isEndTightNodes, trimEndBR } from '../util';
6
+ import { startTight, isEndTightNodes } from '../util';
7
7
  import { html, defrag } from 'typed-dom';
8
8
  import { unshift } from 'spica/array';
9
9
 
@@ -13,6 +13,6 @@ export const emphasis: EmphasisParser = lazy(() => creator(surround(close(
13
13
  str('*'), false,
14
14
  ([as, bs, cs], rest) =>
15
15
  isEndTightNodes(bs)
16
- ? [[html('em', defrag(trimEndBR(bs)))], rest]
16
+ ? [[html('em', defrag(bs))], rest]
17
17
  : [unshift(as, bs), cs[0] + rest],
18
18
  ([as, bs], rest) => [unshift(as, bs), rest])));
@@ -2,7 +2,7 @@ import { EmStrongParser } from '../inline';
2
2
  import { union, some, creator, surround, lazy, bind } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { str } from '../source';
5
- import { startTight, isEndTightNodes, trimEndBR } from '../util';
5
+ import { startTight, isEndTightNodes } from '../util';
6
6
  import { html, defrag } from 'typed-dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
@@ -18,19 +18,19 @@ export const emstrong: EmStrongParser = lazy(() => creator(surround(
18
18
  union([some(inline, '**')]),
19
19
  (ds, rest) =>
20
20
  rest.slice(0, 2) === '**' && isEndTightNodes(ds)
21
- ? [[html('strong', unshift([html('em', defrag(trimEndBR(bs)))], defrag(trimEndBR(ds))))], rest.slice(2)]
22
- : [unshift(['**', html('em', defrag(trimEndBR(bs)))], ds), rest])
23
- (rest, context) ?? [['**', html('em', defrag(trimEndBR(bs)))], rest];
21
+ ? [[html('strong', unshift([html('em', defrag(bs))], defrag(ds)))], rest.slice(2)]
22
+ : [unshift(['**', html('em', defrag(bs))], ds), rest])
23
+ (rest, context) ?? [['**', html('em', defrag(bs))], rest];
24
24
  case '**':
25
25
  return bind<EmStrongParser>(
26
26
  union([some(inline, '*')]),
27
27
  (ds, rest) =>
28
28
  rest.slice(0, 1) === '*' && isEndTightNodes(ds)
29
- ? [[html('em', unshift([html('strong', defrag(trimEndBR(bs)))], defrag(trimEndBR(ds))))], rest.slice(1)]
30
- : [unshift(['*', html('strong', defrag(trimEndBR(bs)))], ds), rest])
31
- (rest, context) ?? [['*', html('strong', defrag(trimEndBR(bs)))], rest];
29
+ ? [[html('em', unshift([html('strong', defrag(bs))], defrag(ds)))], rest.slice(1)]
30
+ : [unshift(['*', html('strong', defrag(bs))], ds), rest])
31
+ (rest, context) ?? [['*', html('strong', defrag(bs))], rest];
32
32
  case '***':
33
- return [[html('em', [html('strong', defrag(trimEndBR(bs)))])], rest];
33
+ return [[html('em', [html('strong', defrag(bs))])], rest];
34
34
  }
35
35
  assert(false);
36
36
  },
@@ -5,7 +5,7 @@ import { HTMLParser } from '../inline';
5
5
  import { union, some, validate, context, creator, surround, match, lazy } from '../../combinator';
6
6
  import { inline } from '../inline';
7
7
  import { str } from '../source';
8
- import { startLoose, trimEndBR } from '../util';
8
+ import { startLoose, trimNodeEndBR } from '../util';
9
9
  import { html as h, defrag } from 'typed-dom';
10
10
  import { memoize } from 'spica/memoize';
11
11
  import { Cache } from 'spica/cache';
@@ -66,7 +66,7 @@ export const html: HTMLParser = lazy(() => creator(validate('<', '>', '\n', vali
66
66
  some(union([inline]), `</${tag}>`)), `</${tag}>`),
67
67
  str(`</${tag}>`), false,
68
68
  ([as, bs, cs], rest, context) =>
69
- [[elem(tag, as, trimEndBR(defrag(bs)), cs, context)], rest])),
69
+ [[elem(tag, as, trimNodeEndBR(defrag(bs)), cs, context)], rest])),
70
70
  ([, tag]) => tag)),
71
71
  match(
72
72
  /^(?=<([a-z]+)(?=[^\S\n]|>))/,
@@ -78,7 +78,7 @@ export const html: HTMLParser = lazy(() => creator(validate('<', '>', '\n', vali
78
78
  startLoose(some(union([inline]), `</${tag}>`), `</${tag}>`),
79
79
  str(`</${tag}>`), false,
80
80
  ([as, bs, cs], rest) =>
81
- [[elem(tag, as, trimEndBR(defrag(bs)), cs, {})], rest],
81
+ [[elem(tag, as, trimNodeEndBR(defrag(bs)), cs, {})], rest],
82
82
  ([as, bs], rest) =>
83
83
  as.length === 1
84
84
  ? [unshift(as, bs), rest]
@@ -32,8 +32,6 @@ describe('Unit: parser/inline/insertion', () => {
32
32
  assert.deepStrictEqual(inspect(parser('++a\\\nb++')), [['<ins>a<span class="linebreak"> </span>b</ins>'], '']);
33
33
  assert.deepStrictEqual(inspect(parser('++\\+++')), [['<ins>+</ins>'], '']);
34
34
  assert.deepStrictEqual(inspect(parser('++a+++')), [['<ins>a</ins>'], '+']);
35
- assert.deepStrictEqual(inspect(parser('+++a++')), [['<ins>+a</ins>'], '']);
36
- assert.deepStrictEqual(inspect(parser('+++a+++')), [['<ins>+a</ins>'], '+']);
37
35
  });
38
36
 
39
37
  it('nest', () => {
@@ -2,7 +2,7 @@ import { InsertionParser } from '../inline';
2
2
  import { union, some, creator, surround, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { str } from '../source';
5
- import { trimEndBR } from '../util';
5
+ import { trimNodeEndBR } from '../util';
6
6
  import { html, defrag } from 'typed-dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
@@ -10,5 +10,5 @@ export const insertion: InsertionParser = lazy(() => creator(surround(
10
10
  str('++'),
11
11
  union([some(inline, '++')]),
12
12
  str('++'), false,
13
- ([, bs], rest) => [[html('ins', defrag(trimEndBR(bs)))], rest],
13
+ ([, bs], rest) => [[html('ins', defrag(trimNodeEndBR(bs)))], rest],
14
14
  ([as, bs], rest) => [unshift(as, bs), rest])));
@@ -12,7 +12,11 @@ describe('Unit: parser/inline/mark', () => {
12
12
  assert.deepStrictEqual(inspect(parser('==')), undefined);
13
13
  assert.deepStrictEqual(inspect(parser('==a')), [['==', 'a'], '']);
14
14
  assert.deepStrictEqual(inspect(parser('==a=')), [['==', 'a', '='], '']);
15
- assert.deepStrictEqual(inspect(parser('==a ==')), [['==', 'a', ' '], '==']);
15
+ assert.deepStrictEqual(inspect(parser('==a ==')), [['==', 'a', ' '], '==']);
16
+ assert.deepStrictEqual(inspect(parser('==a\n==')), [['==', 'a', '<br>'], '==']);
17
+ assert.deepStrictEqual(inspect(parser('==a\\ ==')), [['==', 'a', ' '], '==']);
18
+ assert.deepStrictEqual(inspect(parser('==a\\\n==')), [['==', 'a', '<span class="linebreak"> </span>'], '==']);
19
+ assert.deepStrictEqual(inspect(parser('==a [# b #]==')), [['==', 'a', ' ', '<sup class="comment" title="b"></sup>'], '==']);
16
20
  assert.deepStrictEqual(inspect(parser('== ==')), undefined);
17
21
  assert.deepStrictEqual(inspect(parser('== a==')), undefined);
18
22
  assert.deepStrictEqual(inspect(parser('== a ==')), undefined);
@@ -26,17 +30,14 @@ describe('Unit: parser/inline/mark', () => {
26
30
  it('basic', () => {
27
31
  assert.deepStrictEqual(inspect(parser('==a==')), [['<mark>a</mark>'], '']);
28
32
  assert.deepStrictEqual(inspect(parser('==ab==')), [['<mark>ab</mark>'], '']);
29
- assert.deepStrictEqual(inspect(parser('==a ==')), [['<mark>a </mark>'], '']);
30
- assert.deepStrictEqual(inspect(parser('==a\n==')), [['<mark>a</mark>'], '']);
31
33
  assert.deepStrictEqual(inspect(parser('==a\nb==')), [['<mark>a<br>b</mark>'], '']);
32
34
  assert.deepStrictEqual(inspect(parser('==a\\\nb==')), [['<mark>a<span class="linebreak"> </span>b</mark>'], '']);
33
35
  assert.deepStrictEqual(inspect(parser('==\\===')), [['<mark>=</mark>'], '']);
34
36
  assert.deepStrictEqual(inspect(parser('==a===')), [['<mark>a</mark>'], '=']);
35
- assert.deepStrictEqual(inspect(parser('===a==')), [['<mark>=a</mark>'], '']);
36
- assert.deepStrictEqual(inspect(parser('===a===')), [['<mark>=a</mark>'], '=']);
37
37
  });
38
38
 
39
39
  it('nest', () => {
40
+ assert.deepStrictEqual(inspect(parser('==a[# b #]==')), [['<mark>a<sup class="comment" title="b"></sup></mark>'], '']);
40
41
  assert.deepStrictEqual(inspect(parser('==*==a==*==')), [['<mark><em><mark>a</mark></em></mark>'], '']);
41
42
  });
42
43
 
@@ -2,7 +2,7 @@ import { MarkParser } from '../inline';
2
2
  import { union, some, creator, surround, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { str } from '../source';
5
- import { startTight, isEndTightNodes, trimEndBR } from '../util';
5
+ import { startTight, isEndTightNodes } from '../util';
6
6
  import { html, defrag } from 'typed-dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
@@ -12,6 +12,6 @@ export const mark: MarkParser = lazy(() => creator(surround(
12
12
  str('=='), false,
13
13
  ([as, bs, cs], rest) =>
14
14
  isEndTightNodes(bs)
15
- ? [[html('mark', defrag(trimEndBR(bs)))], rest]
15
+ ? [[html('mark', defrag(bs))], rest]
16
16
  : [unshift(as, bs), cs[0] + rest],
17
17
  ([as, bs], rest) => [unshift(as, bs), rest])));
@@ -11,31 +11,29 @@ describe('Unit: parser/inline/math', () => {
11
11
  assert.deepStrictEqual(inspect(parser('$')), undefined);
12
12
  assert.deepStrictEqual(inspect(parser('$$')), undefined);
13
13
  assert.deepStrictEqual(inspect(parser('$$$')), undefined);
14
- assert.deepStrictEqual(inspect(parser('$0$')), undefined);
15
- assert.deepStrictEqual(inspect(parser('$0-$')), undefined);
16
- assert.deepStrictEqual(inspect(parser('$0-$1')), undefined);
17
- assert.deepStrictEqual(inspect(parser('$0 - $1')), undefined);
18
- assert.deepStrictEqual(inspect(parser('$0+$1')), undefined);
19
- assert.deepStrictEqual(inspect(parser('$0 + $1')), undefined);
20
- assert.deepStrictEqual(inspect(parser('$0-1$')), undefined);
21
- assert.deepStrictEqual(inspect(parser('$0+1$')), undefined);
14
+ assert.deepStrictEqual(inspect(parser('$0 $')), undefined);
15
+ assert.deepStrictEqual(inspect(parser('$0.$')), undefined);
16
+ assert.deepStrictEqual(inspect(parser('$1,000.99.$')), undefined);
17
+ assert.deepStrictEqual(inspect(parser('$0(a)$')), undefined);
18
+ assert.deepStrictEqual(inspect(parser('$0[a]$')), undefined);
19
+ assert.deepStrictEqual(inspect(parser('$0{a}$')), undefined);
20
+ assert.deepStrictEqual(inspect(parser('$0 - $')), undefined);
21
+ assert.deepStrictEqual(inspect(parser('$0 + $')), undefined);
22
22
  assert.deepStrictEqual(inspect(parser('$-0$')), undefined);
23
23
  assert.deepStrictEqual(inspect(parser('$-0$-1')), undefined);
24
24
  assert.deepStrictEqual(inspect(parser('$-a$')), undefined);
25
25
  assert.deepStrictEqual(inspect(parser('$-a$-b')), undefined);
26
+ assert.deepStrictEqual(inspect(parser('$a $')), undefined);
26
27
  assert.deepStrictEqual(inspect(parser('$a-b$')), undefined);
27
28
  assert.deepStrictEqual(inspect(parser('$a-b$c-d')), undefined);
28
- assert.deepStrictEqual(inspect(parser('$a+b$')), undefined);
29
- assert.deepStrictEqual(inspect(parser('$a*b$')), undefined);
30
- assert.deepStrictEqual(inspect(parser('$a/b$')), undefined);
31
- assert.deepStrictEqual(inspect(parser('$a[b]$')), undefined);
32
- assert.deepStrictEqual(inspect(parser('$a{b}$')), undefined);
33
29
  assert.deepStrictEqual(inspect(parser('$a$b')), undefined);
34
30
  assert.deepStrictEqual(inspect(parser('$a$b$')), undefined);
35
31
  assert.deepStrictEqual(inspect(parser('$ $')), undefined);
32
+ assert.deepStrictEqual(inspect(parser('$ a$')), undefined);
33
+ assert.deepStrictEqual(inspect(parser('$ a $')), undefined);
36
34
  assert.deepStrictEqual(inspect(parser('$\n$')), undefined);
37
- assert.deepStrictEqual(inspect(parser('$a\nb$')), undefined);
38
- assert.deepStrictEqual(inspect(parser('$a\\\nb$')), undefined);
35
+ assert.deepStrictEqual(inspect(parser('$a\\$\nb$')), undefined);
36
+ assert.deepStrictEqual(inspect(parser('$a\\$\\\nb$')), undefined);
39
37
  assert.deepStrictEqual(inspect(parser('${')), undefined);
40
38
  assert.deepStrictEqual(inspect(parser('${a')), undefined);
41
39
  assert.deepStrictEqual(inspect(parser('${$')), undefined);
@@ -47,8 +45,8 @@ describe('Unit: parser/inline/math', () => {
47
45
  assert.deepStrictEqual(inspect(parser('${a}b}$')), undefined);
48
46
  assert.deepStrictEqual(inspect(parser('${\\}$')), undefined);
49
47
  assert.deepStrictEqual(inspect(parser('${\n}$')), undefined);
50
- assert.deepStrictEqual(inspect(parser('${a\nb}$')), undefined);
51
- assert.deepStrictEqual(inspect(parser('${a\\\nb}$')), undefined);
48
+ assert.deepStrictEqual(inspect(parser('${a\\$\nb}$')), undefined);
49
+ assert.deepStrictEqual(inspect(parser('${a\\$\\\nb}$')), undefined);
52
50
  assert.deepStrictEqual(inspect(parser('${\\begin}$')), [['<span class="invalid" translate="no">${\\begin}$</span>'], '']);
53
51
  assert.deepStrictEqual(inspect(parser('${\\Huge}$')), [['<span class="invalid" translate="no">${\\Huge}$</span>'], '']);
54
52
  assert.deepStrictEqual(inspect(parser('${a}b$')), undefined);
@@ -56,29 +54,26 @@ describe('Unit: parser/inline/math', () => {
56
54
  });
57
55
 
58
56
  it('basic', () => {
59
- assert.deepStrictEqual(inspect(parser('${}$')), [['<span class="math" translate="no" data-src="${}$">${}$</span>'], '']);
60
- assert.deepStrictEqual(inspect(parser('${ }$')), [['<span class="math" translate="no" data-src="${ }$">${ }$</span>'], '']);
61
- assert.deepStrictEqual(inspect(parser('${a}$')), [['<span class="math" translate="no" data-src="${a}$">${a}$</span>'], '']);
62
- assert.deepStrictEqual(inspect(parser('${a}$0')), [['<span class="math" translate="no" data-src="${a}$">${a}$</span>'], '0']);
63
- assert.deepStrictEqual(inspect(parser('${a}$b')), [['<span class="math" translate="no" data-src="${a}$">${a}$</span>'], 'b']);
64
- assert.deepStrictEqual(inspect(parser('${ab}$')), [['<span class="math" translate="no" data-src="${ab}$">${ab}$</span>'], '']);
65
- assert.deepStrictEqual(inspect(parser('${a b}$')), [['<span class="math" translate="no" data-src="${a b}$">${a b}$</span>'], '']);
66
- assert.deepStrictEqual(inspect(parser('${a }$')), [['<span class="math" translate="no" data-src="${a }$">${a }$</span>'], '']);
67
- assert.deepStrictEqual(inspect(parser('${ a}$')), [['<span class="math" translate="no" data-src="${ a}$">${ a}$</span>'], '']);
68
- assert.deepStrictEqual(inspect(parser('${ a }$')), [['<span class="math" translate="no" data-src="${ a }$">${ a }$</span>'], '']);
69
- assert.deepStrictEqual(inspect(parser('${$}$')), [['<span class="math" translate="no" data-src="${$}$">${$}$</span>'], '']);
70
- assert.deepStrictEqual(inspect(parser('${\\a}$')), [['<span class="math" translate="no" data-src="${\\a}$">${\\a}$</span>'], '']);
71
- assert.deepStrictEqual(inspect(parser('${\\$}$')), [['<span class="math" translate="no" data-src="${\\$}$">${\\$}$</span>'], '']);
72
- assert.deepStrictEqual(inspect(parser('${\\\\}$')), [['<span class="math" translate="no" data-src="${\\\\}$">${\\\\}$</span>'], '']);
57
+ assert.deepStrictEqual(inspect(parser('$0$')), [['<span class="math" translate="no" data-src="$0$">$0$</span>'], '']);
58
+ assert.deepStrictEqual(inspect(parser('$0$$')), [['<span class="math" translate="no" data-src="$0$">$0$</span>'], '$']);
59
+ assert.deepStrictEqual(inspect(parser('$0-1$')), [['<span class="math" translate="no" data-src="$0-1$">$0-1$</span>'], '']);
60
+ assert.deepStrictEqual(inspect(parser('$0+1$')), [['<span class="math" translate="no" data-src="$0+1$">$0+1$</span>'], '']);
61
+ assert.deepStrictEqual(inspect(parser('$0*1$')), [['<span class="math" translate="no" data-src="$0*1$">$0*1$</span>'], '']);
62
+ assert.deepStrictEqual(inspect(parser('$0/1$')), [['<span class="math" translate="no" data-src="$0/1$">$0/1$</span>'], '']);
73
63
  assert.deepStrictEqual(inspect(parser('$a$')), [['<span class="math" translate="no" data-src="$a$">$a$</span>'], '']);
64
+ assert.deepStrictEqual(inspect(parser('$a$$')), [['<span class="math" translate="no" data-src="$a$">$a$</span>'], '$']);
65
+ assert.deepStrictEqual(inspect(parser('$a -b$')), [['<span class="math" translate="no" data-src="$a -b$">$a -b$</span>'], '']);
66
+ assert.deepStrictEqual(inspect(parser('$a+b$')), [['<span class="math" translate="no" data-src="$a+b$">$a+b$</span>'], '']);
67
+ assert.deepStrictEqual(inspect(parser('$a*b$')), [['<span class="math" translate="no" data-src="$a*b$">$a*b$</span>'], '']);
68
+ assert.deepStrictEqual(inspect(parser('$a/b$')), [['<span class="math" translate="no" data-src="$a/b$">$a/b$</span>'], '']);
74
69
  assert.deepStrictEqual(inspect(parser(`$a'$`)), [[`<span class="math" translate="no" data-src="$a'$">$a'$</span>`], '']);
75
70
  assert.deepStrictEqual(inspect(parser(`$a''$`)), [[`<span class="math" translate="no" data-src="$a''$">$a''$</span>`], '']);
76
71
  assert.deepStrictEqual(inspect(parser('$a$[A](a)')), [['<span class="math" translate="no" data-src="$a$">$a$</span>'], '[A](a)']);
77
- assert.deepStrictEqual(inspect(parser('$a$$')), [['<span class="math" translate="no" data-src="$a$">$a$</span>'], '$']);
78
72
  assert.deepStrictEqual(inspect(parser('$A$')), [['<span class="math" translate="no" data-src="$A$">$A$</span>'], '']);
79
73
  assert.deepStrictEqual(inspect(parser('$\\$$')), [['<span class="math" translate="no" data-src="$\\$$">$\\$$</span>'], '']);
80
74
  assert.deepStrictEqual(inspect(parser('$\\Pi$')), [['<span class="math" translate="no" data-src="$\\Pi$">$\\Pi$</span>'], '']);
81
- assert.deepStrictEqual(inspect(parser('$\\Pi $')), [['<span class="math" translate="no" data-src="$\\Pi $">$\\Pi $</span>'], '']);
75
+ assert.deepStrictEqual(inspect(parser('$\\ 0$')), [['<span class="math" translate="no" data-src="$\\ 0$">$\\ 0$</span>'], '']);
76
+ assert.deepStrictEqual(inspect(parser('$\\\\0$')), [['<span class="math" translate="no" data-src="$\\\\0$">$\\\\0$</span>'], '']);
82
77
  assert.deepStrictEqual(inspect(parser('$|1|$')), [['<span class="math" translate="no" data-src="$|1|$">$|1|$</span>'], '']);
83
78
  assert.deepStrictEqual(inspect(parser('$[0,1)$]')), [['<span class="math" translate="no" data-src="$[0,1)$">$[0,1)$</span>'], ']']);
84
79
  assert.deepStrictEqual(inspect(parser('$(0, 1]$)')), [['<span class="math" translate="no" data-src="$(0, 1]$">$(0, 1]$</span>'], ')']);
@@ -90,6 +85,21 @@ describe('Unit: parser/inline/math', () => {
90
85
  assert.deepStrictEqual(inspect(parser('$f(x)$')), [['<span class="math" translate="no" data-src="$f(x)$">$f(x)$</span>'], '']);
91
86
  assert.deepStrictEqual(inspect(parser('$f: x \\to y$')), [['<span class="math" translate="no" data-src="$f: x \\to y$">$f: x \\to y$</span>'], '']);
92
87
  assert.deepStrictEqual(inspect(parser('$k$-space')), [['<span class="math" translate="no" data-src="$k$">$k$</span>'], '-space']);
88
+ assert.deepStrictEqual(inspect(parser('${}$')), [['<span class="math" translate="no" data-src="${}$">${}$</span>'], '']);
89
+ assert.deepStrictEqual(inspect(parser('${ }$')), [['<span class="math" translate="no" data-src="${ }$">${ }$</span>'], '']);
90
+ assert.deepStrictEqual(inspect(parser('${a}$')), [['<span class="math" translate="no" data-src="${a}$">${a}$</span>'], '']);
91
+ assert.deepStrictEqual(inspect(parser('${a}$$')), [['<span class="math" translate="no" data-src="${a}$">${a}$</span>'], '$']);
92
+ assert.deepStrictEqual(inspect(parser('${a}$0')), [['<span class="math" translate="no" data-src="${a}$">${a}$</span>'], '0']);
93
+ assert.deepStrictEqual(inspect(parser('${a}$b')), [['<span class="math" translate="no" data-src="${a}$">${a}$</span>'], 'b']);
94
+ assert.deepStrictEqual(inspect(parser('${ab}$')), [['<span class="math" translate="no" data-src="${ab}$">${ab}$</span>'], '']);
95
+ assert.deepStrictEqual(inspect(parser('${a b}$')), [['<span class="math" translate="no" data-src="${a b}$">${a b}$</span>'], '']);
96
+ assert.deepStrictEqual(inspect(parser('${a }$')), [['<span class="math" translate="no" data-src="${a }$">${a }$</span>'], '']);
97
+ assert.deepStrictEqual(inspect(parser('${ a}$')), [['<span class="math" translate="no" data-src="${ a}$">${ a}$</span>'], '']);
98
+ assert.deepStrictEqual(inspect(parser('${ a }$')), [['<span class="math" translate="no" data-src="${ a }$">${ a }$</span>'], '']);
99
+ assert.deepStrictEqual(inspect(parser('${$}$')), [['<span class="math" translate="no" data-src="${$}$">${$}$</span>'], '']);
100
+ assert.deepStrictEqual(inspect(parser('${\\a}$')), [['<span class="math" translate="no" data-src="${\\a}$">${\\a}$</span>'], '']);
101
+ assert.deepStrictEqual(inspect(parser('${\\$}$')), [['<span class="math" translate="no" data-src="${\\$}$">${\\$}$</span>'], '']);
102
+ assert.deepStrictEqual(inspect(parser('${\\\\}$')), [['<span class="math" translate="no" data-src="${\\\\}$">${\\\\}$</span>'], '']);
93
103
  });
94
104
 
95
105
  it('nest', () => {
@@ -8,7 +8,6 @@ const disallowedCommand = /\\(?:begin|tiny|huge|large)(?![0-9a-z])/i;
8
8
 
9
9
  export const math: MathParser = lazy(() => creator(validate('$', '$', '\n', rewrite(
10
10
  union([
11
- surround('$', bracket, '$'),
12
11
  surround(
13
12
  '$',
14
13
  verify(
@@ -17,12 +16,11 @@ export const math: MathParser = lazy(() => creator(validate('$', '$', '\n', rewr
17
16
  // $[A-z]*- : Label
18
17
  // $[A-z]*(?!-) : Math
19
18
  // $[\^_[({|] : Math
20
- // $[#$%&] : Invalid first character in latex syntax
21
- // $[A-z]*[,.!?()] : Incomplete syntax before texts
22
- // $[A-z]*\s?[!@#&*+~=`$[]{<] : Incomplete syntax in or around another syntax
23
- str(/^(?=[\\^_[(|]|[A-Za-z][0-9A-Za-z]*'*[ ~]?(?:\$|([\\^_(|:=<>])(?!\1)))(?:\\\$|[\x20-\x23\x25-\x7E])*/),
19
+ // $[#$%&] : Invalid first character in Latex syntax
20
+ str(/^(?![\s{}#$%&]|\d+(?:[,.]\d+)*(?:[\s,.!?()[\]{}]|[^\x00-\x7F])|-[\da-z]|[a-z]+-)(?:\\\$|[\x20-\x23\x25-\x7E])*/i),
24
21
  isEndTightNodes),
25
- /^\$(?![0-9A-Za-z])/),
22
+ /^\$(?![0-9a-z])/i),
23
+ surround('$', bracket, '$'),
26
24
  ]),
27
25
  (source, { caches: { math: cache } = {} }) => [[
28
26
  cache?.get(source)?.cloneNode(true) ||
@@ -43,7 +41,7 @@ const bracket: MathParser.BracketParser = lazy(() => creator(surround(
43
41
  '{',
44
42
  some(union([
45
43
  bracket,
46
- some(escsource, /^[{}]/),
44
+ some(escsource, /^(?:[{}]|\\?\n)/),
47
45
  ])),
48
46
  '}',
49
47
  true)));
@@ -51,11 +51,6 @@ const text: RubyParser.TextParser = creator((source, context) => {
51
51
  while (source !== '') {
52
52
  assert(source[0] !== '\n');
53
53
  switch (source[0]) {
54
- case ' ':
55
- case ' ':
56
- acc.push('');
57
- source = source.slice(1);
58
- continue;
59
54
  case '&': {
60
55
  const result = htmlentity(source, context);
61
56
  acc[acc.length - 1] += eval(result, [source[0]])[0];
@@ -63,6 +58,11 @@ const text: RubyParser.TextParser = creator((source, context) => {
63
58
  continue;
64
59
  }
65
60
  default: {
61
+ if (source[0].trimStart() === '') {
62
+ acc.push('');
63
+ source = source.slice(1);
64
+ continue;
65
+ }
66
66
  const result = txt(source, context)!;
67
67
  assert(result);
68
68
  acc[acc.length - 1] += eval(result)[0] ?? source.slice(0, source.length - exec(result).length);
@@ -10,8 +10,12 @@ describe('Unit: parser/inline/strong', () => {
10
10
  assert.deepStrictEqual(inspect(parser('**')), undefined);
11
11
  assert.deepStrictEqual(inspect(parser('**a')), [['**', 'a'], '']);
12
12
  assert.deepStrictEqual(inspect(parser('**a*')), [['**', 'a', '*'], '']);
13
- assert.deepStrictEqual(inspect(parser('**a **')), [['**', 'a', ' '], '**']);
13
+ assert.deepStrictEqual(inspect(parser('**a **')), [['**', 'a', ' '], '**']);
14
+ assert.deepStrictEqual(inspect(parser('**a\n**')), [['**', 'a', '<br>'], '**']);
15
+ assert.deepStrictEqual(inspect(parser('**a\\ **')), [['**', 'a', ' '], '**']);
16
+ assert.deepStrictEqual(inspect(parser('**a\\\n**')), [['**', 'a', '<span class="linebreak"> </span>'], '**']);
14
17
  assert.deepStrictEqual(inspect(parser('**a*b**')), [['**', 'a', '<em>b</em>', '*'], '']);
18
+ assert.deepStrictEqual(inspect(parser('**a [# b #]**')), [['**', 'a', ' ', '<sup class="comment" title="b"></sup>'], '**']);
15
19
  assert.deepStrictEqual(inspect(parser('** **')), undefined);
16
20
  assert.deepStrictEqual(inspect(parser('** a**')), undefined);
17
21
  assert.deepStrictEqual(inspect(parser('** a **')), undefined);
@@ -27,18 +31,16 @@ describe('Unit: parser/inline/strong', () => {
27
31
 
28
32
  it('basic', () => {
29
33
  assert.deepStrictEqual(inspect(parser('**a**')), [['<strong>a</strong>'], '']);
30
- assert.deepStrictEqual(inspect(parser('**a **')), [['<strong>a </strong>'], '']);
31
- assert.deepStrictEqual(inspect(parser('**a\n**')), [['<strong>a</strong>'], '']);
32
- assert.deepStrictEqual(inspect(parser('**a\\\n**')), [['<strong>a<span class="linebreak"> </span></strong>'], '']);
33
34
  assert.deepStrictEqual(inspect(parser('**ab**')), [['<strong>ab</strong>'], '']);
34
35
  assert.deepStrictEqual(inspect(parser('**a\nb**')), [['<strong>a<br>b</strong>'], '']);
35
36
  assert.deepStrictEqual(inspect(parser('**a\\\nb**')), [['<strong>a<span class="linebreak"> </span>b</strong>'], '']);
36
- assert.deepStrictEqual(inspect(parser('**a*b*c**')), [['<strong>a<em>b</em>c</strong>'], '']);
37
- assert.deepStrictEqual(inspect(parser('**a*b*c**d')), [['<strong>a<em>b</em>c</strong>'], 'd']);
38
- assert.deepStrictEqual(inspect(parser('**a *b***')), [['<strong>a <em>b</em></strong>'], '']);
39
37
  });
40
38
 
41
39
  it('nest', () => {
40
+ assert.deepStrictEqual(inspect(parser('**a*b*c**')), [['<strong>a<em>b</em>c</strong>'], '']);
41
+ assert.deepStrictEqual(inspect(parser('**a*b*c**d')), [['<strong>a<em>b</em>c</strong>'], 'd']);
42
+ assert.deepStrictEqual(inspect(parser('**a *b***')), [['<strong>a <em>b</em></strong>'], '']);
43
+ assert.deepStrictEqual(inspect(parser('**a[# b #]**')), [['<strong>a<sup class="comment" title="b"></sup></strong>'], '']);
42
44
  assert.deepStrictEqual(inspect(parser('**`a`**')), [['<strong><code data-src="`a`">a</code></strong>'], '']);
43
45
  assert.deepStrictEqual(inspect(parser('**<small>**')), [['<strong>&lt;small&gt;</strong>'], '']);
44
46
  assert.deepStrictEqual(inspect(parser('**(*a*)**')), [['<strong><span class="paren">(<em>a</em>)</span></strong>'], '']);
@@ -3,7 +3,7 @@ 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, isEndTightNodes, trimEndBR } from '../util';
6
+ import { startTight, isEndTightNodes } from '../util';
7
7
  import { html, defrag } from 'typed-dom';
8
8
  import { unshift } from 'spica/array';
9
9
 
@@ -13,6 +13,6 @@ export const strong: StrongParser = lazy(() => creator(surround(close(
13
13
  str('**'), false,
14
14
  ([as, bs, cs], rest) =>
15
15
  isEndTightNodes(bs)
16
- ? [[html('strong', defrag(trimEndBR(bs)))], rest]
16
+ ? [[html('strong', defrag(bs))], rest]
17
17
  : [unshift(as, bs), cs[0] + rest],
18
18
  ([as, bs], rest) => [unshift(as, bs), rest])));
@@ -30,18 +30,16 @@ describe('Unit: parser/inline', () => {
30
30
  assert.deepStrictEqual(inspect(parser('*a**b*c')), [['*', 'a', '**', 'b', '*', 'c'], '']);
31
31
  assert.deepStrictEqual(inspect(parser('*a**b*c*')), [['*', 'a', '**', 'b', '<em>c</em>'], '']);
32
32
  assert.deepStrictEqual(inspect(parser('*a**b**')), [['*', 'a', '<strong>b</strong>'], '']);
33
- assert.deepStrictEqual(inspect(parser('*a **b***')), [['<em>a <strong>b</strong></em>'], '']);
34
- assert.deepStrictEqual(inspect(parser('*a **b***c')), [['<em>a <strong>b</strong></em>', 'c'], '']);
33
+ assert.deepStrictEqual(inspect(parser('*a **b***')), [['<em>a <strong>b</strong></em>'], '']);
34
+ assert.deepStrictEqual(inspect(parser('*a **b***c')), [['<em>a <strong>b</strong></em>', 'c'], '']);
35
35
  assert.deepStrictEqual(inspect(parser('**a*')), [['**', 'a', '*'], '']);
36
36
  assert.deepStrictEqual(inspect(parser('**a*b**')), [['**', 'a', '<em>b</em>', '*'], '']);
37
37
  assert.deepStrictEqual(inspect(parser('**a*b**c')), [['**', 'a', '*', 'b', '**', 'c'], '']);
38
- assert.deepStrictEqual(inspect(parser('**a *b***')), [['<strong>a <em>b</em></strong>'], '']);
39
- assert.deepStrictEqual(inspect(parser('**a *b***c')), [['<strong>a <em>b</em></strong>', 'c'], '']);
38
+ assert.deepStrictEqual(inspect(parser('**a *b***')), [['<strong>a <em>b</em></strong>'], '']);
39
+ assert.deepStrictEqual(inspect(parser('**a *b***c')), [['<strong>a <em>b</em></strong>', 'c'], '']);
40
40
  assert.deepStrictEqual(inspect(parser('***a*b**')), [['<strong><em>a</em>b</strong>'], '']);
41
- assert.deepStrictEqual(inspect(parser('***a*b **')), [['**', '<em>a</em>', 'b', ' ', '**'], '']);
42
41
  assert.deepStrictEqual(inspect(parser('***a* b**')), [['<strong><em>a</em> b</strong>'], '']);
43
42
  assert.deepStrictEqual(inspect(parser('***a**b*')), [['<em><strong>a</strong>b</em>'], '']);
44
- assert.deepStrictEqual(inspect(parser('***a**b *')), [['*', '<strong>a</strong>', 'b', ' ', '*'], '']);
45
43
  assert.deepStrictEqual(inspect(parser('***a** b*')), [['<em><strong>a</strong> b</em>'], '']);
46
44
  assert.deepStrictEqual(inspect(parser('***a*')), [['**', '<em>a</em>'], '']);
47
45
  assert.deepStrictEqual(inspect(parser('***a**')), [['*', '<strong>a</strong>'], '']);
@@ -13,7 +13,15 @@ describe('Unit: parser/source/escsource', () => {
13
13
  it('basic', () => {
14
14
  assert.deepStrictEqual(inspect(parser('a')), [['a'], '']);
15
15
  assert.deepStrictEqual(inspect(parser('ab')), [['ab'], '']);
16
- assert.deepStrictEqual(inspect(parser('09AZaz')), [['09AZaz'], '']);
16
+ assert.deepStrictEqual(inspect(parser('09あいAZaz')), [['09あいAZaz'], '']);
17
+ });
18
+
19
+ it('space', () => {
20
+ assert.deepStrictEqual(inspect(parser(' ')), [[' '], '']);
21
+ assert.deepStrictEqual(inspect(parser(' ')), [[' '], '']);
22
+ assert.deepStrictEqual(inspect(parser(' ')), [[' '], '']);
23
+ assert.deepStrictEqual(inspect(parser(' \n')), [[' ', '\n'], '']);
24
+ assert.deepStrictEqual(inspect(parser(' \n')), [[' ', '\n'], '']);
17
25
  });
18
26
 
19
27
  it('linebreak', () => {
@@ -1,5 +1,6 @@
1
1
  import { EscapableSourceParser } from '../source';
2
2
  import { creator } from '../../combinator';
3
+ import { nonWhitespace } from './text';
3
4
 
4
5
  const separator = /[\s\x00-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]/;
5
6
 
@@ -15,7 +16,12 @@ export const escsource: EscapableSourceParser = creator(source => {
15
16
  case '\\':
16
17
  return [[source.slice(0, 2)], source.slice(2)];
17
18
  default:
18
- return [[source.slice(0, 1)], source.slice(1)];
19
+ const b = source[0] !== '\n' && source[0].trimStart() === '';
20
+ const i = b
21
+ ? source.search(nonWhitespace)
22
+ : 1;
23
+ assert(i > 0);
24
+ return [[source.slice(0, i)], source.slice(i)];
19
25
  }
20
26
  default:
21
27
  return [[source.slice(0, i)], source.slice(i)];
@@ -12,9 +12,8 @@ describe('Unit: parser/text/text', () => {
12
12
 
13
13
  it('basic', () => {
14
14
  assert.deepStrictEqual(inspect(parser('a')), [['a'], '']);
15
- assert.deepStrictEqual(inspect(parser('az')), [['az'], '']);
16
- assert.deepStrictEqual(inspect(parser('09')), [['09'], '']);
17
- assert.deepStrictEqual(inspect(parser('azあい09')), [['az', 'あい', '09'], '']);
15
+ assert.deepStrictEqual(inspect(parser('ab')), [['ab'], '']);
16
+ assert.deepStrictEqual(inspect(parser('09あいAZaz')), [['09', 'あい', 'AZaz'], '']);
18
17
  assert.deepStrictEqual(inspect(parser('a\nb')), [['a', '<br>', 'b'], '']);
19
18
  });
20
19
 
@@ -13,6 +13,15 @@ describe('Unit: parser/source/unescapable', () => {
13
13
  it('basic', () => {
14
14
  assert.deepStrictEqual(inspect(parser('a')), [['a'], '']);
15
15
  assert.deepStrictEqual(inspect(parser('ab')), [['ab'], '']);
16
+ assert.deepStrictEqual(inspect(parser('09あいAZaz')), [['09', 'あい', 'AZaz'], '']);
17
+ });
18
+
19
+ it('space', () => {
20
+ assert.deepStrictEqual(inspect(parser(' ')), [[' '], '']);
21
+ assert.deepStrictEqual(inspect(parser(' ')), [[' '], '']);
22
+ assert.deepStrictEqual(inspect(parser(' ')), [[' '], '']);
23
+ assert.deepStrictEqual(inspect(parser(' \n')), [[' ', '\n'], '']);
24
+ assert.deepStrictEqual(inspect(parser(' \n')), [[' ', '\n'], '']);
16
25
  });
17
26
 
18
27
  it('linebreak', () => {
@@ -135,22 +135,21 @@ export function isStartTightNodes(nodes: readonly (HTMLElement | string)[]): boo
135
135
  }
136
136
  export function isEndTightNodes(nodes: readonly (HTMLElement | string)[]): boolean {
137
137
  if (nodes.length === 0) return true;
138
- const last = nodes.length - 1;
139
- return typeof nodes[last] === 'string' && (nodes[last] as string).length > 1
140
- ? isVisible(nodes[last], -1) ||
141
- isVisible(nodes[last], -2)
142
- : isVisible(nodes[last], -1) || last === 0 ||
143
- isVisible(nodes[last - 1], -1);
138
+ for (let i = nodes.length; i--;) {
139
+ const node = nodes[i];
140
+ if (typeof node === 'object' && node.className === 'comment') continue;
141
+ return isVisible(node, -1);
142
+ }
143
+ return false;
144
144
  }
145
- function isVisible(node: HTMLElement | string, position?: number): boolean {
146
- if (!node) return false;
145
+ function isVisible(node: HTMLElement | string, strpos?: number): boolean {
147
146
  switch (typeof node) {
148
147
  case 'string':
149
- const char = position === undefined
150
- ? node
151
- : node[position >= 0 ? position : node.length + position];
152
- assert(char);
148
+ const char = node && strpos !== undefined
149
+ ? node[strpos >= 0 ? strpos : node.length + strpos]
150
+ : node;
153
151
  switch (char) {
152
+ case '':
154
153
  case ' ':
155
154
  case '\t':
156
155
  case '\n':
@@ -177,28 +176,19 @@ export function trimNode(nodes: (HTMLElement | string)[]): (HTMLElement | string
177
176
  return trimNodeStart(trimNodeEnd(nodes));
178
177
  }
179
178
  function trimNodeStart(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
180
- const skip = nodes.length > 0 &&
181
- typeof nodes[nodes.length - 1] === 'object' &&
182
- nodes[nodes.length - 1]['className'] === 'indexer'
183
- ? [nodes.pop()!]
184
- : [];
185
- for (
186
- let first = nodes[0];
187
- nodes.length > 0 &&
188
- !isVisible(first, 0) &&
189
- !(typeof first === 'object' && first.className === 'comment');
190
- ) {
191
- assert(nodes.length > 0);
192
- if (typeof first === 'string') {
193
- const pos = first.length - first.trimStart().length;
179
+ for (let node = nodes[0]; nodes.length > 0 && !isVisible(node = nodes[0], 0);) {
180
+ if (nodes.length === 1 && typeof node === 'object' && node.className === 'indexer') break;
181
+ if (typeof node === 'object' && node.className === 'comment') break;
182
+ if (typeof node === 'string') {
183
+ const pos = node.length - node.trimStart().length;
194
184
  if (pos > 0) {
195
- nodes[0] = first.slice(pos);
185
+ nodes[0] = node.slice(pos);
196
186
  break;
197
187
  }
198
188
  }
199
- nodes.pop();
189
+ nodes.shift();
200
190
  }
201
- return push(nodes, skip);
191
+ return nodes;
202
192
  }
203
193
  export function trimNodeEnd(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
204
194
  const skip = nodes.length > 0 &&
@@ -206,17 +196,12 @@ export function trimNodeEnd(nodes: (HTMLElement | string)[]): (HTMLElement | str
206
196
  nodes[nodes.length - 1]['className'] === 'indexer'
207
197
  ? [nodes.pop()!]
208
198
  : [];
209
- for (
210
- let last = nodes[0];
211
- nodes.length > 0 &&
212
- !isVisible(last = nodes[nodes.length - 1], -1) &&
213
- !(typeof last === 'object' && last.className === 'comment');
214
- ) {
215
- assert(nodes.length > 0);
216
- if (typeof last === 'string') {
217
- const pos = last.trimEnd().length;
199
+ for (let node = nodes[0]; nodes.length > 0 && !isVisible(node = nodes[nodes.length - 1], -1);) {
200
+ if (typeof node === 'object' && node.className === 'comment') break;
201
+ if (typeof node === 'string') {
202
+ const pos = node.trimEnd().length;
218
203
  if (pos > 0) {
219
- nodes[nodes.length - 1] = last.slice(0, pos);
204
+ nodes[nodes.length - 1] = node.slice(0, pos);
220
205
  break;
221
206
  }
222
207
  }
@@ -224,8 +209,8 @@ export function trimNodeEnd(nodes: (HTMLElement | string)[]): (HTMLElement | str
224
209
  }
225
210
  return push(nodes, skip);
226
211
  }
227
- export function trimEndBR<T extends HTMLElement | string>(nodes: T[]): T[];
228
- export function trimEndBR(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
212
+ export function trimNodeEndBR<T extends HTMLElement | string>(nodes: T[]): T[];
213
+ export function trimNodeEndBR(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
229
214
  if (nodes.length === 0) return nodes;
230
215
  const node = nodes[nodes.length - 1];
231
216
  return typeof node === 'object' && node.tagName === 'BR'