securemark 0.258.3 → 0.258.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.258.4
4
+
5
+ - Refactoring.
6
+
3
7
  ## 0.258.3
4
8
 
5
9
  - Refactoring.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! securemark v0.258.3 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
1
+ /*! securemark v0.258.4 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
2
2
  (function webpackUniversalModuleDefinition(root, factory) {
3
3
  if(typeof exports === 'object' && typeof module === 'object')
4
4
  module.exports = factory(require("DOMPurify"), require("Prism"));
@@ -2124,11 +2124,14 @@ const global_1 = __webpack_require__(4128);
2124
2124
 
2125
2125
  const parser_1 = __webpack_require__(6728);
2126
2126
 
2127
+ const memo_1 = __webpack_require__(1090);
2128
+
2127
2129
  const line_1 = __webpack_require__(9315);
2128
2130
 
2129
2131
  function block(parser, separation = true) {
2130
- return (source, context) => {
2132
+ return (source, context = {}) => {
2131
2133
  if (source === '') return;
2134
+ context.memo ??= new memo_1.Memo();
2132
2135
  const result = parser(source, context);
2133
2136
  if (!result) return;
2134
2137
  const rest = (0, parser_1.exec)(result);
@@ -2202,11 +2205,17 @@ const global_1 = __webpack_require__(4128);
2202
2205
 
2203
2206
  const parser_1 = __webpack_require__(6728);
2204
2207
 
2208
+ const memo_1 = __webpack_require__(1090);
2209
+
2205
2210
  function line(parser) {
2206
- return (source, context) => {
2211
+ return (source, context = {}) => {
2207
2212
  if (source === '') return;
2213
+ context.memo ??= new memo_1.Memo();
2208
2214
  const line = firstline(source);
2215
+ const memo = context.memo;
2216
+ memo.offset += source.length - line.length;
2209
2217
  const result = parser(line, context);
2218
+ memo.offset -= source.length - line.length;
2210
2219
  if (!result) return;
2211
2220
  return isEmpty((0, parser_1.exec)(result)) ? [(0, parser_1.eval)(result), source.slice(line.length)] : global_1.undefined;
2212
2221
  };
@@ -2412,7 +2421,10 @@ const memoize_1 = __webpack_require__(1808);
2412
2421
  function indent(opener, parser, separation = false) {
2413
2422
  if (typeof opener === 'function') return indent(/^([ \t])\1*/, opener, parser);
2414
2423
  return (0, bind_1.bind)((0, block_1.block)((0, match_1.match)(opener, (0, memoize_1.memoize)(([indent]) => (0, some_1.some)((0, line_1.line)((0, surround_1.open)(indent, source => [[source], '']))), ([indent]) => indent.length * 2 + +(indent[0] === ' '), [])), separation), (lines, rest, context) => {
2424
+ const memo = context.memo;
2425
+ memo && (memo.offset += rest.length);
2415
2426
  const result = parser(trimBlockEnd(lines.join('')), context);
2427
+ memo && (memo.offset -= rest.length);
2416
2428
  return result && (0, parser_1.exec)(result) === '' ? [(0, parser_1.eval)(result), rest] : global_1.undefined;
2417
2429
  });
2418
2430
  }
@@ -2783,7 +2795,7 @@ exports.check = check;
2783
2795
  Object.defineProperty(exports, "__esModule", ({
2784
2796
  value: true
2785
2797
  }));
2786
- exports.state = exports.guard = exports.precedence = exports.creation = exports.syntax = exports.context = exports.reset = void 0;
2798
+ exports.state = exports.constraint = exports.guard = exports.precedence = exports.creation = exports.syntax = exports.context = exports.reset = void 0;
2787
2799
 
2788
2800
  const global_1 = __webpack_require__(4128);
2789
2801
 
