securemark 0.299.1 → 0.299.2

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,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.299.2
4
+
5
+ - Refactoring.
6
+
3
7
  ## 0.299.1
4
8
 
5
9
  - Refactoring.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! securemark v0.299.1 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
1
+ /*! securemark v0.299.2 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("Prism"), require("DOMPurify"));
@@ -3745,16 +3745,21 @@ Object.defineProperty(exports, "__esModule", ({
3745
3745
  }));
3746
3746
  exports.some = void 0;
3747
3747
  const delimiter_1 = __webpack_require__(385);
3748
- function some(parser, delimiter, after, delimiters, limit = -1) {
3748
+ function some(parser, delimiter, after, delimiters, limit = 0) {
3749
3749
  if (typeof delimiter === 'number') {
3750
3750
  limit = delimiter;
3751
+ delimiters = undefined;
3751
3752
  delimiter = undefined;
3752
3753
  } else if (Array.isArray(delimiter)) {
3754
+ limit = after;
3753
3755
  delimiters = delimiter;
3754
3756
  delimiter = undefined;
3755
3757
  } else if (after === undefined || Array.isArray(after)) {
3758
+ limit = delimiters;
3756
3759
  delimiters = after;
3757
3760
  after = undefined;
3761
+ } else {
3762
+ delimiters = delimiters;
3758
3763
  }
3759
3764
  const match = delimiter_1.Delimiters.tester(delimiter, after);
3760
3765
  const delims = delimiters?.map(([delimiter, precedence]) => ({
@@ -3780,7 +3785,8 @@ function some(parser, delimiter, after, delimiters, limit = -1) {
3780
3785
  if (result === undefined) break;
3781
3786
  if (context.position === pos) break;
3782
3787
  nodes = nodes?.import(result) ?? result;
3783
- if (limit >= 0 && context.position - position > limit) break;
3788
+ // 次にパースに成功すれば確実に制限値を超えるので制限値ちょうどでも中止する
3789
+ if (limit > 0 && context.position - position >= limit) break;
3784
3790
  }
3785
3791
  delims && context.delimiters.pop(delims.length);
3786
3792
  return context.position > position ? nodes : undefined;
@@ -4201,7 +4207,11 @@ function header(source) {
4201
4207
  exports.header = header;
4202
4208
  function headers(source) {
4203
4209
  const [el] = parse(source);
4204
- return el?.textContent.trimEnd().slice(el.firstChild.firstChild.textContent.length).split(/\r?\n/) ?? [];
4210
+ const acc = [];
4211
+ for (let field = el?.firstChild?.firstChild; field = field?.nextSibling;) {
4212
+ acc.push(field.textContent);
4213
+ }
4214
+ return acc;
4205
4215
  }
4206
4216
  exports.headers = headers;
4207
4217
  function parse(source) {
@@ -5827,7 +5837,7 @@ class Context extends parser_1.Context {
5827
5837
  } = options;
5828
5838
  this.resources ??= {
5829
5839
  // バックトラックのせいで文字数制限を受けないようにする。
5830
- clock: exports.MAX_SEGMENT_SIZE * (6 + 1),
5840
+ clock: exports.MAX_SEGMENT_SIZE * (5 + 1),
5831
5841
  recursions: [5 || 0 /* Recursion.block */, 20 || 0 /* Recursion.blockquote */, 40 || 0 /* Recursion.listitem */, 20 || 0 /* Recursion.inline */, 20 || 0 /* Recursion.bracket */, 20 || 0 /* Recursion.terminal */]
5832
5842
  };
5833
5843
  this.segment = segment ?? 0 /* Segment.unknown */;
@@ -5881,7 +5891,7 @@ const combinator_1 = __webpack_require__(3484);
5881
5891
  const source_1 = __webpack_require__(8745);
5882
5892
  const util_1 = __webpack_require__(4992);
5883
5893
  const dom_1 = __webpack_require__(394);