@@ -2852,28 +2864,28 @@ function syntax(syntax, precedence, cost, parser) {
2852
2864
  context.precedence = precedence;
2853
2865
  const {
2854
2866
  resources = {
2855
- budget: 1,
2867
+ clock: 1,
2856
2868
  recursion: 1
2857
2869
  }
2858
2870
  } = context;
2859
- if (resources.budget <= 0) throw new Error('Too many creations');
2871
+ if (resources.clock <= 0) throw new Error('Too many creations');
2860
2872
  if (resources.recursion <= 0) throw new Error('Too much recursion');
2861
2873
  --resources.recursion;
2862
- const pos = source.length;
2874
+ const position = source.length;
2863
2875
  const state = context.state ?? 0;
2864
- const cache = syntax && memo.get(pos, syntax, state);
2876
+ const cache = syntax && memo.get(position, syntax, state);
2865
2877
  const result = cache ? cache.length === 0 ? global_1.undefined : [cache[0], source.slice(cache[1])] : parser(source, context);
2866
2878
  ++resources.recursion;
2867
2879
 
2868
2880
  if (result && !cache) {
2869
- resources.budget -= cost;
2881
+ resources.clock -= cost;
2870
2882
  }
2871
2883
 
2872
2884
  if (syntax) {
2873
2885
  if (state & context.memorable) {
2874
- cache ?? memo.set(pos, syntax, state, (0, parser_1.eval)(result), source.length - (0, parser_1.exec)(result, '').length);
2875
- } else if (result && memo.length >= pos) {
2876
- memo.clear(pos);
2886
+ cache ?? memo.set(position, syntax, state, (0, parser_1.eval)(result), source.length - (0, parser_1.exec)(result, '').length);
2887
+ } else if (result && memo.length >= position) {
2888
+ memo.clear(position);
2877
2889
  }
2878
2890
  }
2879
2891
 
@@ -2889,18 +2901,18 @@ function creation(cost, parser) {
2889
2901
  return (source, context) => {
2890
2902
  const {
2891
2903
  resources = {
2892
- budget: 1,
2904
+ clock: 1,
2893
2905
  recursion: 1
2894
2906
  }
2895
2907
  } = context;
2896
- if (resources.budget <= 0) throw new Error('Too many creations');
2908
+ if (resources.clock <= 0) throw new Error('Too many creations');
2897
2909
  if (resources.recursion <= 0) throw new Error('Too much recursion');
2898
2910
  --resources.recursion;
2899
2911
  const result = parser(source, context);
2900
2912
  ++resources.recursion;
2901
2913
 
2902
2914
  if (result) {
2903
- resources.budget -= cost;
2915
+ resources.clock -= cost;
2904
2916
  }
2905
2917
 
2906
2918
  return result;
@@ -2927,6 +2939,20 @@ function guard(f, parser) {
2927
2939
 
2928
2940
  exports.guard = guard;
2929
2941
 
2942
+ function constraint(state, positive, parser) {
2943
+ if (typeof positive === 'function') {
2944
+ parser = positive;
2945
+ positive = true;
2946
+ }
2947
+
2948
+ return (source, context) => {
2949
+ const s = positive ? state & context.state : state & ~context.state;
2950
+ return s === state ? parser(source, context) : global_1.undefined;
2951
+ };
2952
+ }
2953
+
2954
+ exports.constraint = constraint;
2955
+
2930
2956
  function state(state, positive, parser) {
2931
2957
  if (typeof positive === 'function') {
2932
2958
  parser = positive;
@@ -3860,7 +3886,26 @@ const autolink_1 = __webpack_require__(6051);
3860
3886
 
3861
3887
  const source_1 = __webpack_require__(6743);
3862
3888
 
3863
- exports.autolink = (0, combinator_1.lazy)(() => (0, combinator_1.union)([autolink_1.autolink, source_1.linebreak, source_1.unescsource]));
3889
+ const delimiter = /[@#>0-9A-Za-z\n]|\S[#>]/;
3890
+
3891
+ const autolink = (source, context) => {
3892
+ if (source === '') return;
3893
+ const i = source.search(delimiter);
3894
+
3895
+ switch (i) {
3896
+ case -1:
3897
+ return [[source], ''];
3898
+
3899
+ case 0:
3900
+ return parser(source, context);
3901
+
3902
+ default:
3903
+ return [[source.slice(0, i)], source.slice(i)];
3904
+ }
3905
+ };
3906
+
3907
+ exports.autolink = autolink;
3908
+ const parser = (0, combinator_1.lazy)(() => (0, combinator_1.union)([autolink_1.autolink, source_1.linebreak, source_1.unescsource]));
3864
3909
 
3865
3910
  /***/ }),
3866
3911
 
@@ -3915,7 +3960,7 @@ const random_1 = __webpack_require__(7325);
3915
3960
 
3916
3961
  exports.block = (0, combinator_1.creation)(error((0, combinator_1.reset)({
3917
3962
  resources: {
3918
- budget: 50 * 1000,
3963
+ clock: 50 * 1000,
3919
3964
  recursion: 20
3920
3965
  }
3921
3966
  }, (0, combinator_1.union)([source_1.emptyline, horizontalrule_1.horizontalrule, heading_1.heading, ulist_1.ulist, olist_1.olist, ilist_1.ilist, dlist_1.dlist, table_1.table, codeblock_1.codeblock, mathblock_1.mathblock, extension_1.extension, sidefence_1.sidefence, blockquote_1.blockquote, reply_1.reply, paragraph_1.paragraph]))));
@@ -5315,7 +5360,7 @@ const qblock = (source, context) => {
5315
5360
  }
5316
5361
 
5317
5362
  if (child.classList.contains('cite') || child.classList.contains('quote')) {
5318
- context.resources && (context.resources.budget -= child.childNodes.length);
5363
+ context.resources && (context.resources.clock -= child.childNodes.length);
5319
5364
  nodes.splice(i, 1, ...child.childNodes);
5320
5365
  --i;
5321
5366
  continue;
@@ -5450,9 +5495,9 @@ exports.ulist = (0, combinator_1.lazy)(() => (0, combinator_1.block)((0, combina
5450
5495
  /* State.media */
5451
5496
  , exports.ulist_))));
5452
5497
  exports.ulist_ = (0, combinator_1.lazy)(() => (0, combinator_1.block)((0, combinator_1.fmap)((0, combinator_1.validate)(/^-(?=$|\s)/, (0, combinator_1.some)((0, combinator_1.creation)((0, combinator_1.union)([(0, inline_1.indexee)((0, combinator_1.fmap)((0, combinator_1.fallback)((0, combinator_1.inits)([(0, combinator_1.line)((0, combinator_1.open)(/^-(?:$|\s)/, (0, combinator_1.subsequence)([exports.checkbox, (0, visibility_1.trimBlank)((0, combinator_1.some)((0, combinator_1.union)([inline_1.indexer, inline_1.inline])))]), true)), (0, combinator_1.indent)((0, combinator_1.union)([exports.ulist_, olist_1.olist_, ilist_1.ilist_]))]), olist_1.invalid), ns => [(0, dom_1.html)('li', (0, dom_1.defrag)(fillFirstLine(ns)))]), true)])))), es => [format((0, dom_1.html)('ul', es))])));
5453
- exports.checkbox = (0, combinator_1.focus)(/^\[[xX ]\](?=$|\s)/, source => [[(0, dom_1.html)('span', {
5498
+ exports.checkbox = (0, combinator_1.creation)((0, combinator_1.focus)(/^\[[xX ]\](?=$|\s)/, source => [[(0, dom_1.html)('span', {
5454
5499
  class: 'checkbox'
5455
- }, source[1].trimStart() ? '☑' : '☐')], '']);
5500
+ }, source[1].trimStart() ? '☑' : '☐')], '']));
5456
5501
 
5457
5502
  function fillFirstLine(ns) {
5458
5503
  return ns.length === 1 && typeof ns[0] === 'object' && ['UL', 'OL'].includes(ns[0].tagName) ? (0, array_1.unshift)([(0, dom_1.html)('br')], ns) : ns;
@@ -5664,9 +5709,9 @@ const visibility_1 = __webpack_require__(7618);
5664
5709
 
5665
5710
  const dom_1 = __webpack_require__(3252);
5666
5711
 
5667
- exports.annotation = (0, combinator_1.lazy)(() => (0, combinator_1.surround)('((', (0, combinator_1.guard)(context => ~context.state & 64
5712
+ exports.annotation = (0, combinator_1.lazy)(() => (0, combinator_1.surround)('((', (0, combinator_1.constraint)(64
5668
5713
  /* State.annotation */
5669
- , (0, combinator_1.syntax)(32
5714
+ , false, (0, combinator_1.syntax)(32
5670
5715
  /* Syntax.annotation */
5671
5716
  , 6, 1, (0, combinator_1.state)(64
5672
5717
  /* State.annotation */
@@ -5711,12 +5756,12 @@ const source_1 = __webpack_require__(6743);
5711
5756
 
5712
5757
  const util_1 = __webpack_require__(9437);
5713
5758
 
5714
- exports.autolink = (0, combinator_1.fmap)((0, combinator_1.validate)(/^(?:[@#>0-9A-Za-z]|\S#)/, (0, combinator_1.guard)(context => ~context.state & 1
5759
+ exports.autolink = (0, combinator_1.fmap)((0, combinator_1.validate)(/^(?:[@#>0-9A-Za-z]|\S[#>])/, (0, combinator_1.constraint)(1
5715
5760
  /* State.autolink */
5716
- , (0, combinator_1.syntax)(2
5761
+ , false, (0, combinator_1.syntax)(2
5717
5762
  /* Syntax.autolink */
5718
5763
  , 1, 1, (0, combinator_1.some)((0, combinator_1.union)([url_1.url, email_1.email, // Escape unmatched email-like strings.
5719
- (0, source_1.str)(/^[0-9A-Za-z]+(?:[.+_-][0-9A-Za-z]+)*(?:@(?:[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?)+/), channel_1.channel, account_1.account, // Escape unmatched account-like strings.
5764
+ (0, source_1.str)(/^[0-9A-Za-z]+(?:[.+_-][0-9A-Za-z]+)*(?:@(?:[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?)*/), channel_1.channel, account_1.account, // Escape unmatched account-like strings.
5720
5765
  (0, source_1.str)(/^@+[0-9A-Za-z]*(?:-[0-9A-Za-z]+)*/), // Escape invalid leading characters.
5721
5766
  (0, source_1.str)(new RegExp(/^(?:[^\p{C}\p{S}\p{P}\s]|emoji|['_])(?=#)/u.source.replace('emoji', hashtag_1.emoji), 'u')), hashtag_1.hashtag, hashnum_1.hashnum, // Escape unmatched hashtag-like strings.
5722
5767
  (0, source_1.str)(new RegExp(/^#+(?:[^\p{C}\p{S}\p{P}\s]|emoji|['_])*/u.source.replace('emoji', hashtag_1.emoji), 'u')), anchor_1.anchor]))))), ns => ns.length === 1 ? ns : [(0, util_1.stringify)(ns)]);
@@ -6216,9 +6261,9 @@ const visibility_1 = __webpack_require__(7618);
6216
6261
 
6217
6262
  const dom_1 = __webpack_require__(3252);
6218
6263
 
6219
- exports.index = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('[#', (0, combinator_1.fmap)((0, indexee_1.indexee)((0, combinator_1.surround)('[#', (0, combinator_1.guard)(context => ~context.state & 16
6264
+ exports.index = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('[#', (0, combinator_1.fmap)((0, indexee_1.indexee)((0, combinator_1.surround)('[#', (0, combinator_1.constraint)(16
6220
6265
  /* State.index */
6221
- , (0, combinator_1.syntax)(1024
6266
+ , false, (0, combinator_1.syntax)(1024
6222
6267
  /* Syntax.index */
6223
6268
  , 2, 1, (0, combinator_1.state)(64
6224
6269
  /* State.annotation */
@@ -6377,9 +6422,9 @@ const dom_1 = __webpack_require__(3252);
6377
6422
 
6378
6423
  const body = (0, source_1.str)(/^\$[A-Za-z]*(?:(?:-[A-Za-z][0-9A-Za-z]*)+|-(?:(?:0|[1-9][0-9]*)\.)*(?:0|[1-9][0-9]*)(?![0-9A-Za-z]))/);
6379
6424
  exports.segment = (0, combinator_1.clear)((0, combinator_1.validate)(['[$', '$'], (0, combinator_1.union)([(0, combinator_1.surround)('[', body, ']'), body])));
6380
- exports.label = (0, combinator_1.validate)(['[$', '$'], (0, combinator_1.creation)((0, combinator_1.fmap)((0, combinator_1.guard)(context => ~context.state & 8
6425
+ exports.label = (0, combinator_1.validate)(['[$', '$'], (0, combinator_1.creation)((0, combinator_1.fmap)((0, combinator_1.constraint)(8
6381
6426
  /* State.label */
6382
- , (0, combinator_1.union)([(0, combinator_1.surround)('[', body, ']'), body])), ([text]) => [(0, dom_1.html)('a', {
6427
+ , false, (0, combinator_1.union)([(0, combinator_1.surround)('[', body, ']'), body])), ([text]) => [(0, dom_1.html)('a', {
6383
6428
  class: 'label',
6384
6429
  'data-label': text.slice(text[1] === '-' ? 0 : 1).toLowerCase()
6385
6430
  }, text)])));
@@ -6485,7 +6530,7 @@ global_1.Object.setPrototypeOf(attrspecs, null);
6485
6530
  global_1.Object.values(attrspecs).forEach(o => global_1.Object.setPrototypeOf(o, null));
6486
6531
  exports.html = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('<', (0, combinator_1.validate)(/^<[a-z]+(?=[^\S\n]|>)/, (0, combinator_1.syntax)(0
6487
6532
  /* Syntax.none */
6488
- , 5, 1, (0, combinator_1.union)([(0, combinator_1.focus)('<wbr>', () => [[(0, dom_1.html)('wbr')], '']), (0, combinator_1.focus)( // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
6533
+ , 5, 1, (0, combinator_1.union)([(0, combinator_1.focus)(/^<wbr[^\S\n]*>/, () => [[(0, dom_1.html)('wbr')], '']), (0, combinator_1.focus)( // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
6489
6534
  /^<(?:area|base|br|col|embed|hr|img|input|link|meta|source|track|wbr)(?=[^\S\n]|>)/, source => [[source], '']), (0, combinator_1.match)(new RegExp(String.raw`^<(${TAGS.join('|')})(?=[^\S\n]|>)`), (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)(/^[^\S\n]*>/), true), (0, combinator_1.subsequence)([(0, combinator_1.focus)(/^[^\S\n]*\n/, (0, combinator_1.some)(inline_1.inline)), (0, combinator_1.some)((0, combinator_1.open)(/^\n?/, (0, combinator_1.some)(inline_1.inline, (0, visibility_1.blankWith)('\n', `</${tag}>`), [[(0, visibility_1.blankWith)('\n', `</${tag}>`), 5]]), true))]), (0, source_1.str)(`</${tag}>`), true, ([as, bs = [], cs], rest) => [[elem(tag, as, bs, cs)], rest], ([as, bs = []], rest) => [[elem(tag, as, bs, [])], rest]), ([, tag]) => TAGS.indexOf(tag), [])), (0, combinator_1.match)(/^<([a-z]+)(?=[^\S\n]|>)/, (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${tag}`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)(/^[^\S\n]*>/), true), (0, combinator_1.subsequence)([(0, combinator_1.focus)(/^[^\S\n]*\n/, (0, combinator_1.some)(inline_1.inline)), (0, combinator_1.some)(inline_1.inline, `</${tag}>`, [[`</${tag}>`, 5]])]), (0, source_1.str)(`</${tag}>`), true, ([as, bs = [], cs], rest) => [[elem(tag, as, bs, cs)], rest], ([as, bs = []], rest) => [[elem(tag, as, bs, [])], rest]), ([, tag]) => tag, new cache_1.Cache(10000)))])))));
6490
6535
  exports.attribute = (0, combinator_1.union)([(0, source_1.str)(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|>)/)]); // https://developer.mozilla.org/en-US/docs/Web/HTML/Element
6491
6536
  // [...document.querySelectorAll('tbody > tr > td:first-child')].map(el => el.textContent.slice(1, -1))
@@ -6643,9 +6688,9 @@ const optspec = {
6643
6688
  rel: ['nofollow']
6644
6689
  };
6645
6690
  Object.setPrototypeOf(optspec, null);
6646
- exports.link = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[', '{'], (0, combinator_1.bind)((0, combinator_1.guard)(context => ~context.state & 4
6691
+ exports.link = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[', '{'], (0, combinator_1.bind)((0, combinator_1.constraint)(4
6647
6692
  /* State.link */
6648
- , (0, combinator_1.syntax)(256
6693
+ , false, (0, combinator_1.syntax)(256
6649
6694
  /* Syntax.link */
6650
6695
  , 2, 10, (0, combinator_1.fmap)((0, combinator_1.subsequence)([(0, combinator_1.state)(4
6651
6696
  /* State.link */
@@ -6881,9 +6926,9 @@ const optspec = {
6881
6926
  rel: global_1.undefined
6882
6927
  };
6883
6928
  Object.setPrototypeOf(optspec, null);
6884
- exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['![', '!{'], (0, combinator_1.bind)((0, combinator_1.verify)((0, combinator_1.fmap)((0, combinator_1.open)('!', (0, combinator_1.guard)(context => ~context.state & 2
6929
+ exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['![', '!{'], (0, combinator_1.bind)((0, combinator_1.verify)((0, combinator_1.fmap)((0, combinator_1.open)('!', (0, combinator_1.constraint)(2
6885
6930
  /* State.media */
6886
- , (0, combinator_1.syntax)(64
6931
+ , false, (0, combinator_1.syntax)(64
6887
6932
  /* Syntax.media */
6888
6933
  , 2, 10, (0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.some)((0, combinator_1.union)([htmlentity_1.unsafehtmlentity, bracket, source_1.txt]), ']', [[/^\\?\n/, 9]]), ']', true)), (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([link_1.uri, (0, combinator_1.some)(option)]), /^[^\S\n]*}/))])))), ([as, bs]) => bs ? [[as.join('').trim() || as.join('')], (0, array_1.shift)(bs)[1]] : [[''], (0, array_1.shift)(as)[1]]), ([[text]]) => text === '' || text.trim() !== ''), ([[text], params], rest, context) => {
6889
6934
  const INSECURE_URI = params.shift();
@@ -6982,9 +7027,9 @@ const util_1 = __webpack_require__(9437);
6982
7027
 
6983
7028
  const dom_1 = __webpack_require__(3252);
6984
7029
 
6985
- exports.reference = (0, combinator_1.lazy)(() => (0, combinator_1.surround)('[[', (0, combinator_1.guard)(context => ~context.state & 32
7030
+ exports.reference = (0, combinator_1.lazy)(() => (0, combinator_1.surround)('[[', (0, combinator_1.constraint)(32
6986
7031
  /* State.reference */
6987
- , (0, combinator_1.syntax)(4096
7032
+ , false, (0, combinator_1.syntax)(4096
6988
7033
  /* Syntax.reference */
6989
7034
  , 6, 1, (0, combinator_1.state)(64
6990
7035
  /* State.annotation */
@@ -7131,9 +7176,9 @@ const url_1 = __webpack_require__(4318);
7131
7176
 
7132
7177
  const media_1 = __webpack_require__(1303);
7133
7178
 
7134
- exports.shortmedia = (0, combinator_1.rewrite)((0, combinator_1.guard)(context => ~context.state & 2
7179
+ exports.shortmedia = (0, combinator_1.rewrite)((0, combinator_1.constraint)(2
7135
7180
  /* State.media */
7136
- , (0, combinator_1.open)('!', url_1.url)), (0, combinator_1.convert)(source => `!{ ${source.slice(1)} }`, (0, combinator_1.union)([media_1.media])));
7181
+ , false, (0, combinator_1.open)('!', url_1.url)), (0, combinator_1.convert)(source => `!{ ${source.slice(1)} }`, (0, combinator_1.union)([media_1.media])));
7137
7182
 
7138
7183
  /***/ }),
7139
7184
 
@@ -7965,9 +8010,9 @@ const str_1 = __webpack_require__(2790);
7965
8010
 
7966
8011
  const dom_1 = __webpack_require__(3252);
7967
8012
 
7968
- exports.delimiter = /[\s\x00-\x7F]|\S#|[()、。!?][^\S\n]*(?=\\\n)/;
8013
+ exports.delimiter = /[\s\x00-\x7F]|\S[#>]|[()、。!?][^\S\n]*(?=\\\n)/;
7969
8014
  exports.nonWhitespace = /[\S\n]|$/;
7970
- exports.nonAlphanumeric = /[^0-9A-Za-z]|\S#|$/;
8015
+ exports.nonAlphanumeric = /[^0-9A-Za-z]|\S[#>]|$/;
7971
8016
  const repeat = (0, str_1.str)(/^(.)\1*/);
7972
8017
  exports.text = (0, combinator_1.creation)((source, context) => {
7973
8018
  if (source === '') return;
@@ -8146,7 +8191,7 @@ const memoize_1 = __webpack_require__(1808);
8146
8191
  const array_1 = __webpack_require__(8112);
8147
8192
 
8148
8193
  function visualize(parser) {
8149
- const blankline = new RegExp(/^(?:\\$|\\?[^\S\n]|&IHN;|<wbr>)+$/.source.replace('IHN', `(?:${normalize_1.invisibleHTMLEntityNames.join('|')})`), 'gm');
8194
+ const blankline = new RegExp(/^(?:\\$|\\?[^\S\n]|&IHN;|<wbr[^\S\n]*>)+$/.source.replace('IHN', `(?:${normalize_1.invisibleHTMLEntityNames.join('|')})`), 'gm');
8150
8195
  return (0, combinator_1.union)([(0, combinator_1.convert)(source => source.replace(blankline, line => line.replace(/[\\&<]/g, '\x1B$&')), (0, combinator_1.verify)(parser, (ns, rest, context) => !rest && hasVisible(ns, context))), (0, combinator_1.some)((0, combinator_1.union)([source_1.linebreak, source_1.unescsource]))]);
8151
8196
  }
8152
8197
 
@@ -8173,11 +8218,11 @@ function hasVisible(nodes, {
8173
8218
  return false;
8174
8219
  }
8175
8220
 
8176
- exports.regBlankStart = new RegExp(/^(?:\\?[^\S\n]|&IHN;|<wbr>)+/.source.replace('IHN', `(?:${normalize_1.invisibleHTMLEntityNames.join('|')})`));
8221
+ exports.regBlankStart = new RegExp(/^(?:\\?[^\S\n]|&IHN;|<wbr[^\S\n]*>)+/.source.replace('IHN', `(?:${normalize_1.invisibleHTMLEntityNames.join('|')})`));
8177
8222
 
8178
8223
  function blankWith(starting, delimiter) {
8179
8224
  if (delimiter === global_1.undefined) return blankWith('', starting);
8180
- return new RegExp(String.raw`^(?:(?=${starting})(?:\\?\s|&(?:${normalize_1.invisibleHTMLEntityNames.join('|')});|<wbr>)${starting && '+'})?${typeof delimiter === 'string' ? delimiter.replace(/[*+()\[\]]/g, '\\$&') : delimiter.source}`);
8225
+ return new RegExp(String.raw`^(?:(?=${starting})(?:\\?\s|&(?:${normalize_1.invisibleHTMLEntityNames.join('|')});|<wbr[^\S\n]*>)${starting && '+'})?${typeof delimiter === 'string' ? delimiter.replace(/[*+()\[\]]/g, '\\$&') : delimiter.source}`);
8181
8226
  }
8182
8227
 
8183
8228
  exports.blankWith = blankWith;
@@ -8220,7 +8265,7 @@ const isStartTight = (0, memoize_1.reduce)((source, context, except) => {
8220
8265
 
8221
8266
  case '<':
8222
8267
  switch (true) {
8223
- case source.length >= 5 && source[1] === 'w' && source.slice(0, 5) === '<wbr>':
8268
+ case source.length >= 5 && source.slice(0, 4) === '<wbr' && (source[5] === '>' || /^<wbr[^\S\n]*>/.test(source)):
8224
8269
  return false;
8225
8270
  }
8226
8271
 
package/markdown.d.ts CHANGED
@@ -964,7 +964,7 @@ export namespace MarkdownParser {
964
964
  Inline<'html'>,
965
965
  Parser<HTMLElement | string, Context, [
966
966
  HTMLParser.OpenTagParser,
967
- SourceParser.StrParser,
967
+ HTMLParser.OpenTagParser,
968
968
  HTMLParser.TagParser,
969
969
  HTMLParser.TagParser,
970
970
  ]> {
@@ -972,8 +972,8 @@ export namespace MarkdownParser {
972
972
  export namespace HTMLParser {
973
973
  export interface OpenTagParser extends
974
974
  Inline<'html/opentag'>,
975
- Parser<HTMLElement, Context, [
976
- TagParser.AttributeParser,
975
+ Parser<HTMLElement | string, Context, [
976
+ AttributeParser,
977
977
  ]> {
978
978
  }
979
979
  export interface TagParser extends
@@ -983,13 +983,11 @@ export namespace MarkdownParser {
983
983
  InlineParser,
984
984
  ]> {
985
985
  }
986
- export namespace TagParser {
987
- export interface AttributeParser extends
988
- Inline<'html/tag/attribute'>,
989
- Parser<string, Context, [
990
- SourceParser.StrParser,
991
- ]> {
992
- }
986
+ export interface AttributeParser extends
987
+ Inline<'html/attribute'>,
988
+ Parser<string, Context, [
989
+ SourceParser.StrParser,
990
+ ]> {
993
991
  }
994
992
  }
995
993
  export interface InsertionParser extends
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.258.3",
3
+ "version": "0.258.4",
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",
@@ -1,12 +1,14 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { Parser, exec } from '../../data/parser';
3
+ import { Memo } from '../../data/parser/context/memo';
3
4
  import { firstline, isEmpty } from './line';
4
5
 
5
6
  export function block<P extends Parser<unknown>>(parser: P, separation?: boolean): P;
6
7
  export function block<T>(parser: Parser<T>, separation = true): Parser<T> {
7
8
  assert(parser);
8
- return (source, context) => {
9
+ return (source, context = {}) => {
9
10
  if (source === '') return;
11
+ context.memo ??= new Memo();
10
12
  const result = parser(source, context);
11
13
  if (!result) return;
12
14
  const rest = exec(result);
@@ -1,14 +1,19 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { Parser, eval, exec, check } from '../../data/parser';
3
+ import { Memo } from '../../data/parser/context/memo';
3
4
 
4
5
  export function line<P extends Parser<unknown>>(parser: P): P;
5
6
  export function line<T>(parser: Parser<T>): Parser<T> {
6
7
  assert(parser);
7
- return (source, context) => {
8
+ return (source, context = {}) => {
8
9
  if (source === '') return;
10
+ context.memo ??= new Memo();
9
11
  const line = firstline(source);
12
+ const memo = context.memo!;
13
+ memo.offset += source.length - line.length;
10
14
  const result = parser(line, context);
11
15
  assert(check(line, result));
16
+ memo.offset -= source.length - line.length;
12
17
  if (!result) return;
13
18
  return isEmpty(exec(result))
14
19
  ? [eval(result), source.slice(line.length)]
@@ -21,7 +21,10 @@ export function indent<T>(opener: RegExp | Parser<T>, parser?: Parser<T> | boole
21
21
  ([indent]) => indent.length * 2 + +(indent[0] === ' '), [])), separation),
22
22
  (lines, rest, context) => {
23
23
  assert(parser = parser as Parser<T>);
24
+ const memo = context.memo;
25
+ memo && (memo.offset += rest.length);
24
26
  const result = parser(trimBlockEnd(lines.join('')), context);
27
+ memo && (memo.offset -= rest.length);
25
28
  return result && exec(result) === ''
26
29
  ? [eval(result), rest]
27
30
  : undefined;
@@ -10,27 +10,27 @@ describe('Unit: combinator/data/parser/context', () => {
10
10
 
11
11
  describe('reset', () => {
12
12
  const parser: Parser<number> = some(creation(
13
- (s, context) => [[context.resources?.budget ?? NaN], s.slice(1)]));
13
+ (s, context) => [[context.resources?.clock ?? NaN], s.slice(1)]));
14
14
 
15
15
  it('root', () => {
16
- const base: Context = { resources: { budget: 3, recursion: 1 } };
16
+ const base: Context = { resources: { clock: 3, recursion: 1 } };
17
17
  const ctx: Context = {};
18
18
  assert.deepStrictEqual(reset(base, parser)('123', ctx), [[3, 2, 1], '']);
19
- assert(base.resources?.budget === 3);
20
- assert(ctx.resources?.budget === undefined);
19
+ assert(base.resources?.clock === 3);
20
+ assert(ctx.resources?.clock === undefined);
21
21
  assert.throws(() => reset(base, parser)('1234', ctx));
22
- assert(ctx.resources?.budget === undefined);
22
+ assert(ctx.resources?.clock === undefined);
23
23
  assert.deepStrictEqual(reset(base, parser)('123', ctx), [[3, 2, 1], '']);
24
24
  });
25
25
 
26
26
  it('node', () => {
27
- const base: Context = { resources: { budget: 3, recursion: 1 } };
28
- const ctx: Context = { resources: { budget: 1, recursion: 1 } };
27
+ const base: Context = { resources: { clock: 3, recursion: 1 } };
28
+ const ctx: Context = { resources: { clock: 1, recursion: 1 } };
29
29
  assert.deepStrictEqual(reset(base, parser)('1', ctx), [[1], '']);
30
- assert(base.resources?.budget === 3);
31
- assert(ctx.resources?.budget === 0);
30
+ assert(base.resources?.clock === 3);
31
+ assert(ctx.resources?.clock === 0);
32
32
  assert.throws(() => reset(base, parser)('1', ctx));
33
- assert(ctx.resources?.budget === 0);
33
+ assert(ctx.resources?.clock === 0);
34
34
  });
35
35
 
36
36
  });
@@ -41,12 +41,12 @@ describe('Unit: combinator/data/parser/context', () => {
41
41
 
42
42
  it('', () => {
43
43
  const base: Context = { status: true };
44
- const ctx: Context = { resources: { budget: 3, recursion: 1 } };
44
+ const ctx: Context = { resources: { clock: 3, recursion: 1 } };
45
45
  assert.deepStrictEqual(context(base, parser)('123', ctx), [[true, true, true], '']);
46
- assert(ctx.resources?.budget === 0);
46
+ assert(ctx.resources?.clock === 0);
47
47
  assert(ctx.status === undefined);
48
48
  assert.throws(() => reset(base, parser)('1', ctx));
49
- assert(ctx.resources?.budget === 0);
49
+ assert(ctx.resources?.clock === 0);
50
50
  assert(ctx.status === undefined);
51
51
  });
52
52
 
@@ -64,13 +64,13 @@ export function syntax<T>(syntax: number, precedence: number, cost: number, pars
64
64
  context.memorable ??= ~0;
65
65
  const p = context.precedence;
66
66
  context.precedence = precedence;
67
- const { resources = { budget: 1, recursion: 1 } } = context;
68
- if (resources.budget <= 0) throw new Error('Too many creations');
67
+ const { resources = { clock: 1, recursion: 1 } } = context;
68
+ if (resources.clock <= 0) throw new Error('Too many creations');
69
69
  if (resources.recursion <= 0) throw new Error('Too much recursion');
70
70
  --resources.recursion;
71
- const pos = source.length;
71
+ const position = source.length;
72
72
  const state = context.state ?? 0;
73
- const cache = syntax && memo.get(pos, syntax, state);
73
+ const cache = syntax && memo.get(position, syntax, state);
74
74
  const result: Result<T> = cache
75
75
  ? cache.length === 0
76
76
  ? undefined
@@ -78,16 +78,16 @@ export function syntax<T>(syntax: number, precedence: number, cost: number, pars
78
78
  : parser!(source, context);
79
79
  ++resources.recursion;
80
80
  if (result && !cache) {
81
- resources.budget -= cost;
81
+ resources.clock -= cost;
82
82
  }
83
83
  if (syntax) {
84
84
  if (state & context.memorable!) {
85
- cache ?? memo.set(pos, syntax, state, eval(result), source.length - exec(result, '').length);
86
- assert.deepStrictEqual(cache && cache, cache && memo.get(pos, syntax, state));
85
+ cache ?? memo.set(position, syntax, state, eval(result), source.length - exec(result, '').length);
86
+ assert.deepStrictEqual(cache && cache, cache && memo.get(position, syntax, state));
87
87
  }
88
- else if (result && memo.length! >= pos) {
88
+ else if (result && memo.length! >= position) {
89
89
  assert(!(state & context.memorable!));
90
- memo.clear(pos);
90
+ memo.clear(position);
91
91
  }
92
92
  }
93
93
  context.precedence = p;
@@ -101,14 +101,14 @@ export function creation(cost: number | Parser<unknown>, parser?: Parser<unknown
101
101
  if (typeof cost === 'function') return creation(1, cost);
102
102
  assert(cost >= 0);
103
103
  return (source, context) => {
104
- const { resources = { budget: 1, recursion: 1 } } = context;
105
- if (resources.budget <= 0) throw new Error('Too many creations');
104
+ const { resources = { clock: 1, recursion: 1 } } = context;
105
+ if (resources.clock <= 0) throw new Error('Too many creations');
106
106
  if (resources.recursion <= 0) throw new Error('Too much recursion');
107
107
  --resources.recursion;
108
108
  const result = parser!(source, context);
109
109
  ++resources.recursion;
110
110
  if (result) {
111
- resources.budget -= cost;
111
+ resources.clock -= cost;
112
112
  }
113
113
  return result;
114
114
  };
@@ -133,6 +133,24 @@ export function guard<T>(f: (context: Ctx) => boolean | number, parser: Parser<T
133
133
  : undefined;
134
134
  }
135
135
 
136
+ export function constraint<P extends Parser<unknown>>(state: number, parser: P): P;
137
+ export function constraint<P extends Parser<unknown>>(state: number, positive: boolean, parser: P): P;
138
+ export function constraint<T>(state: number, positive: boolean | Parser<T>, parser?: Parser<T>): Parser<T> {
139
+ if (typeof positive === 'function') {
140
+ parser = positive;
141
+ positive = true;
142
+ }
143
+ assert(state);
144
+ return (source, context) => {
145
+ const s = positive
146
+ ? state & context.state!
147
+ : state & ~context.state!;
148
+ return s === state
149
+ ? parser!(source, context)
150
+ : undefined;
151
+ };
152
+ }
153
+
136
154
  export function state<P extends Parser<unknown>>(state: number, parser: P): P;
137
155
  export function state<P extends Parser<unknown>>(state: number, positive: boolean, parser: P): P;
138
156
  export function state<T>(state: number, positive: boolean | Parser<T>, parser?: Parser<T>): Parser<T> {
@@ -140,6 +158,7 @@ export function state<T>(state: number, positive: boolean | Parser<T>, parser?:
140
158
  parser = positive;
141
159
  positive = true;
142
160
  }
161
+ assert(state);
143
162
  return (source, context) => {
144
163
  const s = context.state ?? 0;
145
164
  context.state = positive
@@ -9,7 +9,7 @@ export type Result<T, C extends Ctx = Ctx, D extends Parser<unknown, C>[] = any>
9
9
  | undefined;
10
10
  export interface Ctx {
11
11
  readonly resources?: {
12
- budget: number;
12
+ clock: number;
13
13
  recursion: number;
14
14
  };
15
15
  precedence?: number;
@@ -13,13 +13,14 @@ describe('Unit: parser/autolink', () => {
13
13
  assert.deepStrictEqual(inspect(parser('@a#b')), [['<a href="/@a?ch=b" class="channel">@a#b</a>'], '']);
14
14
  assert.deepStrictEqual(inspect(parser('\\\n')), [['\\', '<br>'], '']);
15
15
  assert.deepStrictEqual(inspect(parser('a#b')), [['a#b'], '']);
16
- assert.deepStrictEqual(inspect(parser('0a#b')), [['0', 'a#b'], '']);
16
+ assert.deepStrictEqual(inspect(parser('0a#b')), [['0a#b'], '']);
17
17
  assert.deepStrictEqual(inspect(parser('あ#b')), [['あ#b'], '']);
18
18
  assert.deepStrictEqual(inspect(parser('あい#b')), [['あ', 'い#b'], '']);
19
- assert.deepStrictEqual(inspect(parser('0aあ#b')), [['0a', 'あ#b'], '']);
19
+ assert.deepStrictEqual(inspect(parser('0aあ#b')), [['0aあ#b'], '']);
20
20
  assert.deepStrictEqual(inspect(parser('0aあい#b')), [['0a', 'あ', 'い#b'], '']);
21
21
  assert.deepStrictEqual(inspect(parser('a\n#b')), [['a', '<br>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
22
22
  assert.deepStrictEqual(inspect(parser('a\\\n#b')), [['a', '\\', '<br>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
23
+ assert.deepStrictEqual(inspect(parser('0a>>b')), [['0a>>b'], '']);
23
24
  });
24
25
 
25
26
  });
@@ -5,7 +5,23 @@ import { linebreak, unescsource } from './source';
5
5
 
6
6
  export import AutolinkParser = MarkdownParser.AutolinkParser;
7
7
 
8
- export const autolink: AutolinkParser = lazy(() => union([
8
+ const delimiter = /[@#>0-9A-Za-z\n]|\S[#>]/;
9
+
10
+ export const autolink: AutolinkParser = (source, context) => {
11
+ if (source === '') return;
12
+ assert(source[0] !== '\x1B');
13
+ const i = source.search(delimiter);
14
+ switch (i) {
15
+ case -1:
16
+ return [[source], ''];
17
+ case 0:
18
+ return parser(source, context);
19
+ default:
20
+ return [[source.slice(0, i)], source.slice(i)];
21
+ }
22
+ };
23
+
24
+ const parser: AutolinkParser = lazy(() => union([
9
25
  autolink_,
10
26
  linebreak,
11
27
  unescsource
@@ -45,13 +45,15 @@ describe('Unit: parser/block/paragraph', () => {
45
45
  assert.deepStrictEqual(inspect(parser('>>11 a')), [['<p><a href="?at=11" class="anchor">&gt;&gt;11</a> a</p>'], '']);
46
46
  assert.deepStrictEqual(inspect(parser('>>>11 a')), [['<p>&gt;<a href="?at=11" class="anchor">&gt;&gt;11</a> a</p>'], '']);
47
47
  assert.deepStrictEqual(inspect(parser('>> a\n>>1')), [['<p>&gt;&gt; a<br><a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
48
- assert.deepStrictEqual(inspect(parser('a>>1')), [['<p>a<a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
48
+ assert.deepStrictEqual(inspect(parser('a>>1')), [['<p>a&gt;&gt;1</p>'], '']);
49
+ assert.deepStrictEqual(inspect(parser('ab>>1')), [['<p>ab&gt;&gt;1</p>'], '']);
49
50
  assert.deepStrictEqual(inspect(parser('a >>1')), [['<p>a <a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
50
51
  assert.deepStrictEqual(inspect(parser('a\n>>1')), [['<p>a<br><a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
51
52
  assert.deepStrictEqual(inspect(parser('a\n>>1\nb')), [['<p>a<br><a href="?at=1" class="anchor">&gt;&gt;1</a><br>b</p>'], '']);
52
53
  assert.deepStrictEqual(inspect(parser('a\n>> b\nc')), [['<p>a<br>&gt;&gt; b<br>c</p>'], '']);
53
54
  assert.deepStrictEqual(inspect(parser('\t>>1')), [['<p>\t<a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
54
55
  assert.deepStrictEqual(inspect(parser('\t>>>1')), [['<p>\t&gt;<a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
56
+ assert.deepStrictEqual(inspect(parser('あ>>1')), [['<p>あ<a href="?at=1" class="anchor">&gt;&gt;1</a></p>'], '']);
55
57
  });
56
58
 
57
59
  it('comment', () => {
@@ -58,7 +58,7 @@ const qblock: ReplyParser.QuoteParser.BlockParser = (source, context) => {
58
58
  continue;
59
59
  }
60
60
  if (child.classList.contains('cite') || child.classList.contains('quote')) {
61
- context.resources && (context.resources.budget -= child.childNodes.length);
61
+ context.resources && (context.resources.clock -= child.childNodes.length);
62
62
  nodes.splice(i, 1, ...child.childNodes as NodeListOf<HTMLElement>);
63
63
  --i;
64
64
  continue;
@@ -26,11 +26,11 @@ export const ulist_: UListParser = lazy(() => block(fmap(validate(
26
26
  ])))),
27
27
  es => [format(html('ul', es))])));
28
28
 
29
- export const checkbox = focus(
29
+ export const checkbox = creation(focus(
30
30
  /^\[[xX ]\](?=$|\s)/,
31
31
  source => [[
32
32
  html('span', { class: 'checkbox' }, source[1].trimStart() ? '☑' : '☐'),
33
- ], '']);
33
+ ], '']));
34
34
 
35
35
  export function fillFirstLine(ns: (HTMLElement | string)[]): (HTMLElement | string)[] {
36
36
  return ns.length === 1
@@ -36,7 +36,7 @@ export import ReplyParser = BlockParser.ReplyParser;
36
36
  export import ParagraphParser = BlockParser.ParagraphParser;
37
37
 
38
38
  export const block: BlockParser = creation(error(
39
- reset({ resources: { budget: 50 * 1000, recursion: 20 } },
39
+ reset({ resources: { clock: 50 * 1000, recursion: 20 } },
40
40
  union([
41
41
  emptyline,
42
42
  horizontalrule,
@@ -1,6 +1,6 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { AnnotationParser } from '../inline';
3
- import { union, some, guard, context, syntax, state, surround, lazy } from '../../combinator';
3
+ import { union, some, context, syntax, constraint, state, surround, lazy } from '../../combinator';
4
4
  import { inline } from '../inline';
5
5
  import { optimize } from './link';
6
6
  import { Syntax, State } from '../context';
@@ -9,7 +9,7 @@ import { html, defrag } from 'typed-dom/dom';
9
9
 
10
10
  export const annotation: AnnotationParser = lazy(() => surround(
11
11
  '((',
12
- guard(context => ~context.state! & State.annotation,
12
+ constraint(State.annotation, false,
13
13
  syntax(Syntax.annotation, 6, 1,
14
14
  state(State.annotation | State.media,
15
15
  startLoose(
@@ -20,9 +20,9 @@ describe('Unit: parser/inline/autolink/email', () => {
20
20
  assert.deepStrictEqual(inspect(parser('a@b#1')), [['a@b#1'], '']);
21
21
  assert.deepStrictEqual(inspect(parser('a@@')), [['a@@'], '']);
22
22
  assert.deepStrictEqual(inspect(parser('a@@b')), [['a@@b'], '']);
23
- assert.deepStrictEqual(inspect(parser('a+@b')), undefined);
24
- assert.deepStrictEqual(inspect(parser('a..b@c')), undefined);
25
- assert.deepStrictEqual(inspect(parser('a++b@c')), undefined);
23
+ assert.deepStrictEqual(inspect(parser('a+@b')), [['a'], '+@b']);
24
+ assert.deepStrictEqual(inspect(parser('a..b@c')), [['a'], '..b@c']);
25
+ assert.deepStrictEqual(inspect(parser('a++b@c')), [['a'], '++b@c']);
26
26
  assert.deepStrictEqual(inspect(parser(`a@${'b'.repeat(64)}`)), [[`a@${'b'.repeat(64)}`], '']);
27
27
  assert.deepStrictEqual(inspect(parser(' a@b')), undefined);
28
28
  });
@@ -8,12 +8,12 @@ describe('Unit: parser/inline/autolink/url', () => {
8
8
 
9
9
  it('invalid', () => {
10
10
  assert.deepStrictEqual(inspect(parser('')), undefined);
11
- assert.deepStrictEqual(inspect(parser('http')), undefined);
12
- assert.deepStrictEqual(inspect(parser('ttp')), undefined);
13
- assert.deepStrictEqual(inspect(parser('http://')), undefined);
14
- assert.deepStrictEqual(inspect(parser('http://[')), undefined);
15
- assert.deepStrictEqual(inspect(parser('http://]')), undefined);
16
- assert.deepStrictEqual(inspect(parser('Http://host')), undefined);
11
+ assert.deepStrictEqual(inspect(parser('http')), [['http'], '']);
12
+ assert.deepStrictEqual(inspect(parser('ttp')), [['ttp'], '']);
13
+ assert.deepStrictEqual(inspect(parser('http://')), [['http'], '://']);
14
+ assert.deepStrictEqual(inspect(parser('http://[')), [['http'], '://[']);
15
+ assert.deepStrictEqual(inspect(parser('http://]')), [['http'], '://]']);
16
+ assert.deepStrictEqual(inspect(parser('Http://host')), [['Http'], '://host']);
17
17
  //assert.deepStrictEqual(inspect(parser('http://[::ffff:0:0%1]')), [['<a class="invalid">http://[::ffff:0:0%1]</a>'], '']);
18
18
  //assert.deepStrictEqual(inspect(parser('http://[::ffff:0:0/96]')), [['<a class="invalid">http://[::ffff:0:0/96]</a>'], '']);
19
19
  assert.deepStrictEqual(inspect(parser(' http://a')), undefined);
@@ -1,5 +1,5 @@
1
1
  import { AutolinkParser } from '../inline';
2
- import { union, some, syntax, guard, validate, fmap } from '../../combinator';
2
+ import { union, some, syntax, constraint, validate, fmap } from '../../combinator';
3
3
  import { url } from './autolink/url';
4
4
  import { email } from './autolink/email';
5
5
  import { channel } from './autolink/channel';
@@ -12,14 +12,14 @@ import { Syntax, State } from '../context';
12
12
  import { stringify } from '../util';
13
13
 
14
14
  export const autolink: AutolinkParser = fmap(
15
- validate(/^(?:[@#>0-9A-Za-z]|\S#)/,
16
- guard(context => ~context.state! & State.autolink,
15
+ validate(/^(?:[@#>0-9A-Za-z]|\S[#>])/,
16
+ constraint(State.autolink, false,
17
17
  syntax(Syntax.autolink, 1, 1,
18
18
  some(union([
19
19
  url,
20
20
  email,
21
21
  // Escape unmatched email-like strings.
22
- str(/^[0-9A-Za-z]+(?:[.+_-][0-9A-Za-z]+)*(?:@(?:[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?)+/),
22
+ str(/^[0-9A-Za-z]+(?:[.+_-][0-9A-Za-z]+)*(?:@(?:[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?)*/),
23
23
  channel,
24
24
  account,
25
25
  // Escape unmatched account-like strings.
@@ -1,6 +1,6 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { ExtensionParser } from '../../inline';
3
- import { union, some, syntax, creation, precedence, guard, state, validate, surround, open, lazy, fmap } from '../../../combinator';
3
+ import { union, some, syntax, creation, precedence, constraint, state, validate, surround, open, lazy, fmap } from '../../../combinator';
4
4
  import { inline } from '../../inline';
5
5
  import { indexee, identity } from './indexee';
6
6
  import { txt, str, stropt } from '../../source';
@@ -12,7 +12,7 @@ import IndexParser = ExtensionParser.IndexParser;
12
12
 
13
13
  export const index: IndexParser = lazy(() => validate('[#', fmap(indexee(surround(
14
14
  '[#',
15
- guard(context => ~context.state! & State.index,
15
+ constraint(State.index, false,
16
16
  syntax(Syntax.index, 2, 1,
17
17
  state(State.annotation | State.reference | State.index | State.label | State.link | State.media | State.autolink,
18
18
  startTight(
@@ -1,6 +1,6 @@
1
1
  import { Array } from 'spica/global';
2
2
  import { ExtensionParser } from '../../inline';
3
- import { union, guard, creation, validate, surround, clear, fmap } from '../../../combinator';
3
+ import { union, constraint, creation, validate, surround, clear, fmap } from '../../../combinator';
4
4
  import { str } from '../../source';
5
5
  import { State } from '../../context';
6
6
  import { html } from 'typed-dom/dom';
@@ -13,7 +13,7 @@ export const segment: ExtensionParser.LabelParser.SegmentParser = clear(validate
13
13
  ])));
14
14
 
15
15
  export const label: ExtensionParser.LabelParser = validate(['[$', '$'], creation(fmap(
16
- guard(context => ~context.state! & State.label,
16
+ constraint(State.label, false,
17
17
  union([
18
18
  surround('[', body, ']'),
19
19
  body,
@@ -122,7 +122,7 @@ describe('Unit: parser/inline/html', () => {
122
122
  assert.deepStrictEqual(inspect(parser('<bdo dir="rtl" >a</bdo>')), [['<bdo dir="rtl">a</bdo>'], '']);
123
123
  assert.deepStrictEqual(inspect(parser('<bdo dir="rtl">a</bdo>')), [['<bdo dir="rtl">a</bdo>'], '']);
124
124
  assert.deepStrictEqual(inspect(parser('<wbr\n>')), undefined);
125
- assert.deepStrictEqual(inspect(parser('<wbr >')), [['<wbr'], ' >']);
125
+ assert.deepStrictEqual(inspect(parser('<wbr >')), [['<wbr>'], '']);
126
126
  assert.deepStrictEqual(inspect(parser('<wbr constructor>')), [['<wbr'], ' constructor>']);
127
127
  assert.deepStrictEqual(inspect(parser('<wbr X>')), [['<wbr'], ' X>']);
128
128
  assert.deepStrictEqual(inspect(parser('<wbr x>')), [['<wbr'], ' x>']);
@@ -21,7 +21,7 @@ Object.values(attrspecs).forEach(o => Object.setPrototypeOf(o, null));
21
21
 
22
22
  export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^\S\n]|>)/, syntax(Syntax.none, 5, 1, union([
23
23
  focus(
24
- '<wbr>',
24
+ /^<wbr[^\S\n]*>/,
25
25
  () => [[h('wbr')], '']),
26
26
  focus(
27
27
  // https://html.spec.whatwg.org/multipage/syntax.html#void-elements
@@ -62,7 +62,7 @@ export const html: HTMLParser = lazy(() => validate('<', validate(/^<[a-z]+(?=[^
62
62
  new Cache(10000))),
63
63
  ])))));
64
64
 
65
- export const attribute: HTMLParser.TagParser.AttributeParser = union([
65
+ export const attribute: HTMLParser.AttributeParser = union([
66
66
  str(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|>)/),
67
67
  ]);
68
68
 
@@ -1,7 +1,7 @@
1
1
  import { undefined, location, encodeURI, decodeURI, Location } from 'spica/global';
2
2
  import { LinkParser, TextLinkParser } from '../inline';
3
3
  import { Result, eval, exec } from '../../combinator/data/parser';
4
- import { union, inits, tails, subsequence, some, guard, syntax, creation, precedence, state, validate, surround, open, dup, reverse, lazy, fmap, bind } from '../../combinator';
4
+ import { union, inits, tails, subsequence, some, constraint, syntax, creation, precedence, state, validate, surround, open, dup, reverse, lazy, fmap, bind } from '../../combinator';
5
5
  import { inline, media, shortmedia } from '../inline';
6
6
  import { attributes } from './html';
7
7
  import { autolink } from '../autolink';
@@ -18,7 +18,7 @@ const optspec = {
18
18
  Object.setPrototypeOf(optspec, null);
19
19
 
20
20
  export const link: LinkParser = lazy(() => validate(['[', '{'], bind(
21
- guard(context => ~context.state! & State.link,
21
+ constraint(State.link, false,
22
22
  syntax(Syntax.link, 2, 10,
23
23
  fmap(subsequence([
24
24
  state(State.link,
@@ -1,6 +1,6 @@
1
1
  import { undefined, location } from 'spica/global';
2
2
  import { MediaParser } from '../inline';
3
- import { union, inits, tails, some, creation, precedence, guard, syntax, validate, verify, surround, open, dup, lazy, fmap, bind } from '../../combinator';
3
+ import { union, inits, tails, some, syntax, creation, precedence, constraint, validate, verify, surround, open, dup, lazy, fmap, bind } from '../../combinator';
4
4
  import { textlink, uri, option as linkoption, resolve } from './link';
5
5
  import { attributes } from './html';
6
6
  import { unsafehtmlentity } from './htmlentity';
@@ -20,7 +20,7 @@ Object.setPrototypeOf(optspec, null);
20
20
 
21
21
  export const media: MediaParser = lazy(() => validate(['![', '!{'], bind(verify(fmap(open(
22
22
  '!',
23
- guard(context => ~context.state! & State.media,
23
+ constraint(State.media, false,
24
24
  syntax(Syntax.media, 2, 10,
25
25
  tails([
26
26
  dup(surround(
@@ -1,6 +1,6 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { ReferenceParser } from '../inline';
3
- import { union, subsequence, some, context, creation, guard, syntax, state, surround, open, lazy, bind } from '../../combinator';
3
+ import { union, subsequence, some, context, syntax, creation, constraint, state, surround, open, lazy, bind } from '../../combinator';
4
4
  import { inline } from '../inline';
5
5
  import { optimize } from './link';
6
6
  import { str, stropt } from '../source';
@@ -11,7 +11,7 @@ import { html, defrag } from 'typed-dom/dom';
11
11
 
12
12
  export const reference: ReferenceParser = lazy(() => surround(
13
13
  '[[',
14
- guard(context => ~context.state! & State.reference,
14
+ constraint(State.reference, false,
15
15
  syntax(Syntax.reference, 6, 1,
16
16
  state(State.annotation | State.reference | State.media,
17
17
  startLoose(
@@ -1,11 +1,11 @@
1
1
  import { ShortmediaParser } from '../inline';
2
- import { union, guard, rewrite, open, convert } from '../../combinator';
2
+ import { union, constraint, rewrite, open, convert } from '../../combinator';
3
3
  import { url } from './autolink/url';
4
4
  import { media } from './media';
5
5
  import { State } from '../context';
6
6
 
7
7
  export const shortmedia: ShortmediaParser = rewrite(
8
- guard(context => ~context.state! & State.media,
8
+ constraint(State.media, false,
9
9
  open('!', url)),
10
10
  convert(
11
11
  source => `!{ ${source.slice(1)} }`,
@@ -110,7 +110,7 @@ describe('Unit: parser/inline', () => {
110
110
  assert.deepStrictEqual(inspect(parser('[@a]')), [['[', '<a href="/@a" class="account">@a</a>', ']'], '']);
111
111
  assert.deepStrictEqual(inspect(parser('[#1][#2]')), [['<a class="index" href="#index:1">1</a>', '<a class="index" href="#index:2">2</a>'], '']);
112
112
  assert.deepStrictEqual(inspect(parser('[$1]')), [['[', '$', '1', ']'], '']);
113
- assert.deepStrictEqual(inspect(parser('[$1-2]')), [['[', '$', '1', '-', '2', ']'], '']);
113
+ assert.deepStrictEqual(inspect(parser('[$1-2]')), [['[', '$', '1-2', ']'], '']);
114
114
  assert.deepStrictEqual(inspect(parser('[$-1][$-2]')), [['<a class="label" data-label="$-1">$-1</a>', '<a class="label" data-label="$-2">$-2</a>'], '']);
115
115
  assert.deepStrictEqual(inspect(parser('$-1, $-2')), [['<a class="label" data-label="$-1">$-1</a>', ',', ' ', '<a class="label" data-label="$-2">$-2</a>'], '']);
116
116
  assert.deepStrictEqual(inspect(parser('$-1 and $-2')), [['<a class="label" data-label="$-1">$-1</a>', ' ', 'and', ' ', '<a class="label" data-label="$-2">$-2</a>'], '']);
@@ -225,10 +225,10 @@ describe('Unit: parser/inline', () => {
225
225
  assert.deepStrictEqual(inspect(parser('#a\nb\n#c\n[#d]')), [['<a href="/hashtags/a" class="hashtag">#a</a>', '<br>', 'b', '<br>', '<a href="/hashtags/c" class="hashtag">#c</a>', '<br>', '<a class="index" href="#index:d">d</a>'], '']);
226
226
  assert.deepStrictEqual(inspect(parser('##a')), [['##a'], '']);
227
227
  assert.deepStrictEqual(inspect(parser('a#b')), [['a#b'], '']);
228
- assert.deepStrictEqual(inspect(parser('0a#b')), [['0', 'a#b'], '']);
228
+ assert.deepStrictEqual(inspect(parser('0a#b')), [['0a#b'], '']);
229
229
  assert.deepStrictEqual(inspect(parser('あ#b')), [['あ#b'], '']);
230
230
  assert.deepStrictEqual(inspect(parser('あい#b')), [['あ', 'い#b'], '']);
231
- assert.deepStrictEqual(inspect(parser('0aあ#b')), [['0a', 'あ#b'], '']);
231
+ assert.deepStrictEqual(inspect(parser('0aあ#b')), [['0aあ#b'], '']);
232
232
  assert.deepStrictEqual(inspect(parser('0aあい#b')), [['0a', 'あ', 'い#b'], '']);
233
233
  assert.deepStrictEqual(inspect(parser('「#あ」')), [['「', '<a href="/hashtags/あ" class="hashtag">#あ</a>', '」'], '']);
234
234
  assert.deepStrictEqual(inspect(parser('a\n#b')), [['a', '<br>', '<a href="/hashtags/b" class="hashtag">#b</a>'], '']);
@@ -82,7 +82,7 @@ describe('Unit: parser/text/text', () => {
82
82
  assert.deepStrictEqual(inspect(parser('0@0')), [['0', '@', '0'], '']);
83
83
  assert.deepStrictEqual(inspect(parser('a@0')), [['a', '@', '0'], '']);
84
84
  assert.deepStrictEqual(inspect(parser('A@0')), [['A', '@', '0'], '']);
85
- assert.deepStrictEqual(inspect(parser('0aA@0')), [['0aA', '@', '0'], '']);
85
+ assert.deepStrictEqual(inspect(parser('aA@0')), [['aA', '@', '0'], '']);
86
86
  assert.deepStrictEqual(inspect(parser(' @0')), [[' ', '@', '0'], '']);
87
87
  assert.deepStrictEqual(inspect(parser('@@0')), [['@', '@', '0'], '']);
88
88
  });
@@ -96,10 +96,25 @@ describe('Unit: parser/text/text', () => {
96
96
  assert.deepStrictEqual(inspect(parser('0#0')), [['0', '#', '0'], '']);
97
97
  assert.deepStrictEqual(inspect(parser('a#0')), [['a', '#', '0'], '']);
98
98
  assert.deepStrictEqual(inspect(parser('A#0')), [['A', '#', '0'], '']);
99
+ assert.deepStrictEqual(inspect(parser('aA#0')), [['a', 'A', '#', '0'], '']);
99
100
  assert.deepStrictEqual(inspect(parser(' #0')), [[' ', '#', '0'], '']);
100
101
  assert.deepStrictEqual(inspect(parser('##0')), [['#', '#', '0'], '']);
101
102
  });
102
103
 
104
+ it('anchor', () => {
105
+ assert.deepStrictEqual(inspect(parser('>>0')), [['>', '>', '0'], '']);
106
+ assert.deepStrictEqual(inspect(parser('_>>0')), [['_', '>', '>', '0'], '']);
107
+ assert.deepStrictEqual(inspect(parser('$>>0')), [['$', '>', '>', '0'], '']);
108
+ assert.deepStrictEqual(inspect(parser('+>>0')), [['+', '>', '>', '0'], '']);
109
+ assert.deepStrictEqual(inspect(parser('->>0')), [['-', '>', '>', '0'], '']);
110
+ assert.deepStrictEqual(inspect(parser('0>>0')), [['0', '>', '>', '0'], '']);
111
+ assert.deepStrictEqual(inspect(parser('a>>0')), [['a', '>', '>', '0'], '']);
112
+ assert.deepStrictEqual(inspect(parser('A>>0')), [['A', '>', '>', '0'], '']);
113
+ assert.deepStrictEqual(inspect(parser('aA>>0')), [['a', 'A', '>', '>', '0'], '']);
114
+ assert.deepStrictEqual(inspect(parser(' >>0')), [[' ', '>', '>', '0'], '']);
115
+ assert.deepStrictEqual(inspect(parser('>>>>0')), [['>', '>', '>', '>', '0'], '']);
116
+ });
117
+
103
118
  it('localize', () => {
104
119
  assert.deepStrictEqual(inspect(parser('.\\\n0')), [['.', '<span class="linebreak"> </span>', '0'], '']);
105
120
  assert.deepStrictEqual(inspect(parser('。\\\n0')), [['。', '<span class="linebreak"></span>', '0'], '']);
@@ -4,9 +4,9 @@ import { union, creation, focus } from '../../combinator';
4
4
  import { str } from './str';
5
5
  import { html } from 'typed-dom/dom';
6
6
 
7
- export const delimiter = /[\s\x00-\x7F]|\S#|[()、。!?][^\S\n]*(?=\\\n)/;
7
+ export const delimiter = /[\s\x00-\x7F]|\S[#>]|[()、。!?][^\S\n]*(?=\\\n)/;
8
8
  export const nonWhitespace = /[\S\n]|$/;
9
- export const nonAlphanumeric = /[^0-9A-Za-z]|\S#|$/;
9
+ export const nonAlphanumeric = /[^0-9A-Za-z]|\S[#>]|$/;
10
10
  const repeat = str(/^(.)\1*/);
11
11
 
12
12
  export const text: TextParser = creation((source, context) => {
@@ -12,7 +12,7 @@ import { push } from 'spica/array';
12
12
  export function visualize<P extends Parser<HTMLElement | string>>(parser: P): P;
13
13
  export function visualize<T extends HTMLElement | string>(parser: Parser<T>): Parser<T> {
14
14
  const blankline = new RegExp(
15
- /^(?:\\$|\\?[^\S\n]|&IHN;|<wbr>)+$/.source.replace('IHN', `(?:${invisibleHTMLEntityNames.join('|')})`),
15
+ /^(?:\\$|\\?[^\S\n]|&IHN;|<wbr[^\S\n]*>)+$/.source.replace('IHN', `(?:${invisibleHTMLEntityNames.join('|')})`),
16
16
  'gm');
17
17
  return union([
18
18
  convert(
@@ -40,7 +40,7 @@ function hasVisible(
40
40
  }
41
41
 
42
42
  export const regBlankStart = new RegExp(
43
- /^(?:\\?[^\S\n]|&IHN;|<wbr>)+/.source.replace('IHN', `(?:${invisibleHTMLEntityNames.join('|')})`));
43
+ /^(?:\\?[^\S\n]|&IHN;|<wbr[^\S\n]*>)+/.source.replace('IHN', `(?:${invisibleHTMLEntityNames.join('|')})`));
44
44
 
45
45
  export function blankWith(delimiter: string | RegExp): RegExp;
46
46
  export function blankWith(starting: '' | '\n', delimiter: string | RegExp): RegExp;
@@ -49,7 +49,7 @@ export function blankWith(starting: '' | '\n', delimiter?: string | RegExp): Reg
49
49
  return new RegExp(String.raw
50
50
  `^(?:(?=${
51
51
  starting
52
- })(?:\\?\s|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr>)${starting && '+'})?${
52
+ })(?:\\?\s|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr[^\S\n]*>)${starting && '+'})?${
53
53
  typeof delimiter === 'string' ? delimiter.replace(/[*+()\[\]]/g, '\\$&') : delimiter.source
54
54
  }`);
55
55
  }
@@ -94,8 +94,8 @@ const isStartTight = reduce((source: string, context: MarkdownParser.Context, ex
94
94
  case '<':
95
95
  switch (true) {
96
96
  case source.length >= 5
97
- && source[1] === 'w'
98
- && source.slice(0, 5) === '<wbr>':
97
+ && source.slice(0, 4) === '<wbr'
98
+ && (source[5] === '>' || /^<wbr[^\S\n]*>/.test(source)):
99
99
  return false;
100
100
  }
101
101
  return true;