5884
- exports.header = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(/---+[^\S\r\n]*\r?\n(?=\S)/y, (0, combinator_1.inits)([(0, combinator_1.block)((0, combinator_1.union)([(0, combinator_1.validate)(context => context.header, (0, combinator_1.focus)(/(---+)[^\S\r\n]*\r?\n(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*:[ \t]+\S[^\r\n]*\r?\n){1,100}\1[^\S\r\n]*(?:$|\r?\n)/yi, (0, combinator_1.convert)(source => source.slice(source.indexOf('\n') + 1, source.trimEnd().lastIndexOf('\n')), (0, combinator_1.fmap)((0, combinator_1.some)((0, combinator_1.union)([field])), ns => new parser_1.List([new parser_1.Node((0, dom_1.html)('aside', {
5894
+ exports.header = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(/---+[^\S\r\n]*\r?\n(?=\S)/y, (0, combinator_1.inits)([(0, combinator_1.block)((0, combinator_1.union)([(0, combinator_1.validate)(context => context.header, (0, combinator_1.focus)(/(---+)[^\S\r\n]*\r?\n(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*:[ \t]+\S[^\r\n]*\r?\n){1,32}\1[^\S\r\n]*(?:$|\r?\n)/yi, (0, combinator_1.convert)(source => source.slice(source.indexOf('\n') + 1, source.trimEnd().lastIndexOf('\n')), (0, combinator_1.fmap)((0, combinator_1.some)((0, combinator_1.union)([field])), ns => new parser_1.List([new parser_1.Node((0, dom_1.html)('aside', {
5885
5895
  class: 'header'
5886
5896
  }, [(0, dom_1.html)('details', {
5887
5897
  open: ''
@@ -5903,7 +5913,7 @@ const field = (0, combinator_1.line)(({
5903
5913
  }) => {
5904
5914
  const name = source.slice(position, source.indexOf(':', position));
5905
5915
  const value = source.slice(position + name.length + 1).trim();
5906
- return new parser_1.List([new parser_1.Node((0, dom_1.html)('span', {
5916
+ return new parser_1.List([new parser_1.Node((0, dom_1.html)('div', {
5907
5917
  class: 'field',
5908
5918
  'data-name': name.toLowerCase(),
5909
5919
  'data-value': value
@@ -5911,7 +5921,7 @@ const field = (0, combinator_1.line)(({
5911
5921
  class: 'field-name'
5912
5922
  }, name), ': ', (0, dom_1.html)('span', {
5913
5923
  class: 'field-value'
5914
- }, value), '\n']))]);
5924
+ }, value)]))]);
5915
5925
  });
5916
5926
 
5917
5927
  /***/ },
@@ -6359,10 +6369,9 @@ Object.defineProperty(exports, "__esModule", ({
6359
6369
  exports.lineurl = exports.url = void 0;
6360
6370
  const parser_1 = __webpack_require__(605);
6361
6371
  const combinator_1 = __webpack_require__(3484);
6362
- const inline_1 = __webpack_require__(7973);
6363
6372
  const link_1 = __webpack_require__(3628);
6364
6373
  const source_1 = __webpack_require__(8745);
6365
- exports.url = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)(/(?<![0-9A-Za-z][.+-]?|[@#])https?:\/\/(?=[\x21-\x7E])/y, (0, combinator_1.precedence)(0, (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(source_1.unescsource, /(?<![-+*=~^_,.;:!?]|\/{3})(?:[-+*=~^_,.;:!?]|\/{3,}(?!\/))*(?=[\\$"`\[\](){}<>()[]{}|]|[^\x21-\x7E]|$)/y), (0, combinator_1.precedence)(1, bracket)]), [[/[^\x21-\x7E]|\$/y, 9]])), false, [3 | 8 /* Backtrack.unescapable */]), (0, combinator_1.union)([(0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, context => new parser_1.List([new parser_1.Node((0, link_1.parse)(new parser_1.List(), new parser_1.List([new parser_1.Node(context.source)]), context))]))), (0, combinator_1.open)((0, source_1.str)(/[^:]+/y), (0, combinator_1.some)(inline_1.inline))])));
6374
+ exports.url = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.open)(/(?<![0-9A-Za-z][.+-]?|[@#])https?:\/\/(?=[[0-9A-Za-z])/y, (0, combinator_1.precedence)(0, (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(source_1.unescsource, /(?<![-+*=~^_,.;:!?]|\/{3})(?:[-+*=~^_,.;:!?]|\/{3,}(?!\/))*(?=[\\$"`\[\](){}<>()[]{}|]|[^\x21-\x7E]|$)/y), (0, combinator_1.precedence)(1, bracket)]), [[/[^\x21-\x7E]|\$/y, 9]])), false, [3 | 8 /* Backtrack.unescapable */]), (0, combinator_1.union)([(0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, context => new parser_1.List([new parser_1.Node((0, link_1.parse)(new parser_1.List(), new parser_1.List([new parser_1.Node(context.source)]), context))]))), context => new parser_1.List([new parser_1.Node(context.source)])])));
6366
6375
  exports.lineurl = (0, combinator_1.lazy)(() => (0, combinator_1.focus)(/(?<=^|[\r\n])!?https?:\/\/\S+(?=[^\S\r\n]*(?=$|\r?\n))/y, (0, combinator_1.tails)([(0, source_1.str)('!'), (0, combinator_1.union)([(0, combinator_1.constraint)(1 /* State.autolink */, (0, combinator_1.state)(1 /* State.autolink */, context => {
6367
6376
  const {
6368
6377
  source,
@@ -6370,7 +6379,7 @@ exports.lineurl = (0, combinator_1.lazy)(() => (0, combinator_1.focus)(/(?<=^|[\
6370
6379
  } = context;
6371
6380
  context.position -= source[0] === '!' ? 1 : 0;
6372
6381
  return new parser_1.List([new parser_1.Node((0, link_1.parse)(new parser_1.List(), new parser_1.List([new parser_1.Node(source.slice(position))]), context))]);
6373
- })), (0, source_1.str)(/[^:]+/y)])])));
6382
+ })), context => new parser_1.List([new parser_1.Node(context.source)])])])));
6374
6383
  const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.union)([(0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.recursion)(5 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), ')')), (0, source_1.str)(')'), true, [3 | 8 /* Backtrack.unescapable */]), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.recursion)(5 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), ']')), (0, source_1.str)(']'), true, [3 | 8 /* Backtrack.unescapable */]), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.recursion)(5 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), '}')), (0, source_1.str)('}'), true, [3 | 8 /* Backtrack.unescapable */]), (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.precedence)(2, (0, combinator_1.recursion)(5 /* Recursion.terminal */, (0, combinator_1.some)(source_1.unescsource, '"'))), (0, source_1.str)('"'), true, [3 | 8 /* Backtrack.unescapable */])]));
6375
6384
 
6376
6385
  /***/ },
@@ -7389,8 +7398,8 @@ const combinator_1 = __webpack_require__(3484);
7389
7398
  const source_1 = __webpack_require__(8745);
7390
7399
  const util_1 = __webpack_require__(4992);
7391
7400
  const dom_1 = __webpack_require__(394);
7392
- const forbiddenCommand = /\\(?:begin|tiny|huge|large)(?![a-z])|:\/\//i;
7393
- exports.math = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.union)([(0, combinator_1.surround)(/\$(?={)/y, (0, combinator_1.precedence)(4, bracket), '$', false, [3 | 16 /* Backtrack.escapable */]), (0, combinator_1.surround)(/\$(?![\s{}])/y, (0, combinator_1.precedence)(2, (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(source_1.escsource, /\$|[`"{}\r\n]/y), (0, combinator_1.precedence)(4, bracket)]))), /(?<!\s)\$(?![-0-9A-Za-z])/y, false, [3 | 16 /* Backtrack.escapable */])]), ({
7401
+ const forbiddenCommand = /\\(?:begin|tiny|huge|large)(?![a-z])/i;
7402
+ exports.math = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combinator_1.union)([(0, combinator_1.surround)(/\$(?={)/y, (0, combinator_1.precedence)(4, bracket), '$', false, [3 | 16 /* Backtrack.escapable */]), (0, combinator_1.surround)(/\$(?![\s{}])/y, (0, combinator_1.precedence)(2, (0, combinator_1.some)((0, combinator_1.union)([(0, combinator_1.some)(source_1.escsource, /[`"{}$\r\n]|(?<=[0-9A-Za-z]):\/\/[[0-9A-Za-z]/y), (0, combinator_1.precedence)(4, bracket)]))), /(?<!\s)\$(?![-0-9A-Za-z])/y, false, [3 | 16 /* Backtrack.escapable */])]), ({
7394
7403
  source,
7395
7404
  caches: {
7396
7405
  math: cache
@@ -7404,7 +7413,7 @@ exports.math = (0, combinator_1.lazy)(() => (0, combinator_1.rewrite)((0, combin
7404
7413
  translate: 'no',
7405
7414
  ...(0, util_1.invalid)('math', 'content', `"${source.match(forbiddenCommand)[0]}" command is forbidden`)
7406
7415
  }, source))])));
7407
- const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.recursion)(5 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, (0, combinator_1.some)(source_1.escsource, /[{}$\r\n]/y)]))), (0, source_1.str)('}'), true));
7416
+ const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.recursion)(5 /* Recursion.terminal */, (0, combinator_1.some)((0, combinator_1.union)([bracket, (0, combinator_1.some)(source_1.escsource, /[{}$\r\n]|(?<=[0-9A-Za-z]):\/\/[[0-9A-Za-z]/y)]))), (0, source_1.str)('}'), true));
7408
7417
 
7409
7418
  /***/ },
7410
7419
 
@@ -7666,14 +7675,14 @@ const dom_1 = __webpack_require__(394);
7666
7675
  exports.ruby = (0, combinator_1.lazy)(() => (0, combinator_1.bind)((0, combinator_1.inits)([(0, combinator_1.dup)((0, combinator_1.surround)('[', text, ']', false, [1 | 4 /* Backtrack.common */, 3 | 32 /* Backtrack.ruby */], ([, ns]) => {
7667
7676
  ns && ns.last?.value === '' && ns.pop();
7668
7677
  return (0, visibility_1.isNonblankNodeStart)(ns) ? ns : undefined;
7669
- })), (0, combinator_1.dup)((0, combinator_1.surround)('(', text, ')', false, [3 | 32 /* Backtrack.ruby */]))]), ([{
7678
+ })), (0, combinator_1.dup)((0, combinator_1.surround)('(', text, ')', false))]), ([{
7670
7679
  value: texts
7671
7680
  }, {
7672
7681
  value: rubies = undefined
7673
7682
  } = {}], context) => {
7674
7683
  if (rubies === undefined) {
7675
7684
  const head = context.position - context.range;
7676
- return void (0, combinator_1.setBacktrack)(context, 2 | 32 /* Backtrack.ruby */, head);
7685
+ return void (0, combinator_1.setBacktrack)(context, 2 | 64 /* Backtrack.link */ | 32 /* Backtrack.ruby */, head);
7677
7686
  }
7678
7687
  switch (true) {
7679
7688
  case texts.length >= rubies.length:
@@ -8209,10 +8218,10 @@ function repeat(opener, after, closer, rs, parser, cons, termination = (nodes, c
8209
8218
  follow = follow > 0 ? follow : countFollows(source, pos, closer, lead / opener.length | 0);
8210
8219
  nodes = cons(nodes, context, lead, follow);
8211
8220
  if (context.position > pos) {
8212
- const advance = opener.length * (context.position - pos) / closer.length | 0;
8221
+ const advance = context.position - pos;
8213
8222
  i -= advance;
8214
8223
  follow -= advance;
8215
- depth -= advance;
8224
+ depth -= advance / closer.length | 0;
8216
8225
  }
8217
8226
  continue;
8218
8227
  }
@@ -8239,10 +8248,10 @@ function repeat(opener, after, closer, rs, parser, cons, termination = (nodes, c
8239
8248
  nodes = cons(nodes, context, lead, follow);
8240
8249
  state = true;
8241
8250
  if (context.position > pos) {
8242
- const advance = opener.length * (context.position - pos) / closer.length | 0;
8251
+ const advance = context.position - pos;
8243
8252
  i -= advance;
8244
8253
  follow -= advance;
8245
- depth -= advance;
8254
+ depth -= advance / closer.length | 0;
8246
8255
  }
8247
8256
  continue;
8248
8257
  }
@@ -8431,14 +8440,11 @@ Object.defineProperty(exports, "__esModule", ({
8431
8440
  exports.escsource = void 0;
8432
8441
  const parser_1 = __webpack_require__(605);
8433
8442
  const combinator_1 = __webpack_require__(3484);
8434
- const text_1 = __webpack_require__(5655);
8435
8443
  const dom_1 = __webpack_require__(394);
8436
- const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s\$|:\/\/)/g;
8437
8444
  const escsource = context => {
8438
8445
  const {
8439
8446
  source,
8440
- position,
8441
- state
8447
+ position
8442
8448
  } = context;
8443
8449
  if (position === source.length) return;
8444
8450
  const char = source[position];
@@ -8467,7 +8473,7 @@ const escsource = context => {
8467
8473
  return new parser_1.List([new parser_1.Node((0, dom_1.html)('br'), 1 /* Flag.blank */)]);
8468
8474
  default:
8469
8475
  if (context.sequential) return new parser_1.List([new parser_1.Node(char)]);
8470
- let i = (0, text_1.next)(source, position, state, delimiter);
8476
+ let i = seek(source, position);
8471
8477
  i -= position;
8472
8478
  (0, combinator_1.consume)(i - 1, context);
8473
8479
  context.position += i - 1;
@@ -8475,6 +8481,30 @@ const escsource = context => {
8475
8481
  }
8476
8482
  };
8477
8483
  exports.escsource = escsource;
8484
+ function seek(source, position) {
8485
+ for (let i = position + 1; i < source.length; ++i) {
8486
+ const char = source[i];
8487
+ switch (char) {
8488
+ case '\\':
8489
+ case '$':
8490
+ case '"':
8491
+ case '`':
8492
+ case ':':
8493
+ case '[':
8494
+ case ']':
8495
+ case '(':
8496
+ case ')':
8497
+ case '{':
8498
+ case '}':
8499
+ case '\r':
8500
+ case '\n':
8501
+ return i;
8502
+ default:
8503
+ continue;
8504
+ }
8505
+ }
8506
+ return source.length;
8507
+ }
8478
8508
 
8479
8509
  /***/ },
8480
8510
 
@@ -8604,7 +8634,7 @@ exports.strs = strs;
8604
8634
  Object.defineProperty(exports, "__esModule", ({
8605
8635
  value: true
8606
8636
  }));
8607
- exports.isAlphanumeric = exports.next = exports.canSkip = exports.txt = exports.text = exports.nonWhitespace = void 0;
8637
+ exports.isAlphanumeric = exports.backToEmailHead = exports.backToUrlHead = exports.canSkip = exports.txt = exports.text = exports.nonWhitespace = void 0;
8608
8638
  const parser_1 = __webpack_require__(605);
8609
8639
  const combinator_1 = __webpack_require__(3484);
8610
8640
  const dom_1 = __webpack_require__(394);
@@ -8675,23 +8705,16 @@ function isWhitespace(char, linebreak) {
8675
8705
  return false;
8676
8706
  }
8677
8707
  }
8678
- function next(source, position, state, delimiter) {
8679
- let index;
8680
- if (delimiter) {
8681
- delimiter.lastIndex = position + 1;
8682
- delimiter.test(source);
8683
- index = delimiter.lastIndex || position;
8684
- } else {
8685
- index = seek(source, position, state);
8686
- }
8687
- if (index === position || index === source.length) return source.length;
8708
+ function next(source, position, state) {
8709
+ let index = seek(source, position, state);
8710
+ if (index === source.length) return source.length;
8688
8711
  const char = source[index];
8689
8712
  switch (char) {
8690
8713
  case '%':
8691
- index += !delimiter && index - 1 > position ? -1 : 0;
8714
+ index += index - 1 > position ? -1 : 0;
8692
8715
  break;
8693
8716
  case '[':
8694
- index += !delimiter && index - 1 > position && source.startsWith(' [|', index - 1) ? -1 : 0;
8717
+ index += index - 1 > position && source.startsWith(' [|', index - 1) ? -1 : 0;
8695
8718
  break;
8696
8719
  case ':':
8697
8720
  index = source.startsWith('//', index + 1) ? backToUrlHead(source, position, index) : index;
@@ -8702,7 +8725,6 @@ function next(source, position, state, delimiter) {
8702
8725
  }
8703
8726
  return index;
8704
8727
  }
8705
- exports.next = next;
8706
8728
  function backToUrlHead(source, position, index) {
8707
8729
  const delim = index;
8708
8730
  let state = false;
@@ -8724,6 +8746,7 @@ function backToUrlHead(source, position, index) {
8724
8746
  }
8725
8747
  return index === position || source[index] !== 'h' ? delim : index;
8726
8748
  }
8749
+ exports.backToUrlHead = backToUrlHead;
8727
8750
  function backToEmailHead(source, position, index) {
8728
8751
  const delim = index;
8729
8752
  let state = false;
@@ -8746,6 +8769,7 @@ function backToEmailHead(source, position, index) {
8746
8769
  }
8747
8770
  return index === position ? delim : index;
8748
8771
  }
8772
+ exports.backToEmailHead = backToEmailHead;
8749
8773
  function isAlphanumeric(char) {
8750
8774
  if (char < '0' || '\x7F' < char) return false;
8751
8775
  return '0' <= char && char <= '9' || 'A' <= char && char <= 'Z' || 'a' <= char && char <= 'z';
@@ -8841,12 +8865,11 @@ function seek(source, position, state) {
8841
8865
  Object.defineProperty(exports, "__esModule", ({
8842
8866
  value: true
8843
8867
  }));
8844
- exports.unescsource = exports.delimiter = void 0;
8868
+ exports.unescsource = void 0;
8845
8869
  const parser_1 = __webpack_require__(605);
8846
8870
  const combinator_1 = __webpack_require__(3484);
8847
8871
  const text_1 = __webpack_require__(5655);
8848
8872
  const dom_1 = __webpack_require__(394);
8849
- exports.delimiter = /(?=(?=[\x00-\x7F])[^0-9A-Za-z]|(?<=[\x00-\x7F])[^\x00-\x7F])/g;
8850
8873
  const unescsource = context => {
8851
8874
  const {
8852
8875
  source,
@@ -8870,7 +8893,7 @@ const unescsource = context => {
8870
8893
  default:
8871
8894
  if (context.sequential) return new parser_1.List([new parser_1.Node(char)]);
8872
8895
  text_1.nonWhitespace.lastIndex = position + 1;
8873
- let i = (0, text_1.canSkip)(source, position) ? text_1.nonWhitespace.test(source) ? text_1.nonWhitespace.lastIndex - 1 : source.length : (0, text_1.next)(source, position, state, exports.delimiter);
8896
+ let i = (0, text_1.canSkip)(source, position) ? text_1.nonWhitespace.test(source) ? text_1.nonWhitespace.lastIndex - 1 : source.length : next(source, position, state);
8874
8897
  i -= position;
8875
8898
  (0, combinator_1.consume)(i - 1, context);
8876
8899
  context.position += i - 1;
@@ -8878,6 +8901,79 @@ const unescsource = context => {
8878
8901
  }
8879
8902
  };
8880
8903
  exports.unescsource = unescsource;
8904
+ function next(source, position, state) {
8905
+ let index = seek(source, position, state);
8906
+ if (index === source.length) return source.length;
8907
+ const char = source[index];
8908
+ switch (char) {
8909
+ case ':':
8910
+ index = source.startsWith('//', index + 1) ? (0, text_1.backToUrlHead)(source, position, index) : index;
8911
+ break;
8912
+ case '@':
8913
+ index = ~state & 1 /* State.autolink */ ? (0, text_1.backToEmailHead)(source, position, index) : index;
8914
+ break;
8915
+ }
8916
+ return index;
8917
+ }
8918
+ function seek(source, position, state) {
8919
+ const cat = category(source[position]);
8920
+ for (let i = position + 1; i < source.length; ++i) {
8921
+ const char = source[i];
8922
+ switch (char) {
8923
+ case '\\':
8924
+ case '!':
8925
+ case '$':
8926
+ case '"':
8927
+ case '`':
8928
+ case '[':
8929
+ case ']':
8930
+ case '(':
8931
+ case ')':
8932
+ case '{':
8933
+ case '}':
8934
+ case '<':
8935
+ case '>':
8936
+ case '(':
8937
+ case ')':
8938
+ case '[':
8939
+ case ']':
8940
+ case '{':
8941
+ case '}':
8942
+ case '-':
8943
+ case '+':
8944
+ case '*':
8945
+ case '=':
8946
+ case '~':
8947
+ case '^':
8948
+ case '_':
8949
+ case ',':
8950
+ case '.':
8951
+ case ';':
8952
+ case ':':
8953
+ case '!':
8954
+ case '?':
8955
+ case '/':
8956
+ case '|':
8957
+ case '\r':
8958
+ case '\n':
8959
+ return i;
8960
+ case '@':
8961
+ case '#':
8962
+ if (~state & 1 /* State.autolink */) return i;
8963
+ continue;
8964
+ case ':':
8965
+ if (source[i + 1] === '/' && source[i + 2] === '/') return i;
8966
+ continue;
8967
+ default:
8968
+ if (cat && !category(char)) return i;
8969
+ continue;
8970
+ }
8971
+ }
8972
+ return source.length;
8973
+ }
8974
+ function category(char) {
8975
+ return '\x21' <= char && char <= '\x7E';
8976
+ }
8881
8977
 
8882
8978
  /***/ },
8883
8979
 
package/markdown.d.ts CHANGED
@@ -1082,7 +1082,7 @@ export namespace MarkdownParser {
1082
1082
  Inline<'url'>,
1083
1083
  Parser<string | HTMLElement, Context, [
1084
1084
  Parser<HTMLAnchorElement, Context, []>,
1085
- InlineParser,
1085
+ Parser<string, Context, []>,
1086
1086
  ]> {
1087
1087
  }
1088
1088
  export namespace UrlParser {
@@ -1092,7 +1092,7 @@ export namespace MarkdownParser {
1092
1092
  SourceParser.StrParser,
1093
1093
  Parser<string | HTMLElement, Context, [
1094
1094
  Parser<HTMLAnchorElement, Context, []>,
1095
- SourceParser.StrParser,
1095
+ Parser<string, Context, []>,
1096
1096
  ]>,
1097
1097
  ]> {
1098
1098
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.299.1",
3
+ "version": "0.299.2",
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",
@@ -37,6 +37,8 @@ export class Delimiters {
37
37
  assert(pattern !== '');
38
38
  return index(`'${pattern}`);
39
39
  case 'object':
40
+ assert(pattern.flags.includes('y'));
41
+ assert(/^yu?$/.test(pattern.flags));
40
42
  return index(`/${pattern.source}`);
41
43
  }
42
44
  }
@@ -4,24 +4,31 @@ import { Delimiters } from '../delimiter';
4
4
  type DelimiterOption = readonly [delimiter: string | RegExp, precedence: number];
5
5
 
6
6
  export function some<P extends Parser>(parser: P, limit?: number): P;
7
- export function some<P extends Parser>(parser: P, delimiters?: readonly DelimiterOption[]): P;
8
- export function some<P extends Parser>(parser: P, delimiter: string | RegExp, delimiters?: readonly DelimiterOption[]): P;
9
- export function some<P extends Parser>(parser: P, delimiter: string | RegExp, after: string | RegExp, delimiters?: readonly DelimiterOption[]): P;
10
- export function some<N>(parser: Parser<N>, delimiter?: number | string | RegExp | readonly DelimiterOption[], after?: string | RegExp | readonly DelimiterOption[], delimiters?: readonly DelimiterOption[], limit = -1): Parser<N> {
7
+ export function some<P extends Parser>(parser: P, delimiters?: readonly DelimiterOption[], limit?: number): P;
8
+ export function some<P extends Parser>(parser: P, delimiter: string | RegExp, delimiters?: readonly DelimiterOption[], limit?: number): P;
9
+ export function some<P extends Parser>(parser: P, delimiter: string | RegExp, after: string | RegExp, delimiters?: readonly DelimiterOption[], limit?: number): P;
10
+ export function some<N>(parser: Parser<N>, delimiter?: number | string | RegExp | readonly DelimiterOption[], after?: number | string | RegExp | readonly DelimiterOption[], delimiters?: number | readonly DelimiterOption[], limit = 0): Parser<N> {
11
11
  if (typeof delimiter === 'number') {
12
12
  limit = delimiter;
13
+ delimiters = undefined;
13
14
  delimiter = undefined;
14
15
  }
15
16
  else if (Array.isArray(delimiter)) {
17
+ limit = after as number;
16
18
  delimiters = delimiter;
17
19
  delimiter = undefined;
18
20
  }
19
21
  else if (after === undefined || Array.isArray(after)) {
22
+ limit = delimiters as number;
20
23
  delimiters = after;
21
24
  after = undefined;
22
25
  }
26
+ else {
27
+ delimiters = delimiters as readonly DelimiterOption[];
28
+ }
23
29
  assert(parser);
24
30
  assert(delimiter !== '');
31
+ assert(delimiters === undefined || Array.isArray(delimiters));
25
32
  const match = Delimiters.tester(delimiter as string, after as string);
26
33
  const delims = delimiters?.map(([delimiter, precedence]) => ({
27
34
  signature: Delimiters.signature(delimiter),
@@ -43,7 +50,8 @@ export function some<N>(parser: Parser<N>, delimiter?: number | string | RegExp
43
50
  if (result === undefined) break;
44
51
  if (context.position === pos) break;
45
52
  nodes = nodes?.import(result) ?? result;
46
- if (limit >= 0 && context.position - position > limit) break;
53
+ // 次にパースに成功すれば確実に制限値を超えるので制限値ちょうどでも中止する
54
+ if (limit > 0 && context.position - position >= limit) break;
47
55
  }
48
56
  delims && context.delimiters.pop(delims.length);
49
57
  assert(context.position >= position);
@@ -8,7 +8,11 @@ export function header(source: string): string {
8
8
 
9
9
  export function headers(source: string): string[] {
10
10
  const [el] = parse(source);
11
- return el?.textContent!.trimEnd().slice(el.firstChild!.firstChild!.textContent!.length).split(/\r?\n/) ?? [];
11
+ const acc = [];
12
+ for (let field = el?.firstChild?.firstChild; field = field?.nextSibling;) {
13
+ acc.push(field.textContent!);
14
+ }
15
+ return acc;
12
16
  }
13
17
 
14
18
  function parse(source: string): [HTMLElement, number] | [] {
@@ -125,7 +125,7 @@ describe('Unit: parser/api/parse', () => {
125
125
  '!{../../a}',
126
126
  ].join('\n\n'), { host: new URL(`${location.origin}/z`) }).children].map(el => el.outerHTML),
127
127
  [
128
- '<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="url" data-value="https://source/x/y"><span class="field-name">URL</span>: <span class="field-value">https://source/x/y</span>\n</span></details></aside>',
128
+ '<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="url" data-value="https://source/x/y"><span class="field-name">URL</span>: <span class="field-value">https://source/x/y</span></div></details></aside>',
129
129
  '<p><a class="account" href="https://source/@a" target="_blank">@a</a></p>',
130
130
  '<p><a class="account" href="https://domain/@a" target="_blank">@domain/a</a></p>',
131
131
  '<p><a class="channel" href="https://source/@a?ch=b" target="_blank">@a#b</a></p>',
@@ -158,7 +158,7 @@ describe('Unit: parser/api/parse', () => {
158
158
  '{./a}',
159
159
  ].join('\n\n'), { host: new URL(`${location.origin}/index.md`) }).children].map(el => el.outerHTML),
160
160
  [
161
- '<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="url" data-value="https://source/x/y"><span class="field-name">URL</span>: <span class="field-value">https://source/x/y</span>\n</span></details></aside>',
161
+ '<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="url" data-value="https://source/x/y"><span class="field-name">URL</span>: <span class="field-value">https://source/x/y</span></div></details></aside>',
162
162
  '<p><a class="url" href="/a">^/a</a></p>',
163
163
  '<p><a class="url" href="https://source/x/a" target="_blank">./a</a></p>',
164
164
  ]);
@@ -173,7 +173,7 @@ describe('Unit: parser/api/parse', () => {
173
173
  '{./a}',
174
174
  ].join('\n\n'), { host: new URL(`${location.origin}/z`) }).children].map(el => el.outerHTML),
175
175
  [
176
- `<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="url" data-value="${location.origin}/x/y"><span class="field-name">URL</span>: <span class="field-value">${location.origin}/x/y</span>\n</span></details></aside>`,
176
+ `<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="url" data-value="${location.origin}/x/y"><span class="field-name">URL</span>: <span class="field-value">${location.origin}/x/y</span></div></details></aside>`,
177
177
  '<p><a class="url" href="/z/a">^/a</a></p>',
178
178
  '<p><a class="url" href="/x/a">./a</a></p>',
179
179
  ]);
@@ -204,9 +204,9 @@ describe('Unit: parser/api/parse', () => {
204
204
  '{#}',
205
205
  ].join('\n\n'), { host: new URL(`${location.origin}/z`) }).children].map(el => normalize(el.outerHTML)),
206
206
  [
207
- `<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="url" data-value="https://example/x"><span class="field-name">URL</span>: <span class="field-value">https://example/x</span>\n</span></details></aside>`,
207
+ `<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="url" data-value="https://example/x"><span class="field-name">URL</span>: <span class="field-value">https://example/x</span></div></details></aside>`,
208
208
  '<pre class="invalid" translate="no">---\nURL: https://example/y\n---\n</pre>',
209
- '<aside class="example" data-type="markdown"><pre translate="no">---\nURL: https://example/y\n---\n\n{#}</pre><hr><section><aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="url" data-value="https://example/y"><span class="field-name">URL</span>: <span class="field-value">https://example/y</span>\n</span></details></aside><p><a class="url" href="https://example/y#" target="_blank">#</a></p><h2>References</h2><ol class="references"></ol></section></aside>',
209
+ '<aside class="example" data-type="markdown"><pre translate="no">---\nURL: https://example/y\n---\n\n{#}</pre><hr><section><aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="url" data-value="https://example/y"><span class="field-name">URL</span>: <span class="field-value">https://example/y</span></div></details></aside><p><a class="url" href="https://example/y#" target="_blank">#</a></p><h2>References</h2><ol class="references"></ol></section></aside>',
210
210
  '<p><a class="url" href="https://example/x#" target="_blank">#</a></p>',
211
211
  ]);
212
212
  });
@@ -380,28 +380,28 @@ describe('Unit: parser/api/parse', () => {
380
380
  // 最悪計算量での実行速度はCommonMarkの公式JS実装の32nに対して1-4倍程度。
381
381
  // 5n = reference + link + url/math + ruby + text
382
382
  assert.deepStrictEqual(
383
- [...parse(`((([[[[#$[${'.'.repeat(19998)}`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
383
+ [...parse(`((([[[[#$http://[${'.'.repeat(19992)}`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
384
384
  .map(el => el.tagName),
385
385
  ['P']);
386
386
  });
387
387
 
388
388
  it('backtrack 1 error', () => {
389
389
  assert.deepStrictEqual(
390
- [...parse(`((([[[[#$[${'.'.repeat(19998 + 1)}`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
390
+ [...parse(`((([[[[#$http://[${'.'.repeat(19992 + 1)}`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
391
391
  .map(el => el.tagName),
392
392
  ['H1', 'PRE']);
393
393
  });
394
394
 
395
395
  it('backtrack 2', () => {
396
396
  assert.deepStrictEqual(
397
- [...parse(`((([[[[#$[${'.'.repeat(19998)}]]]`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
397
+ [...parse(`((([[[[#$http://[${'.'.repeat(33324)}]]]`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
398
398
  .map(el => el.tagName),
399
399
  ['P', 'OL']);
400
400
  });
401
401
 
402
402
  it('backtrack 2 error', () => {
403
403
  assert.deepStrictEqual(
404
- [...parse(`((([[[[#$[${'.'.repeat(19998 + 1)}]]]`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
404
+ [...parse(`((([[[[#$http://[${'.'.repeat(33324 + 1)}]]]`, {}, new Context({ resources: { clock: 100000, recursions: [100] } })).children]
405
405
  .map(el => el.tagName),
406
406
  ['H1', 'PRE']);
407
407
  });
@@ -22,7 +22,7 @@ export class Context extends Ctx {
22
22
  } = options;
23
23
  this.resources ??= {
24
24
  // バックトラックのせいで文字数制限を受けないようにする。
25
- clock: MAX_SEGMENT_SIZE * (6 + 1),
25
+ clock: MAX_SEGMENT_SIZE * (5 + 1),
26
26
  recursions: [
27
27
  5 || Recursion.block,
28
28
  20 || Recursion.blockquote,
@@ -24,11 +24,11 @@ describe('Unit: parser/header', () => {
24
24
  });
25
25
 
26
26
  it('basic', () => {
27
- assert.deepStrictEqual(inspect(parser, input('---\na: b\n---', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span>\n</span></details></aside>'], '']);
28
- assert.deepStrictEqual(inspect(parser, input('---\na: b\n---\n', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span>\n</span></details></aside>'], '']);
29
- assert.deepStrictEqual(inspect(parser, input('---\na: b\nC: D e\n---\n', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span>\n</span><span class="field" data-name="c" data-value="D e"><span class="field-name">C</span>: <span class="field-value">D e</span>\n</span></details></aside>'], '']);
30
- assert.deepStrictEqual(inspect(parser, input('---\r\na: b\r\nC: D e\r\n---\r\n', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span>\n</span><span class="field" data-name="c" data-value="D e"><span class="field-name">C</span>: <span class="field-value">D e</span>\n</span></details></aside>'], '']);
31
- assert.deepStrictEqual(inspect(parser, input('----\na: b\n----', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><span class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span>\n</span></details></aside>'], '']);
27
+ assert.deepStrictEqual(inspect(parser, input('---\na: b\n---', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span></div></details></aside>'], '']);
28
+ assert.deepStrictEqual(inspect(parser, input('---\na: b\n---\n', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span></div></details></aside>'], '']);
29
+ assert.deepStrictEqual(inspect(parser, input('---\na: b\nC: D e\n---\n', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span></div><div class="field" data-name="c" data-value="D e"><span class="field-name">C</span>: <span class="field-value">D e</span></div></details></aside>'], '']);
30
+ assert.deepStrictEqual(inspect(parser, input('---\r\na: b\r\nC: D e\r\n---\r\n', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span></div><div class="field" data-name="c" data-value="D e"><span class="field-name">C</span>: <span class="field-value">D e</span></div></details></aside>'], '']);
31
+ assert.deepStrictEqual(inspect(parser, input('----\na: b\n----', new Context())), [['<aside class="header"><details open=""><summary>Header</summary><div class="field" data-name="a" data-value="b"><span class="field-name">a</span>: <span class="field-value">b</span></div></details></aside>'], '']);
32
32
  });
33
33
 
34
34
  });
@@ -11,7 +11,7 @@ export const header: MarkdownParser.HeaderParser = lazy(() => validate(
11
11
  block(
12
12
  union([
13
13
  validate(context => context.header,
14
- focus(/(---+)[^\S\r\n]*\r?\n(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*:[ \t]+\S[^\r\n]*\r?\n){1,100}\1[^\S\r\n]*(?:$|\r?\n)/yi,
14
+ focus(/(---+)[^\S\r\n]*\r?\n(?:[a-z][0-9a-z]*(?:-[0-9a-z]+)*:[ \t]+\S[^\r\n]*\r?\n){1,32}\1[^\S\r\n]*(?:$|\r?\n)/yi,
15
15
  convert(source =>
16
16
  source.slice(source.indexOf('\n') + 1, source.trimEnd().lastIndexOf('\n')),
17
17
  fmap(
@@ -42,11 +42,10 @@ const field: MarkdownParser.HeaderParser.FieldParser = line(({ source, position
42
42
  const name = source.slice(position, source.indexOf(':', position));
43
43
  const value = source.slice(position + name.length + 1).trim();
44
44
  return new List([
45
- new Node(html('span', { class: 'field', 'data-name': name.toLowerCase(), 'data-value': value }, [
45
+ new Node(html('div', { class: 'field', 'data-name': name.toLowerCase(), 'data-value': value }, [
46
46
  html('span', { class: 'field-name' }, name),
47
47
  ': ',
48
48
  html('span', { class: 'field-value' }, value),
49
- '\n',
50
49
  ])),
51
50
  ]);
52
51
  });
@@ -41,17 +41,22 @@ export const annotation: AnnotationParser = lazy(() => constraint(State.annotati
41
41
  new Node(html('span', { class: bracketname(context, 1, 1) }, defrag(unwrap(nodes))))
42
42
  ]);
43
43
  }
44
- recursion.add(MAX_DEPTH - (resources?.recursions[Recursion.bracket] ?? resources?.recursions.at(-1) ?? MAX_DEPTH));
44
+ recursion.add(
45
+ MAX_DEPTH - (resources?.recursions[Recursion.bracket] ?? resources?.recursions.at(-1) ?? MAX_DEPTH));
45
46
  context.position += 1;
46
47
  return new List([
47
- new Node(html('sup', { class: 'annotation' }, [html('span', defrag(unwrap(trimBlankNodeEnd(nodes))))]))
48
+ new Node(html('sup', { class: 'annotation' }, [
49
+ html('span', defrag(unwrap(trimBlankNodeEnd(nodes))))
50
+ ]))
48
51
  ]);
49
52
  },
50
53
  (nodes, context, prefix, postfix) => {
51
54
  assert(postfix === 0);
52
55
  for (let i = 0; i < prefix; ++i) {
53
56
  nodes.unshift(new Node('('));
54
- nodes = new List([new Node(html('span', { class: bracketname(context, 0, 0) }, defrag(unwrap(nodes))))]);
57
+ nodes = new List([
58
+ new Node(html('span', { class: bracketname(context, 0, 0) }, defrag(unwrap(nodes))))
59
+ ]);
55
60
  context.range += 1;
56
61
  }
57
62
  return nodes;
@@ -2,13 +2,12 @@ import { AutolinkParser } from '../../inline';
2
2
  import { State, Recursion, Backtrack } from '../../context';
3
3
  import { List, Node } from '../../../combinator/data/parser';
4
4
  import { union, tails, some, recursion, precedence, state, constraint, focus, rewrite, surround, open, lazy } from '../../../combinator';
5
- import { inline } from '../../inline';
6
5
  import { parse } from '../link';
7
6
  import { unescsource, str } from '../../source';
8
7
 
9
8
  export const url: AutolinkParser.UrlParser = lazy(() => rewrite(
10
9
  open(
11
- /(?<![0-9A-Za-z][.+-]?|[@#])https?:\/\/(?=[\x21-\x7E])/y,
10
+ /(?<![0-9A-Za-z][.+-]?|[@#])https?:\/\/(?=[[0-9A-Za-z])/y,
12
11
  precedence(0, some(union([
13
12
  some(unescsource, /(?<![-+*=~^_,.;:!?]|\/{3})(?:[-+*=~^_,.;:!?]|\/{3,}(?!\/))*(?=[\\$"`\[\](){}<>()[]{}|]|[^\x21-\x7E]|$)/y),
14
13
  precedence(1, bracket),
@@ -18,7 +17,7 @@ export const url: AutolinkParser.UrlParser = lazy(() => rewrite(
18
17
  union([
19
18
  constraint(State.autolink, state(State.autolink, context =>
20
19
  new List([new Node(parse(new List(), new List([new Node(context.source)]), context))]))),
21
- open(str(/[^:]+/y), some(inline)),
20
+ context => new List([new Node(context.source)]),
22
21
  ])));
23
22
 
24
23
  export const lineurl: AutolinkParser.UrlParser.LineUrlParser = lazy(() => focus(
@@ -36,7 +35,7 @@ export const lineurl: AutolinkParser.UrlParser.LineUrlParser = lazy(() => focus(
36
35
  context))
37
36
  ]);
38
37
  })),
39
- str(/[^:]+/y),
38
+ context => new List([new Node(context.source)]),
40
39
  ]),
41
40
  ])));
42
41
 
@@ -93,8 +93,8 @@ describe('Unit: parser/inline/math', () => {
93
93
  assert.deepStrictEqual(inspect(parser, input('$\\Begin$', new Context())), [['<span class="invalid" translate="no">$\\Begin$</span>'], '']);
94
94
  assert.deepStrictEqual(inspect(parser, input('$\\begin{}$', new Context())), [['<span class="invalid" translate="no">$\\begin{}$</span>'], '']);
95
95
  assert.deepStrictEqual(inspect(parser, input('${\\begin}$', new Context())), [['<span class="invalid" translate="no">${\\begin}$</span>'], '']);
96
- assert.deepStrictEqual(inspect(parser, input('$http://host$', new Context())), [['<span class="invalid" translate="no">$http://host$</span>'], '']);
97
- assert.deepStrictEqual(inspect(parser, input('${http://host}$', new Context())), [['<span class="invalid" translate="no">${http://host}$</span>'], '']);
96
+ assert.deepStrictEqual(inspect(parser, input('$http://host$', new Context())), undefined);
97
+ assert.deepStrictEqual(inspect(parser, input('${http://host}$', new Context())), undefined);
98
98
  assert.deepStrictEqual(inspect(parser, input(' ${a}$', new Context())), undefined);
99
99
  });
100
100
 
@@ -6,7 +6,7 @@ import { escsource, str } from '../source';
6
6
  import { invalid } from '../util';
7
7
  import { html } from 'typed-dom/dom';
8
8
 
9
- const forbiddenCommand = /\\(?:begin|tiny|huge|large)(?![a-z])|:\/\//i;
9
+ const forbiddenCommand = /\\(?:begin|tiny|huge|large)(?![a-z])/i;
10
10
 
11
11
  export const math: MathParser = lazy(() => rewrite(
12
12
  union([
@@ -19,7 +19,7 @@ export const math: MathParser = lazy(() => rewrite(
19
19
  surround(
20
20
  /\$(?![\s{}])/y,
21
21
  precedence(2, some(union([
22
- some(escsource, /\$|[`"{}\r\n]/y),
22
+ some(escsource, /[`"{}$\r\n]|(?<=[0-9A-Za-z]):\/\/[[0-9A-Za-z]/y),
23
23
  precedence(4, bracket),
24
24
  ]))),
25
25
  /(?<!\s)\$(?![-0-9A-Za-z])/y,
@@ -45,7 +45,7 @@ const bracket: MathParser.BracketParser = lazy(() => surround(
45
45
  recursion(Recursion.terminal,
46
46
  some(union([
47
47
  bracket,
48
- some(escsource, /[{}$\r\n]/y),
48
+ some(escsource, /[{}$\r\n]|(?<=[0-9A-Za-z]):\/\/[[0-9A-Za-z]/y),
49
49
  ]))),
50
50
  str('}'),
51
51
  true));
@@ -20,13 +20,12 @@ export const ruby: RubyParser = lazy(() => bind(
20
20
  })),
21
21
  dup(surround(
22
22
  '(', text, ')',
23
- false,
24
- [3 | Backtrack.ruby])),
23
+ false)),
25
24
  ]),
26
25
  ([{ value: texts }, { value: rubies = undefined } = {}], context) => {
27
26
  if (rubies === undefined) {
28
27
  const head = context.position - context.range;
29
- return void setBacktrack(context, 2 | Backtrack.ruby, head);
28
+ return void setBacktrack(context, 2 | Backtrack.link | Backtrack.ruby, head);
30
29
  }
31
30
  switch (true) {
32
31
  case texts.length >= rubies.length:
@@ -167,7 +167,7 @@ describe('Unit: parser/inline', () => {
167
167
  assert.deepStrictEqual(inspect(parser, input('"[% "*"* %]', new Context())), [['"', '<span class="remark"><input type="checkbox"><span>[% "*"* %]</span></span>'], '']);
168
168
  assert.deepStrictEqual(inspect(parser, input('"{{""}}', new Context())), [['"', '{', '<a class="url" href="&quot;&quot;">""</a>', '}'], '']);
169
169
  assert.deepStrictEqual(inspect(parser, input('[#http://host/(<bdi>)]</bdi>', new Context())), [['<a class="index" href="#index::http://host/(&lt;bdi&gt;)">http://host/<span class="paren">(<span class="invalid">&lt;bdi&gt;</span>)</span></a>', '</bdi', '>'], '']);
170
- assert.deepStrictEqual(inspect(parser, input('[#@a/http://host/(<bdi>)]</bdi>', new Context())), [['<a class="index" href="#index::@a/http://host/(&lt;bdi&gt;)">@a/http://host/<span class="paren">(<span class="invalid">&lt;bdi&gt;</span>)</span></a>', '</bdi', '>'], '']);
170
+ assert.deepStrictEqual(inspect(parser, input('[#@a/http://host/(<bdi>)]</bdi>', new Context())), [['<a class="index" href="#index::@a/http://host/(&lt;bdi&gt;)">@a/http://host/(&lt;bdi&gt;)</a>', '</bdi', '>'], '']);
171
171
  assert.deepStrictEqual(inspect(parser, input('[#a|<bdi>]</bdi>', new Context())), [['<a class="index" href="#index::a|&lt;bdi&gt;">a|<span class="invalid">&lt;bdi&gt;</span></a>', '</bdi', '>'], '']);
172
172
  assert.deepStrictEqual(inspect(parser, input('[[#a|<bdi>]</bdi>', new Context())), [['[', '<a class="index" href="#index::a|&lt;bdi&gt;">a|<span class="invalid">&lt;bdi&gt;</span></a>', '</bdi', '>'], '']);
173
173
  assert.deepStrictEqual(inspect(parser, input('[*==*]{a}', new Context())), [['<a class="link" href="a">*==*</a>'], '']);
@@ -68,10 +68,10 @@ export function repeat<N extends HTMLElement | string>(
68
68
  follow = follow > 0 ? follow : countFollows(source, pos, closer, lead / opener.length | 0);
69
69
  nodes = cons(nodes, context, lead, follow);
70
70
  if (context.position > pos) {
71
- const advance = opener.length * (context.position - pos) / closer.length | 0;
71
+ const advance = context.position - pos;
72
72
  i -= advance;
73
73
  follow -= advance;
74
- depth -= advance;
74
+ depth -= advance / closer.length | 0;
75
75
  }
76
76
  continue;
77
77
  }
@@ -100,10 +100,10 @@ export function repeat<N extends HTMLElement | string>(
100
100
  nodes = cons(nodes, context, lead, follow);
101
101
  state = true;
102
102
  if (context.position > pos) {
103
- const advance = opener.length * (context.position - pos) / closer.length | 0;
103
+ const advance = context.position - pos;
104
104
  i -= advance;
105
105
  follow -= advance;
106
- depth -= advance;
106
+ depth -= advance / closer.length | 0;
107
107
  }
108
108
  continue;
109
109
  }
@@ -3,13 +3,10 @@ import { Command } from '../context';
3
3
  import { Flag } from '../node';
4
4
  import { List, Node } from '../../combinator/data/parser';
5
5
  import { consume } from '../../combinator';
6
- import { next } from './text';
7
6
  import { html } from 'typed-dom/dom';
8
7
 
9
- const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s\$|:\/\/)/g;
10
-
11
8
  export const escsource: EscapableSourceParser = context => {
12
- const { source, position, state } = context;
9
+ const { source, position } = context;
13
10
  if (position === source.length) return;
14
11
  const char = source[position];
15
12
  consume(1, context);
@@ -38,7 +35,7 @@ export const escsource: EscapableSourceParser = context => {
38
35
  default:
39
36
  assert(char !== '\n');
40
37
  if (context.sequential) return new List([new Node(char)]);
41
- let i = next(source, position, state, delimiter);
38
+ let i = seek(source, position);
42
39
  assert(i > position);
43
40
  i -= position;
44
41
  consume(i - 1, context);
@@ -46,3 +43,29 @@ export const escsource: EscapableSourceParser = context => {
46
43
  return new List([new Node(source.slice(position, context.position))]);
47
44
  }
48
45
  };
46
+
47
+ function seek(source: string, position: number): number {
48
+ for (let i = position + 1; i < source.length; ++i) {
49
+ const char = source[i];
50
+ switch (char) {
51
+ case '\\':
52
+ case '$':
53
+ case '"':
54
+ case '`':
55
+ case ':':
56
+ case '[':
57
+ case ']':
58
+ case '(':
59
+ case ')':
60
+ case '{':
61
+ case '}':
62
+ case '\r':
63
+ case '\n':
64
+ return i;
65
+ default:
66
+ continue;
67
+ }
68
+ assert(false);
69
+ }
70
+ return source.length;
71
+ }
@@ -83,28 +83,20 @@ function isWhitespace(char: string, linebreak: boolean): boolean {
83
83
  }
84
84
  }
85
85
 
86
- export function next(source: string, position: number, state: number, delimiter?: RegExp): number {
87
- let index: number;
88
- if (delimiter) {
89
- delimiter.lastIndex = position + 1;
90
- delimiter.test(source);
91
- index = delimiter.lastIndex || position;
92
- }
93
- else {
94
- index = seek(source, position, state);
95
- }
96
- if (index === position || index === source.length) return source.length;
86
+ function next(source: string, position: number, state: number): number {
87
+ let index= seek(source, position, state);
97
88
  assert(index > position);
89
+ if (index === source.length) return source.length;
98
90
  const char = source[index];
99
91
  switch (char) {
100
92
  case '%':
101
- assert(source.startsWith('%]', index) && isWhitespace(source[index - 1], true) || delimiter);
102
- index += !delimiter && index - 1 > position
93
+ assert(source.startsWith('%]', index) && isWhitespace(source[index - 1], true));
94
+ index += index - 1 > position
103
95
  ? -1
104
96
  : 0;
105
97
  break;
106
98
  case '[':
107
- index += !delimiter && index - 1 > position && source.startsWith(' [|', index - 1)
99
+ index += index - 1 > position && source.startsWith(' [|', index - 1)
108
100
  ? -1
109
101
  : 0;
110
102
  break;
@@ -122,7 +114,7 @@ export function next(source: string, position: number, state: number, delimiter?
122
114
  assert(index > position);
123
115
  return index;
124
116
  }
125
- function backToUrlHead(source: string, position: number, index: number): number {
117
+ export function backToUrlHead(source: string, position: number, index: number): number {
126
118
  const delim = index;
127
119
  let state = false;
128
120
  for (let i = index - 1; i >= position; --i) {
@@ -145,7 +137,7 @@ function backToUrlHead(source: string, position: number, index: number): number
145
137
  ? delim
146
138
  : index;
147
139
  }
148
- function backToEmailHead(source: string, position: number, index: number): number {
140
+ export function backToEmailHead(source: string, position: number, index: number): number {
149
141
  const delim = index;
150
142
  let state = false;
151
143
  for (let i = index - 1; i >= position; --i) {
@@ -15,7 +15,7 @@ describe('Unit: parser/source/unescapable', () => {
15
15
  it('basic', () => {
16
16
  assert.deepStrictEqual(inspect(parser, input('a', new Context())), [['a'], '']);
17
17
  assert.deepStrictEqual(inspect(parser, input('ab', new Context())), [['ab'], '']);
18
- assert.deepStrictEqual(inspect(parser, input('a b c', new Context())), [['a', ' b', ' c'], '']);
18
+ assert.deepStrictEqual(inspect(parser, input('a b c', new Context())), [['a', ' b c'], '']);
19
19
  assert.deepStrictEqual(inspect(parser, input('09あいAZaz', new Context())), [['09', 'あいAZaz'], '']);
20
20
  });
21
21
 
@@ -1,13 +1,11 @@
1
1
  import { UnescapableSourceParser } from '../source';
2
- import { Command } from '../context';
2
+ import { State, Command } from '../context';
3
3
  import { Flag } from '../node';
4
4
  import { List, Node } from '../../combinator/data/parser';
5
5
  import { consume } from '../../combinator';
6
- import { nonWhitespace, canSkip, next } from './text';
6
+ import { nonWhitespace, canSkip, backToUrlHead, backToEmailHead } from './text';
7
7
  import { html } from 'typed-dom/dom';
8
8
 
9
- export const delimiter = /(?=(?=[\x00-\x7F])[^0-9A-Za-z]|(?<=[\x00-\x7F])[^\x00-\x7F])/g;
10
-
11
9
  export const unescsource: UnescapableSourceParser = context => {
12
10
  const { source, position, state } = context;
13
11
  if (position === source.length) return;
@@ -32,7 +30,7 @@ export const unescsource: UnescapableSourceParser = context => {
32
30
  ? nonWhitespace.test(source)
33
31
  ? nonWhitespace.lastIndex - 1
34
32
  : source.length
35
- : next(source, position, state, delimiter);
33
+ : next(source, position, state);
36
34
  assert(i > position);
37
35
  i -= position;
38
36
  consume(i - 1, context);
@@ -40,3 +38,86 @@ export const unescsource: UnescapableSourceParser = context => {
40
38
  return new List([new Node(source.slice(position, context.position))]);
41
39
  }
42
40
  };
41
+
42
+ function next(source: string, position: number, state: number): number {
43
+ let index= seek(source, position, state);
44
+ assert(index > position);
45
+ if (index === source.length) return source.length;
46
+ const char = source[index];
47
+ switch (char) {
48
+ case ':':
49
+ index = source.startsWith('//', index + 1)
50
+ ? backToUrlHead(source, position, index)
51
+ : index;
52
+ break;
53
+ case '@':
54
+ index = ~state & State.autolink
55
+ ? backToEmailHead(source, position, index)
56
+ : index;
57
+ break;
58
+ }
59
+ assert(index > position);
60
+ return index;
61
+ }
62
+
63
+ function seek(source: string, position: number, state: number): number {
64
+ const cat = category(source[position]);
65
+ for (let i = position + 1; i < source.length; ++i) {
66
+ const char = source[i];
67
+ switch (char) {
68
+ case '\\':
69
+ case '!':
70
+ case '$':
71
+ case '"':
72
+ case '`':
73
+ case '[':
74
+ case ']':
75
+ case '(':
76
+ case ')':
77
+ case '{':
78
+ case '}':
79
+ case '<':
80
+ case '>':
81
+ case '(':
82
+ case ')':
83
+ case '[':
84
+ case ']':
85
+ case '{':
86
+ case '}':
87
+ case '-':
88
+ case '+':
89
+ case '*':
90
+ case '=':
91
+ case '~':
92
+ case '^':
93
+ case '_':
94
+ case ',':
95
+ case '.':
96
+ case ';':
97
+ case ':':
98
+ case '!':
99
+ case '?':
100
+ case '/':
101
+ case '|':
102
+ case '\r':
103
+ case '\n':
104
+ return i;
105
+ case '@':
106
+ case '#':
107
+ if (~state & State.autolink) return i;
108
+ continue;
109
+ case ':':
110
+ if (source[i + 1] === '/' && source[i + 2] === '/') return i;
111
+ continue;
112
+ default:
113
+ if (cat && !category(char)) return i;
114
+ continue;
115
+ }
116
+ assert(false);
117
+ }
118
+ return source.length;
119
+ }
120
+
121
+ function category(char: string): boolean {
122
+ return '\x21' <= char && char <= '\x7E';
123
+ }