securemark 0.257.0 → 0.257.1

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.257.1
4
+
5
+ - Refactoring.
6
+
3
7
  ## 0.257.0
4
8
 
5
9
  - Introduce operator precedence.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! securemark v0.257.0 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
1
+ /*! securemark v0.257.1 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"));
@@ -5402,13 +5402,13 @@ const combinator_1 = __webpack_require__(2087);
5402
5402
 
5403
5403
  const inline_1 = __webpack_require__(1160);
5404
5404
 
5405
- const reference_1 = __webpack_require__(3555);
5405
+ const link_1 = __webpack_require__(9628);
5406
5406
 
5407
5407
  const util_1 = __webpack_require__(9437);
5408
5408
 
5409
5409
  const dom_1 = __webpack_require__(3252);
5410
5410
 
5411
- exports.annotation = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.recursion)((0, combinator_1.precedence)(6, (0, combinator_1.validate)('((', (0, combinator_1.surround)('((', (0, combinator_1.guard)(context => context.syntax?.inline?.annotation ?? true, (0, combinator_1.context)({
5411
+ exports.annotation = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.recursion)((0, combinator_1.precedence)(6, (0, combinator_1.validate)('((', (0, combinator_1.surround)('((', (0, combinator_1.guard)(context => context.syntax?.inline?.annotation ?? true, (0, util_1.startLoose)((0, combinator_1.context)({
5412
5412
  syntax: {
5413
5413
  inline: {
5414
5414
  annotation: false,
@@ -5423,9 +5423,9 @@ exports.annotation = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0,
5423
5423
  }
5424
5424
  },
5425
5425
  delimiters: global_1.undefined
5426
- }, (0, util_1.trimBlankStart)((0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), ')', [[/^\\?\n/, 9], [')', 3], ['))', 6]])))), '))', false, ([, ns], rest) => [[(0, dom_1.html)('sup', {
5426
+ }, (0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), ')', [[/^\\?\n/, 9], [')', 3], ['))', 6]])), ')')), '))', false, ([, ns], rest) => [[(0, dom_1.html)('sup', {
5427
5427
  class: 'annotation'
5428
- }, [(0, dom_1.html)('span', (0, util_1.trimNodeEnd)((0, dom_1.defrag)(ns)))])], rest], ([, ns, rest], next) => next[0] === ')' ? global_1.undefined : (0, reference_1.optimize)('((', ns, rest)))))));
5428
+ }, [(0, dom_1.html)('span', (0, util_1.trimNode)((0, dom_1.defrag)(ns)))])], rest], ([, ns, rest], next) => next[0] === ')' ? global_1.undefined : (0, link_1.optimize)('((', ns, rest)))))));
5429
5429
 
5430
5430
  /***/ }),
5431
5431
 
@@ -5488,14 +5488,7 @@ const source_1 = __webpack_require__(6743);
5488
5488
  const dom_1 = __webpack_require__(3252); // https://example/@user must be a user page or a redirect page going there.
5489
5489
 
5490
5490
 
5491
- exports.account = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.open)('@', (0, combinator_1.tails)([(0, combinator_1.verify)((0, source_1.str)(/^[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?)*\//), ([source]) => source.length <= 253 + 1), (0, combinator_1.verify)((0, source_1.str)(/^[A-Za-z][0-9A-Za-z]*(?:-[0-9A-Za-z]+)*/), ([source]) => source.length <= 64)])), (0, combinator_1.context)({
5492
- syntax: {
5493
- inline: {
5494
- link: true,
5495
- autolink: false
5496
- }
5497
- }
5498
- }, (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `https://${source.slice(1).replace('/', '/@')}` : `/${source}`} }`, (0, combinator_1.union)([link_1.link])))), ([el]) => [(0, dom_1.define)(el, {
5491
+ exports.account = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.open)('@', (0, combinator_1.tails)([(0, combinator_1.verify)((0, source_1.str)(/^[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?)*\//), ([source]) => source.length <= 253 + 1), (0, combinator_1.verify)((0, source_1.str)(/^[A-Za-z][0-9A-Za-z]*(?:-[0-9A-Za-z]+)*/), ([source]) => source.length <= 64)])), (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `https://${source.slice(1).replace('/', '/@')}` : `/${source}`} }`, (0, combinator_1.union)([link_1.textlink]))), ([el]) => [(0, dom_1.define)(el, {
5499
5492
  class: 'account'
5500
5493
  })]));
5501
5494
 
@@ -5524,14 +5517,7 @@ const dom_1 = __webpack_require__(3252); // Timeline(pseudonym): user/tid
5524
5517
  // 外部表現は投稿ごとに投稿者の投稿時のタイムゾーンに統一する(非時系列順)
5525
5518
 
5526
5519
 
5527
- exports.anchor = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('>>', (0, combinator_1.fmap)((0, combinator_1.focus)(/^>>(?:[A-Za-z][0-9A-Za-z]*(?:-[0-9A-Za-z]+)*\/)?[0-9A-Za-z]+(?:-[0-9A-Za-z]+)*(?![0-9A-Za-z@#:])/, (0, combinator_1.context)({
5528
- syntax: {
5529
- inline: {
5530
- link: true,
5531
- autolink: false
5532
- }
5533
- }
5534
- }, (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `/@${source.slice(2).replace('/', '/timeline/')}` : `?at=${source.slice(2)}`} }`, (0, combinator_1.union)([link_1.link])))), ([el]) => [(0, dom_1.define)(el, {
5520
+ exports.anchor = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('>>', (0, combinator_1.fmap)((0, combinator_1.focus)(/^>>(?:[A-Za-z][0-9A-Za-z]*(?:-[0-9A-Za-z]+)*\/)?[0-9A-Za-z]+(?:-[0-9A-Za-z]+)*(?![0-9A-Za-z@#:])/, (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `/@${source.slice(2).replace('/', '/timeline/')}` : `?at=${source.slice(2)}`} }`, (0, combinator_1.union)([link_1.textlink]))), ([el]) => [(0, dom_1.define)(el, {
5535
5521
  class: 'anchor'
5536
5522
  })])));
5537
5523
 
@@ -5618,14 +5604,7 @@ const source_1 = __webpack_require__(6743);
5618
5604
 
5619
5605
  const dom_1 = __webpack_require__(3252);
5620
5606
 
5621
- exports.hashnum = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.open)('#', (0, source_1.str)(new RegExp(/^[0-9]{1,16}(?![^\p{C}\p{S}\p{P}\s]|emoji|['_])/u.source.replace(/emoji/, hashtag_1.emoji), 'u'))), (0, combinator_1.context)({
5622
- syntax: {
5623
- inline: {
5624
- link: true,
5625
- autolink: false
5626
- }
5627
- }
5628
- }, (0, combinator_1.convert)(source => `[${source}]{ ${source.slice(1)} }`, (0, combinator_1.union)([link_1.link])))), ([el]) => [(0, dom_1.define)(el, {
5607
+ exports.hashnum = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.open)('#', (0, source_1.str)(new RegExp(/^[0-9]{1,16}(?![^\p{C}\p{S}\p{P}\s]|emoji|['_])/u.source.replace(/emoji/, hashtag_1.emoji), 'u'))), (0, combinator_1.convert)(source => `[${source}]{ ${source.slice(1)} }`, (0, combinator_1.union)([link_1.textlink]))), ([el]) => [(0, dom_1.define)(el, {
5629
5608
  class: 'hashnum',
5630
5609
  href: null
5631
5610
  })]));
@@ -5654,14 +5633,7 @@ const dom_1 = __webpack_require__(3252); // https://example/hashtags/a must be a
5654
5633
 
5655
5634
 
5656
5635
  exports.emoji = String.raw`\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F`;
5657
- exports.hashtag = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.open)('#', (0, combinator_1.tails)([(0, combinator_1.verify)((0, source_1.str)(/^[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?)*\//), ([source]) => source.length <= 253 + 1), (0, combinator_1.verify)((0, source_1.str)(new RegExp([/^(?=[0-9]{0,127}_?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji))/u.source, /(?:[^\p{C}\p{S}\p{P}\s]|emoji|_(?=[^\p{C}\p{S}\p{P}\s]|emoji)){1,128}/u.source, /(?!_?(?:[^\p{C}\p{S}\p{P}\s]|emoji)|')/u.source].join('').replace(/emoji/g, exports.emoji), 'u')), ([source]) => source.length <= 128)])), (0, combinator_1.context)({
5658
- syntax: {
5659
- inline: {
5660
- link: true,
5661
- autolink: false
5662
- }
5663
- }
5664
- }, (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `https://${source.slice(1).replace('/', '/hashtags/')}` : `/hashtags/${source.slice(1)}`} }`, (0, combinator_1.union)([link_1.link])))), ([el]) => [(0, dom_1.define)(el, {
5636
+ exports.hashtag = (0, combinator_1.lazy)(() => (0, combinator_1.fmap)((0, combinator_1.rewrite)((0, combinator_1.open)('#', (0, combinator_1.tails)([(0, combinator_1.verify)((0, source_1.str)(/^[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-(?=\w)){0,61}[0-9A-Za-z])?)*\//), ([source]) => source.length <= 253 + 1), (0, combinator_1.verify)((0, source_1.str)(new RegExp([/^(?=[0-9]{0,127}_?(?:[^\d\p{C}\p{S}\p{P}\s]|emoji))/u.source, /(?:[^\p{C}\p{S}\p{P}\s]|emoji|_(?=[^\p{C}\p{S}\p{P}\s]|emoji)){1,128}/u.source, /(?!_?(?:[^\p{C}\p{S}\p{P}\s]|emoji)|')/u.source].join('').replace(/emoji/g, exports.emoji), 'u')), ([source]) => source.length <= 128)])), (0, combinator_1.convert)(source => `[${source}]{ ${source.includes('/') ? `https://${source.slice(1).replace('/', '/hashtags/')}` : `/hashtags/${source.slice(1)}`} }`, (0, combinator_1.union)([link_1.textlink]))), ([el]) => [(0, dom_1.define)(el, {
5665
5637
  class: 'hashtag'
5666
5638
  }, el.innerText)]));
5667
5639
 
@@ -5684,10 +5656,8 @@ const link_1 = __webpack_require__(9628);
5684
5656
 
5685
5657
  const source_1 = __webpack_require__(6743);
5686
5658
 
5687
- const util_1 = __webpack_require__(9437);
5688
-
5689
- const closer = /^[-+*=~^,.;:!?]*(?=["`|\[\](){}<>]|\\?$)/;
5690
- exports.url = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['http://', 'https://'], (0, combinator_1.rewrite)((0, combinator_1.open)(/^https?:\/\/(?=[\x21-\x7E])/, (0, combinator_1.focus)(/^[\x21-\x7E]+/, (0, combinator_1.some)((0, combinator_1.union)([bracket, (0, combinator_1.some)(source_1.unescsource, closer)])))), (0, combinator_1.convert)(url => `{ ${url} }`, (0, util_1.clean)((0, combinator_1.union)([link_1.link]))))));
5659
+ const closer = /^[-+*=~^,.;:!?]*(?=[\\"`|\[\](){}<>]|$)/;
5660
+ exports.url = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['http://', 'https://'], (0, combinator_1.rewrite)((0, combinator_1.open)(/^https?:\/\/(?=[\x21-\x7E])/, (0, combinator_1.focus)(/^[\x21-\x7E]+/, (0, combinator_1.some)((0, combinator_1.union)([bracket, (0, combinator_1.some)(source_1.unescsource, closer)])))), (0, combinator_1.convert)(url => `{ ${url} }`, (0, combinator_1.union)([link_1.textlink])))));
5691
5661
  const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.precedence)(3, (0, combinator_1.union)([(0, combinator_1.surround)('(', (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), ')'), ')', true), (0, combinator_1.surround)('[', (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), ']'), ']', true), (0, combinator_1.surround)('{', (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.unescsource]), '}'), '}', true), (0, combinator_1.surround)('"', (0, combinator_1.precedence)(8, (0, combinator_1.some)(source_1.unescsource, '"')), '"', true)]))));
5692
5662
 
5693
5663
  /***/ }),
@@ -6356,7 +6326,7 @@ exports.insertion = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, c
6356
6326
  Object.defineProperty(exports, "__esModule", ({
6357
6327
  value: true
6358
6328
  }));
6359
- exports.resolve = exports.option = exports.uri = exports.link = void 0;
6329
+ exports.optimize = exports.resolve = exports.option = exports.uri = exports.textlink = exports.link = void 0;
6360
6330
 
6361
6331
  const global_1 = __webpack_require__(4128);
6362
6332
 
@@ -6382,7 +6352,7 @@ const optspec = {
6382
6352
  rel: ['nofollow']
6383
6353
  };
6384
6354
  Object.setPrototypeOf(optspec, null);
6385
- exports.link = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[', '{'], (0, combinator_1.creator)(10, (0, combinator_1.precedence)(3, (0, combinator_1.bind)((0, combinator_1.guard)(context => context.syntax?.inline?.link ?? true, (0, combinator_1.reverse)((0, combinator_1.tails)([(0, combinator_1.context)({
6355
+ exports.link = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[', '{'], (0, combinator_1.creator)(10, (0, combinator_1.precedence)(3, (0, combinator_1.bind)((0, combinator_1.guard)(context => context.syntax?.inline?.link ?? true, (0, combinator_1.fmap)((0, combinator_1.subsequence)([(0, combinator_1.context)({
6386
6356
  syntax: {
6387
6357
  inline: {
6388
6358
  link: false
@@ -6401,15 +6371,25 @@ exports.link = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[', '{'
6401
6371
  autolink: false
6402
6372
  }
6403
6373
  }
6404
- }, (0, util_1.trimBlankStart)((0, combinator_1.some)(inline_1.inline, ']', [[/^\\?\n/, 9], [']', 3]]))), ']', true)]))), (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /^[^\S\n]*}/))]))), ([params, content = []], rest, context) => {
6405
- content = (0, util_1.trimNodeEnd)(content);
6374
+ }, (0, combinator_1.some)(inline_1.inline, ']', [[/^\\?\n/, 9], [']', 3]])), ']', true, global_1.undefined, ([, ns = [], rest], next) => next[0] === ']' ? global_1.undefined : optimize('[', ns, rest))]))), // 全体の失敗が確定した時も解析し予算を浪費している
6375
+ (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /^[^\S\n]*}/))]), ([as, bs = []]) => bs[0] === '\r' && bs.shift() ? [as, bs] : as[0] === '\r' && as.shift() ? [[], as] : [as, []])), ([content, params], rest, context) => {
6376
+ if (params.length === 0) return;
6377
+ if (content[0] === '') return [content, rest];
6378
+ if (content.length !== 0 && (0, util_1.trimNode)(content).length === 0) return;
6406
6379
  if ((0, parser_1.eval)((0, combinator_1.some)(autolink_1.autolink)((0, util_1.stringify)(content), context))?.some(node => typeof node === 'object')) return;
6407
6380
  const INSECURE_URI = params.shift();
6408
6381
  const el = elem(INSECURE_URI, (0, dom_1.defrag)(content), new url_1.ReadonlyURL(resolve(INSECURE_URI, context.host ?? global_1.location, context.url ?? context.host ?? global_1.location), context.host?.href || global_1.location.href), context.host?.origin || global_1.location.origin);
6409
6382
  if (el.classList.contains('invalid')) return [[el], rest];
6410
6383
  return [[(0, dom_1.define)(el, (0, html_1.attributes)('link', [], optspec, params))], rest];
6411
6384
  })))));
6412
- exports.uri = (0, combinator_1.union)([(0, combinator_1.open)(/^[^\S\n]+/, (0, source_1.str)(/^\S+/)), (0, source_1.str)(/^[^\s{}]+/)]);
6385
+ exports.textlink = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['[', '{'], (0, combinator_1.creator)(10, (0, combinator_1.precedence)(3, (0, combinator_1.bind)((0, combinator_1.reverse)((0, combinator_1.tails)([(0, combinator_1.dup)((0, combinator_1.surround)('[', (0, combinator_1.some)((0, combinator_1.union)([source_1.unescsource]), ']'), ']')), (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([exports.uri, (0, combinator_1.some)(exports.option)]), /^[^\S\n]*}/))])), ([params, content = []], rest, context) => {
6386
+ params.shift();
6387
+ (0, util_1.trimNode)(content);
6388
+ const INSECURE_URI = params.shift();
6389
+ const el = elem(INSECURE_URI, (0, dom_1.defrag)(content), new url_1.ReadonlyURL(resolve(INSECURE_URI, context.host ?? global_1.location, context.url ?? context.host ?? global_1.location), context.host?.href || global_1.location.href), context.host?.origin || global_1.location.origin);
6390
+ return [[(0, dom_1.define)(el, (0, html_1.attributes)('link', [], optspec, params))], rest];
6391
+ })))));
6392
+ exports.uri = (0, combinator_1.fmap)((0, combinator_1.union)([(0, combinator_1.open)(/^[^\S\n]+/, (0, source_1.str)(/^\S+/)), (0, source_1.str)(/^[^\s{}]+/)]), ([uri]) => ['\r', uri]);
6413
6393
  exports.option = (0, combinator_1.union)([(0, combinator_1.fmap)((0, source_1.str)(/^[^\S\n]+nofollow(?=[^\S\n]|})/), () => [` rel="nofollow"`]), (0, source_1.str)(/^[^\S\n]+[a-z]+(?:-[a-z]+)*(?:="(?:\\[^\n]|[^\\\n"])*")?(?=[^\S\n]|})/), (0, combinator_1.fmap)((0, source_1.str)(/^[^\S\n]+[^\s{}]+/), opt => [` \\${opt.slice(1)}`])]);
6414
6394
 
6415
6395
  function resolve(uri, host, source) {
@@ -6486,6 +6466,19 @@ function decode(uri) {
6486
6466
  }
6487
6467
  }
6488
6468
 
6469
+ function optimize(opener, ns, rest) {
6470
+ let count = 0;
6471
+
6472
+ for (let i = 0; i < ns.length - 1; i += 2) {
6473
+ if (ns[i] !== '' || ns[i + 1] !== opener[0]) break;
6474
+ ++count;
6475
+ }
6476
+
6477
+ return [['', opener[0].repeat(opener.length + count)], rest.slice(count)];
6478
+ }
6479
+
6480
+ exports.optimize = optimize;
6481
+
6489
6482
  /***/ }),
6490
6483
 
6491
6484
  /***/ 2480:
@@ -6589,7 +6582,7 @@ const optspec = {
6589
6582
  rel: global_1.undefined
6590
6583
  };
6591
6584
  Object.setPrototypeOf(optspec, null);
6592
- exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['![', '!{'], (0, combinator_1.creator)(10, (0, combinator_1.precedence)(3, (0, combinator_1.bind)((0, combinator_1.verify)((0, combinator_1.fmap)((0, combinator_1.open)('!', (0, combinator_1.guard)(context => context.syntax?.inline?.media ?? true, (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('')], bs] : [[''], as]), ([[text]]) => text === '' || text.trim() !== ''), ([[text], params], rest, context) => {
6585
+ exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['![', '!{'], (0, combinator_1.creator)(10, (0, combinator_1.precedence)(3, (0, combinator_1.bind)((0, combinator_1.verify)((0, combinator_1.fmap)((0, combinator_1.open)('!', (0, combinator_1.guard)(context => context.syntax?.inline?.media ?? true, (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) => {
6593
6586
  const INSECURE_URI = params.shift();
6594
6587
  const url = new url_1.ReadonlyURL((0, link_1.resolve)(INSECURE_URI, context.host ?? global_1.location, context.url ?? context.host ?? global_1.location), context.host?.href || global_1.location.href);
6595
6588
  let cache;
@@ -6608,7 +6601,7 @@ exports.media = (0, combinator_1.lazy)(() => (0, combinator_1.validate)(['![', '
6608
6601
  }
6609
6602
 
6610
6603
  if (context.syntax?.inline?.link === false || cache && cache.tagName !== 'IMG') return [[el], rest];
6611
- return (0, combinator_1.fmap)(link_1.link, ([link]) => [(0, dom_1.define)(link, {
6604
+ return (0, combinator_1.fmap)(link_1.textlink, ([link]) => [(0, dom_1.define)(link, {
6612
6605
  target: '_blank'
6613
6606
  }, [el])])(`{ ${INSECURE_URI}${params.join('')} }${rest}`, context);
6614
6607
  })))));
@@ -6666,7 +6659,7 @@ function sanitize(target, uri, alt) {
6666
6659
  Object.defineProperty(exports, "__esModule", ({
6667
6660
  value: true
6668
6661
  }));
6669
- exports.optimize = exports.reference = void 0;
6662
+ exports.reference = void 0;
6670
6663
 
6671
6664
  const global_1 = __webpack_require__(4128);
6672
6665
 
@@ -6674,13 +6667,15 @@ const combinator_1 = __webpack_require__(2087);
6674
6667
 
6675
6668
  const inline_1 = __webpack_require__(1160);
6676
6669
 
6670
+ const link_1 = __webpack_require__(9628);
6671
+
6677
6672
  const source_1 = __webpack_require__(6743);
6678
6673
 
6679
6674
  const util_1 = __webpack_require__(9437);
6680
6675
 
6681
6676
  const dom_1 = __webpack_require__(3252);
6682
6677
 
6683
- exports.reference = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('[[', (0, combinator_1.creator)((0, combinator_1.recursion)((0, combinator_1.precedence)(6, (0, combinator_1.surround)('[[', (0, combinator_1.guard)(context => context.syntax?.inline?.reference ?? true, (0, combinator_1.context)({
6678
+ exports.reference = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('[[', (0, combinator_1.creator)((0, combinator_1.recursion)((0, combinator_1.precedence)(6, (0, combinator_1.surround)('[[', (0, combinator_1.guard)(context => context.syntax?.inline?.reference ?? true, (0, util_1.startLoose)((0, combinator_1.context)({
6684
6679
  syntax: {
6685
6680
  inline: {
6686
6681
  annotation: false,
@@ -6694,7 +6689,7 @@ exports.reference = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('[['
6694
6689
  }
6695
6690
  },
6696
6691
  delimiters: global_1.undefined
6697
- }, (0, combinator_1.subsequence)([abbr, (0, combinator_1.open)((0, source_1.stropt)(/^(?=\^)/), (0, combinator_1.some)(inline_1.inline, ']', [[/^\\?\n/, 9], [']', 3], [']]', 6]])), (0, util_1.trimBlankStart)((0, combinator_1.some)(inline_1.inline, ']', [[/^\\?\n/, 9], [']', 3], [']]', 6]]))]))), ']]', false, ([, ns], rest) => [[(0, dom_1.html)('sup', attributes(ns), [(0, dom_1.html)('span', (0, util_1.trimNodeEnd)((0, dom_1.defrag)(ns)))])], rest], ([, ns, rest], next) => next[0] === ']' ? global_1.undefined : optimize('[[', ns, rest)))))));
6692
+ }, (0, combinator_1.subsequence)([abbr, (0, combinator_1.open)((0, source_1.stropt)(/^(?=\^)/), (0, combinator_1.some)(inline_1.inline, ']', [[/^\\?\n/, 9], [']', 3], [']]', 6]])), (0, combinator_1.some)(inline_1.inline, ']', [[/^\\?\n/, 9], [']', 3], [']]', 6]])])), ']')), ']]', false, ([, ns], rest) => [[(0, dom_1.html)('sup', attributes(ns), [(0, dom_1.html)('span', (0, util_1.trimNode)((0, dom_1.defrag)(ns)))])], rest], ([, ns, rest], next) => next[0] === ']' ? global_1.undefined : (0, link_1.optimize)('[[', ns, rest)))))));
6698
6693
  const abbr = (0, combinator_1.creator)((0, combinator_1.bind)((0, combinator_1.surround)('^', (0, combinator_1.union)([(0, source_1.str)(/^(?![0-9]+\s?[|\]])[0-9A-Za-z]+(?:(?:-|(?=\W)(?!'\d)'?(?!\.\d)\.?(?!,\S),? ?)[0-9A-Za-z]+)*(?:-|'?\.?,? ?)?/)]), /^\|?(?=]])|^\|[^\S\n]*/), ([source], rest) => [[(0, dom_1.html)('abbr', source)], rest.replace(util_1.regBlankStart, '')]));
6699
6694
 
6700
6695
  function attributes(ns) {
@@ -6711,19 +6706,6 @@ function attributes(ns) {
6711
6706
  };
6712
6707
  }
6713
6708
 
6714
- function optimize(opener, ns, rest) {
6715
- let count = 0;
6716
-
6717
- for (let i = 0; i < ns.length - 1; i += 2) {
6718
- if (ns[i] !== '' || ns[i + 1] !== opener[0]) break;
6719
- ++count;
6720
- }
6721
-
6722
- return [[opener[0].repeat(opener.length + count)], rest.slice(count)];
6723
- }
6724
-
6725
- exports.optimize = optimize;
6726
-
6727
6709
  /***/ }),
6728
6710
 
6729
6711
  /***/ 6705:
@@ -7798,7 +7780,7 @@ exports.unescsource = (0, combinator_1.creator)(source => {
7798
7780
  Object.defineProperty(exports, "__esModule", ({
7799
7781
  value: true
7800
7782
  }));
7801
- exports.stringify = exports.trimNodeEnd = exports.trimBlankEnd = exports.trimBlankStart = exports.trimBlank = exports.isStartTightNodes = exports.isStartLooseNodes = exports.startTight = exports.startLoose = exports.visualize = exports.blankWith = exports.regBlankStart = exports.clean = void 0;
7783
+ exports.stringify = exports.trimNode = exports.trimBlankEnd = exports.trimBlankStart = exports.trimBlank = exports.isStartTightNodes = exports.isStartLooseNodes = exports.startTight = exports.startLoose = exports.visualize = exports.blankWith = exports.regBlankStart = exports.clean = void 0;
7802
7784
 
7803
7785
  const global_1 = __webpack_require__(4128);
7804
7786
 
@@ -7993,23 +7975,32 @@ function trimBlankEnd(parser) {
7993
7975
  return (0, combinator_1.fmap)(parser, trimNodeEnd);
7994
7976
  }
7995
7977
 
7996
- exports.trimBlankEnd = trimBlankEnd; //export function trimNode(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
7997
- // return trimNodeStart(trimNodeEnd(nodes));
7998
- //}
7999
- //function trimNodeStart(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
8000
- // for (let node = nodes[0]; nodes.length > 0 && !isVisible(node = nodes[0], 0);) {
8001
- // if (nodes.length === 1 && typeof node === 'object' && node.className === 'indexer') break;
8002
- // if (typeof node === 'string') {
8003
- // const pos = node.length - node.trimStart().length;
8004
- // if (pos > 0) {
8005
- // nodes[0] = node.slice(pos);
8006
- // break;
8007
- // }
8008
- // }
8009
- // nodes.shift();
8010
- // }
8011
- // return nodes;
8012
- //}
7978
+ exports.trimBlankEnd = trimBlankEnd;
7979
+
7980
+ function trimNode(nodes) {
7981
+ return trimNodeStart(trimNodeEnd(nodes));
7982
+ }
7983
+
7984
+ exports.trimNode = trimNode;
7985
+
7986
+ function trimNodeStart(nodes) {
7987
+ for (let node = nodes[0]; nodes.length > 0 && !isVisible(node = nodes[0], 0);) {
7988
+ if (nodes.length === 1 && typeof node === 'object' && node.className === 'indexer') break;
7989
+
7990
+ if (typeof node === 'string') {
7991
+ const pos = node.trimStart().length;
7992
+
7993
+ if (pos > 0) {
7994
+ nodes[0] = node.slice(pos);
7995
+ break;
7996
+ }
7997
+ }
7998
+
7999
+ nodes.shift();
8000
+ }
8001
+
8002
+ return nodes;
8003
+ }
8013
8004
 
8014
8005
  function trimNodeEnd(nodes) {
8015
8006
  const skip = nodes.length > 0 && typeof nodes[nodes.length - 1] === 'object' && nodes[nodes.length - 1]['className'] === 'indexer' ? [nodes.pop()] : [];
@@ -8030,8 +8021,6 @@ function trimNodeEnd(nodes) {
8030
8021
  return (0, array_1.push)(nodes, skip);
8031
8022
  }
8032
8023
 
8033
- exports.trimNodeEnd = trimNodeEnd;
8034
-
8035
8024
  function stringify(nodes) {
8036
8025
  let acc = '';
8037
8026
 
package/markdown.d.ts CHANGED
@@ -869,11 +869,20 @@ export namespace MarkdownParser {
869
869
  // { uri }
870
870
  // [abc]{uri nofollow}
871
871
  Inline<'link'>,
872
- Parser<HTMLAnchorElement, Context, [
872
+ Parser<HTMLElement | string, Context, [
873
873
  LinkParser.ContentParser,
874
874
  LinkParser.ParameterParser,
875
875
  ]> {
876
876
  }
877
+ export interface TextLinkParser extends
878
+ // { uri }
879
+ // [abc]{uri nofollow}
880
+ Inline<'textlink'>,
881
+ Parser<HTMLAnchorElement, Context, [
882
+ LinkParser.TextParser,
883
+ LinkParser.ParameterParser,
884
+ ]> {
885
+ }
877
886
  export namespace LinkParser {
878
887
  export interface ContentParser extends
879
888
  Inline<'link/content'>,
@@ -883,6 +892,12 @@ export namespace MarkdownParser {
883
892
  InlineParser,
884
893
  ]> {
885
894
  }
895
+ export interface TextParser extends
896
+ Inline<'link/text'>,
897
+ Parser<string[], Context, [
898
+ SourceParser.UnescapableSourceParser,
899
+ ]> {
900
+ }
886
901
  export interface ParameterParser extends
887
902
  Inline<'link/parameter'>,
888
903
  Parser<string[], Context, [
@@ -1107,7 +1122,7 @@ export namespace MarkdownParser {
1107
1122
  // https://host
1108
1123
  Inline<'url'>,
1109
1124
  Parser<HTMLAnchorElement, Context, [
1110
- LinkParser,
1125
+ TextLinkParser,
1111
1126
  ]> {
1112
1127
  }
1113
1128
  export namespace UrlParser {
@@ -1149,28 +1164,28 @@ export namespace MarkdownParser {
1149
1164
  // @user
1150
1165
  Inline<'account'>,
1151
1166
  Parser<HTMLAnchorElement, Context, [
1152
- LinkParser,
1167
+ TextLinkParser,
1153
1168
  ]> {
1154
1169
  }
1155
1170
  export interface HashtagParser extends
1156
1171
  // #tag
1157
1172
  Inline<'hashtag'>,
1158
1173
  Parser<HTMLAnchorElement, Context, [
1159
- LinkParser,
1174
+ TextLinkParser,
1160
1175
  ]> {
1161
1176
  }
1162
1177
  export interface HashnumParser extends
1163
1178
  // #1
1164
1179
  Inline<'hashnum'>,
1165
1180
  Parser<HTMLAnchorElement, Context, [
1166
- LinkParser,
1181
+ TextLinkParser,
1167
1182
  ]> {
1168
1183
  }
1169
1184
  export interface AnchorParser extends
1170
1185
  // >>1
1171
1186
  Inline<'anchor'>,
1172
1187
  Parser<HTMLAnchorElement, Context, [
1173
- LinkParser,
1188
+ TextLinkParser,
1174
1189
  ]> {
1175
1190
  }
1176
1191
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.257.0",
3
+ "version": "0.257.1",
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",
package/src/debug.test.ts CHANGED
@@ -5,7 +5,7 @@ import { querySelector, querySelectorAll } from 'typed-dom/query';
5
5
  export function inspect(result: Result<HTMLElement | string>, until: number | string = Infinity): Result<string> {
6
6
  return result && [
7
7
  eval(result).map((node, i, nodes) => {
8
- assert(node || node === '' && '([{'.includes(nodes[i + 1] as string));
8
+ assert(node || node === '' && '([{'.includes(nodes[i + 1][0]));
9
9
  if (typeof node === 'string') return node;
10
10
  node = node.cloneNode(true);
11
11
  assert(!querySelector(node, '.invalid[data-invalid-message$="."]'));
@@ -14,14 +14,15 @@ describe('Unit: parser/inline/annotation', () => {
14
14
  assert.deepStrictEqual(inspect(parser('(())')), undefined);
15
15
  assert.deepStrictEqual(inspect(parser('(()))')), undefined);
16
16
  assert.deepStrictEqual(inspect(parser('(( ))')), undefined);
17
+ assert.deepStrictEqual(inspect(parser('(( (a')), [['', '(('], ' (a']);
17
18
  assert.deepStrictEqual(inspect(parser('((\n))')), undefined);
18
19
  assert.deepStrictEqual(inspect(parser('((\na))')), undefined);
19
20
  assert.deepStrictEqual(inspect(parser('((\\\na))')), undefined);
20
- assert.deepStrictEqual(inspect(parser('((a\n))')), [['(('], 'a\n))']);
21
- assert.deepStrictEqual(inspect(parser('((a\\\n))')), [['(('], 'a\\\n))']);
22
- assert.deepStrictEqual(inspect(parser('((a\nb))')), [['(('], 'a\nb))']);
23
- assert.deepStrictEqual(inspect(parser('((a\\\nb))')), [['(('], 'a\\\nb))']);
24
- assert.deepStrictEqual(inspect(parser('((*a\nb*))')), [['(('], '*a\nb*))']);
21
+ assert.deepStrictEqual(inspect(parser('((a\n))')), [['', '(('], 'a\n))']);
22
+ assert.deepStrictEqual(inspect(parser('((a\\\n))')), [['', '(('], 'a\\\n))']);
23
+ assert.deepStrictEqual(inspect(parser('((a\nb))')), [['', '(('], 'a\nb))']);
24
+ assert.deepStrictEqual(inspect(parser('((a\\\nb))')), [['', '(('], 'a\\\nb))']);
25
+ assert.deepStrictEqual(inspect(parser('((*a\nb*))')), [['', '(('], '*a\nb*))']);
25
26
  assert.deepStrictEqual(inspect(parser('((\\))')), undefined);
26
27
  assert.deepStrictEqual(inspect(parser('((a)b))')), undefined);
27
28
  assert.deepStrictEqual(inspect(parser('(((a))')), undefined);
@@ -2,13 +2,14 @@ import { undefined } from 'spica/global';
2
2
  import { AnnotationParser } from '../inline';
3
3
  import { union, some, validate, guard, context, precedence, creator, recursion, surround, lazy } from '../../combinator';
4
4
  import { inline } from '../inline';
5
- import { optimize } from './reference';
6
- import { trimBlankStart, trimNodeEnd } from '../util';
5
+ import { optimize } from './link';
6
+ import { startLoose, trimNode } from '../util';
7
7
  import { html, defrag } from 'typed-dom/dom';
8
8
 
9
9
  export const annotation: AnnotationParser = lazy(() => creator(recursion(precedence(6, validate('((', surround(
10
10
  '((',
11
11
  guard(context => context.syntax?.inline?.annotation ?? true,
12
+ startLoose(
12
13
  context({ syntax: { inline: {
13
14
  annotation: false,
14
15
  // Redundant
@@ -20,8 +21,8 @@ export const annotation: AnnotationParser = lazy(() => creator(recursion(precede
20
21
  //link: true,
21
22
  //autolink: true,
22
23
  }}, delimiters: undefined },
23
- trimBlankStart(some(union([inline]), ')', [[/^\\?\n/, 9], [')', 3], ['))', 6]])))),
24
+ some(union([inline]), ')', [[/^\\?\n/, 9], [')', 3], ['))', 6]])), ')')),
24
25
  '))',
25
26
  false,
26
- ([, ns], rest) => [[html('sup', { class: 'annotation' }, [html('span', trimNodeEnd(defrag(ns)))])], rest],
27
+ ([, ns], rest) => [[html('sup', { class: 'annotation' }, [html('span', trimNode(defrag(ns)))])], rest],
27
28
  ([, ns, rest], next) => next[0] === ')' ? undefined : optimize('((', ns, rest)))))));
@@ -1,6 +1,6 @@
1
1
  import { AutolinkParser } from '../../inline';
2
- import { union, tails, verify, rewrite, context, open, convert, fmap, lazy } from '../../../combinator';
3
- import { link } from '../link';
2
+ import { union, tails, verify, rewrite, open, convert, fmap, lazy } from '../../../combinator';
3
+ import { textlink } from '../link';
4
4
  import { str } from '../../source';
5
5
  import { define } from 'typed-dom/dom';
6
6
 
@@ -17,10 +17,6 @@ export const account: AutolinkParser.AccountParser = lazy(() => fmap(rewrite(
17
17
  str(/^[A-Za-z][0-9A-Za-z]*(?:-[0-9A-Za-z]+)*/),
18
18
  ([source]) => source.length <= 64),
19
19
  ])),
20
- context({ syntax: { inline: {
21
- link: true,
22
- autolink: false,
23
- }}},
24
20
  convert(
25
21
  source =>
26
22
  `[${source}]{ ${
@@ -28,5 +24,5 @@ export const account: AutolinkParser.AccountParser = lazy(() => fmap(rewrite(
28
24
  ? `https://${source.slice(1).replace('/', '/@')}`
29
25
  : `/${source}`
30
26
  } }`,
31
- union([link])))),
27
+ union([textlink]))),
32
28
  ([el]) => [define(el, { class: 'account' })]));
@@ -1,6 +1,6 @@
1
1
  import { AutolinkParser } from '../../inline';
2
- import { union, validate, focus, context, convert, fmap, lazy } from '../../../combinator';
3
- import { link } from '../link';
2
+ import { union, validate, focus, convert, fmap, lazy } from '../../../combinator';
3
+ import { textlink } from '../link';
4
4
  import { define } from 'typed-dom/dom';
5
5
 
6
6
  // Timeline(pseudonym): user/tid
@@ -14,10 +14,6 @@ import { define } from 'typed-dom/dom';
14
14
 
15
15
  export const anchor: AutolinkParser.AnchorParser = lazy(() => validate('>>', fmap(focus(
16
16
  /^>>(?:[A-Za-z][0-9A-Za-z]*(?:-[0-9A-Za-z]+)*\/)?[0-9A-Za-z]+(?:-[0-9A-Za-z]+)*(?![0-9A-Za-z@#:])/,
17
- context({ syntax: { inline: {
18
- link: true,
19
- autolink: false,
20
- }}},
21
17
  convert(
22
18
  source =>
23
19
  `[${source}]{ ${
@@ -25,5 +21,5 @@ export const anchor: AutolinkParser.AnchorParser = lazy(() => validate('>>', fma
25
21
  ? `/@${source.slice(2).replace('/', '/timeline/')}`
26
22
  : `?at=${source.slice(2)}`
27
23
  } }`,
28
- union([link])))),
24
+ union([textlink]))),
29
25
  ([el]) => [define(el, { class: 'anchor' })])));
@@ -1,17 +1,13 @@
1
1
  import { AutolinkParser } from '../../inline';
2
- import { union, rewrite, context, open, convert, fmap, lazy } from '../../../combinator';
3
- import { link } from '../link';
2
+ import { union, rewrite, open, convert, fmap, lazy } from '../../../combinator';
3
+ import { textlink } from '../link';
4
4
  import { emoji } from './hashtag';
5
5
  import { str } from '../../source';
6
6
  import { define } from 'typed-dom/dom';
7
7
 
8
8
  export const hashnum: AutolinkParser.HashnumParser = lazy(() => fmap(rewrite(
9
9
  open('#', str(new RegExp(/^[0-9]{1,16}(?![^\p{C}\p{S}\p{P}\s]|emoji|['_])/u.source.replace(/emoji/, emoji), 'u'))),
10
- context({ syntax: { inline: {
11
- link: true,
12
- autolink: false,
13
- }}},
14
10
  convert(
15
11
  source => `[${source}]{ ${source.slice(1)} }`,
16
- union([link])))),
12
+ union([textlink]))),
17
13
  ([el]) => [define(el, { class: 'hashnum', href: null })]));
@@ -1,6 +1,6 @@
1
1
  import { AutolinkParser } from '../../inline';
2
- import { union, tails, verify, rewrite, context, open, convert, fmap, lazy } from '../../../combinator';
3
- import { link } from '../link';
2
+ import { union, tails, verify, rewrite, open, convert, fmap, lazy } from '../../../combinator';
3
+ import { textlink } from '../link';
4
4
  import { str } from '../../source';
5
5
  import { define } from 'typed-dom/dom';
6
6
 
@@ -24,10 +24,6 @@ export const hashtag: AutolinkParser.HashtagParser = lazy(() => fmap(rewrite(
24
24
  ].join('').replace(/emoji/g, emoji), 'u')),
25
25
  ([source]) => source.length <= 128),
26
26
  ])),
27
- context({ syntax: { inline: {
28
- link: true,
29
- autolink: false,
30
- }}},
31
27
  convert(
32
28
  source =>
33
29
  `[${source}]{ ${
@@ -35,5 +31,5 @@ export const hashtag: AutolinkParser.HashtagParser = lazy(() => fmap(rewrite(
35
31
  ? `https://${source.slice(1).replace('/', '/hashtags/')}`
36
32
  : `/hashtags/${source.slice(1)}`
37
33
  } }`,
38
- union([link])))),
34
+ union([textlink]))),
39
35
  ([el]) => [define(el, { class: 'hashtag' }, el.innerText)]));
@@ -72,6 +72,7 @@ describe('Unit: parser/inline/autolink/url', () => {
72
72
  assert.deepStrictEqual(inspect(parser('http://host>')), [['<a href="http://host" target="_blank">http://host</a>'], '>']);
73
73
  assert.deepStrictEqual(inspect(parser('http://host(')), [['<a href="http://host" target="_blank">http://host</a>'], '(']);
74
74
  assert.deepStrictEqual(inspect(parser('http://host)')), [['<a href="http://host" target="_blank">http://host</a>'], ')']);
75
+ assert.deepStrictEqual(inspect(parser('http://host\\"')), [['<a href="http://host" target="_blank">http://host</a>'], '\\"']);
75
76
  assert.deepStrictEqual(inspect(parser('http://host!?**.*--++==~~^^')), [['<a href="http://host" target="_blank">http://host</a>'], '!?**.*--++==~~^^']);
76
77
  });
77
78
 
@@ -1,10 +1,9 @@
1
1
  import { AutolinkParser } from '../../inline';
2
2
  import { union, some, validate, focus, rewrite, precedence, creator, convert, surround, open, lazy } from '../../../combinator';
3
- import { link } from '../link';
3
+ import { textlink } from '../link';
4
4
  import { unescsource } from '../../source';
5
- import { clean } from '../../util';
6
5
 
7
- const closer = /^[-+*=~^,.;:!?]*(?=["`|\[\](){}<>]|\\?$)/;
6
+ const closer = /^[-+*=~^,.;:!?]*(?=[\\"`|\[\](){}<>]|$)/;
8
7
 
9
8
  export const url: AutolinkParser.UrlParser = lazy(() => validate(['http://', 'https://'], rewrite(
10
9
  open(
@@ -12,7 +11,7 @@ export const url: AutolinkParser.UrlParser = lazy(() => validate(['http://', 'ht
12
11
  focus(/^[\x21-\x7E]+/, some(union([bracket, some(unescsource, closer)])))),
13
12
  convert(
14
13
  url => `{ ${url} }`,
15
- clean(union([link]))))));
14
+ union([textlink])))));
16
15
 
17
16
  const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => creator(precedence(3, union([
18
17
  surround('(', some(union([bracket, unescsource]), ')'), ')', true),
@@ -73,7 +73,7 @@ describe('Unit: parser/inline/bracket', () => {
73
73
  assert.deepStrictEqual(inspect(parser('"a')), [['"', 'a'], '']);
74
74
  assert.deepStrictEqual(inspect(parser('"a"')), [['"', 'a', '"'], '']);
75
75
  assert.deepStrictEqual(inspect(parser('"(")"')), [['"', '', '(', '"'], ')"']);
76
- assert.deepStrictEqual(inspect(parser('"(("')), [['"', '((', '"'], '']);
76
+ assert.deepStrictEqual(inspect(parser('"(("')), [['"', '', '((', '"'], '']);
77
77
  assert.deepStrictEqual(inspect(parser('"(\\")"')), [['"', '<span class="paren">(")</span>', '"'], '']);
78
78
  assert.deepStrictEqual(inspect(parser('"(\n)"')), [['"', '<span class="paren">(<br>)</span>', '"'], '']);
79
79
  assert.deepStrictEqual(inspect(parser('"(\\\n)"')), [['"', '<span class="paren">(<span class="linebreak"> </span>)</span>', '"'], '']);
@@ -1,12 +1,12 @@
1
1
  import { undefined, location, encodeURI, decodeURI, Location } from 'spica/global';
2
- import { LinkParser } from '../inline';
3
- import { eval } from '../../combinator/data/parser';
4
- import { union, inits, tails, some, validate, guard, context, precedence, creator, surround, open, dup, reverse, lazy, fmap, bind } from '../../combinator';
2
+ import { LinkParser, TextLinkParser } from '../inline';
3
+ import { Result, eval } from '../../combinator/data/parser';
4
+ import { union, inits, tails, subsequence, some, validate, guard, context, precedence, creator, 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';
8
- import { str } from '../source';
9
- import { trimBlankStart, trimNodeEnd, stringify } from '../util';
8
+ import { unescsource, str } from '../source';
9
+ import { trimNode, stringify } from '../util';
10
10
  import { html, define, defrag } from 'typed-dom/dom';
11
11
  import { ReadonlyURL } from 'spica/url';
12
12
 
@@ -17,7 +17,7 @@ Object.setPrototypeOf(optspec, null);
17
17
 
18
18
  export const link: LinkParser = lazy(() => validate(['[', '{'], creator(10, precedence(3, bind(
19
19
  guard(context => context.syntax?.inline?.link ?? true,
20
- reverse(tails([
20
+ fmap(subsequence([
21
21
  context({ syntax: { inline: {
22
22
  link: false,
23
23
  }}},
@@ -36,15 +36,22 @@ export const link: LinkParser = lazy(() => validate(['[', '{'], creator(10, prec
36
36
  media: false,
37
37
  autolink: false,
38
38
  }}},
39
- trimBlankStart(some(inline, ']', [[/^\\?\n/, 9], [']', 3]]))),
39
+ some(inline, ']', [[/^\\?\n/, 9], [']', 3]])),
40
40
  ']',
41
- true),
41
+ true,
42
+ undefined,
43
+ ([, ns = [], rest], next) => next[0] === ']' ? undefined : optimize('[', ns, rest)),
42
44
  ]))),
45
+ // 全体の失敗が確定した時も解析し予算を浪費している
43
46
  dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
44
- ]))),
45
- ([params, content = []]: [string[], (HTMLElement | string)[]], rest, context) => {
47
+ ]),
48
+ ([as, bs = []]) => bs[0] === '\r' && bs.shift() ? [as, bs] : as[0] === '\r' && as.shift() ? [[], as] : [as, []])),
49
+ ([content, params]: [(HTMLElement | string)[], string[]], rest, context) => {
50
+ if (params.length === 0) return;
51
+ assert(content[0] !== '' || params.length === 0);
52
+ if (content[0] === '') return [content, rest];
46
53
  assert(params.every(p => typeof p === 'string'));
47
- content = trimNodeEnd(content);
54
+ if (content.length !== 0 && trimNode(content).length === 0) return;
48
55
  if (eval(some(autolink)(stringify(content), context))?.some(node => typeof node === 'object')) return;
49
56
  assert(!html('div', content).querySelector('a, .media, .annotation, .reference') || (content[0] as HTMLElement).matches('.media'));
50
57
  const INSECURE_URI = params.shift()!;
@@ -62,10 +69,35 @@ export const link: LinkParser = lazy(() => validate(['[', '{'], creator(10, prec
62
69
  return [[define(el, attributes('link', [], optspec, params))], rest];
63
70
  })))));
64
71
 
65
- export const uri: LinkParser.ParameterParser.UriParser = union([
72
+ export const textlink: TextLinkParser = lazy(() => validate(['[', '{'], creator(10, precedence(3, bind(
73
+ reverse(tails([
74
+ dup(surround('[', some(union([unescsource]), ']'), ']')),
75
+ dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
76
+ ])),
77
+ ([params, content = []], rest, context) => {
78
+ assert(params[0] === '\r');
79
+ params.shift();
80
+ assert(params.every(p => typeof p === 'string'));
81
+ trimNode(content);
82
+ const INSECURE_URI = params.shift()!;
83
+ assert(INSECURE_URI === INSECURE_URI.trim());
84
+ assert(!INSECURE_URI.match(/\s/));
85
+ const el = elem(
86
+ INSECURE_URI,
87
+ defrag(content),
88
+ new ReadonlyURL(
89
+ resolve(INSECURE_URI, context.host ?? location, context.url ?? context.host ?? location),
90
+ context.host?.href || location.href),
91
+ context.host?.origin || location.origin);
92
+ assert(!el.classList.contains('invalid'));
93
+ assert(el.classList.length === 0);
94
+ return [[define(el, attributes('link', [], optspec, params))], rest];
95
+ })))));
96
+
97
+ export const uri: LinkParser.ParameterParser.UriParser = fmap(union([
66
98
  open(/^[^\S\n]+/, str(/^\S+/)),
67
99
  str(/^[^\s{}]+/),
68
- ]);
100
+ ]), ([uri]) => ['\r', uri]);
69
101
 
70
102
  export const option: LinkParser.ParameterParser.OptionParser = union([
71
103
  fmap(str(/^[^\S\n]+nofollow(?=[^\S\n]|})/), () => [` rel="nofollow"`]),
@@ -163,3 +195,12 @@ function decode(uri: string): string {
163
195
  return uri.replace(/\s+/g, encodeURI);
164
196
  }
165
197
  }
198
+
199
+ export function optimize(opener: string, ns: readonly (string | HTMLElement)[], rest: string): Result<string> {
200
+ let count = 0;
201
+ for (let i = 0; i < ns.length - 1; i += 2) {
202
+ if (ns[i] !== '' || ns[i + 1] !== opener[0]) break;
203
+ ++count;
204
+ }
205
+ return [['', opener[0].repeat(opener.length + count)], rest.slice(count)];
206
+ }
@@ -1,13 +1,13 @@
1
1
  import { undefined, location } from 'spica/global';
2
2
  import { MediaParser } from '../inline';
3
3
  import { union, inits, tails, some, validate, verify, guard, precedence, creator, surround, open, dup, lazy, fmap, bind } from '../../combinator';
4
- import { link, uri, option as linkoption, resolve } from './link';
4
+ import { textlink, uri, option as linkoption, resolve } from './link';
5
5
  import { attributes } from './html';
6
6
  import { unsafehtmlentity } from './htmlentity';
7
7
  import { txt, str } from '../source';
8
8
  import { html, define } from 'typed-dom/dom';
9
9
  import { ReadonlyURL } from 'spica/url';
10
- import { unshift, push } from 'spica/array';
10
+ import { unshift, shift, push } from 'spica/array';
11
11
 
12
12
  const optspec = {
13
13
  'width': [],
@@ -28,7 +28,7 @@ export const media: MediaParser = lazy(() => validate(['![', '!{'], creator(10,
28
28
  true)),
29
29
  dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]*}/)),
30
30
  ]))),
31
- ([as, bs]) => bs ? [[as.join('').trim() || as.join('')], bs] : [[''], as]),
31
+ ([as, bs]) => bs ? [[as.join('').trim() || as.join('')], shift(bs)[1]] : [[''], shift(as)[1]]),
32
32
  ([[text]]) => text === '' || text.trim() !== ''),
33
33
  ([[text], params], rest, context) => {
34
34
  assert(text === text.trim());
@@ -54,7 +54,7 @@ export const media: MediaParser = lazy(() => validate(['![', '!{'], creator(10,
54
54
  }
55
55
  if (context.syntax?.inline?.link === false || cache && cache.tagName !== 'IMG') return [[el], rest];
56
56
  return fmap(
57
- link as MediaParser,
57
+ textlink as MediaParser,
58
58
  ([link]) => [define(link, { target: '_blank' }, [el])])
59
59
  (`{ ${INSECURE_URI}${params.join('')} }${rest}`, context);
60
60
  })))));
@@ -14,14 +14,15 @@ describe('Unit: parser/inline/reference', () => {
14
14
  assert.deepStrictEqual(inspect(parser('[[]]')), undefined);
15
15
  assert.deepStrictEqual(inspect(parser('[[]]]')), undefined);
16
16
  assert.deepStrictEqual(inspect(parser('[[ ]]')), undefined);
17
+ assert.deepStrictEqual(inspect(parser('[[ [a')), [['', '[['], ' [a']);
17
18
  assert.deepStrictEqual(inspect(parser('[[\n]]')), undefined);
18
19
  assert.deepStrictEqual(inspect(parser('[[\na]]')), undefined);
19
20
  assert.deepStrictEqual(inspect(parser('[[\\\na]]')), undefined);
20
- assert.deepStrictEqual(inspect(parser('[[a\n]]')), [['[['], 'a\n]]']);
21
- assert.deepStrictEqual(inspect(parser('[[a\\\n]]')), [['[['], 'a\\\n]]']);
22
- assert.deepStrictEqual(inspect(parser('[[a\nb]]')), [['[['], 'a\nb]]']);
23
- assert.deepStrictEqual(inspect(parser('[[a\\\nb]]')), [['[['], 'a\\\nb]]']);
24
- assert.deepStrictEqual(inspect(parser('[[*a\nb*]]')), [['[['], '*a\nb*]]']);
21
+ assert.deepStrictEqual(inspect(parser('[[a\n]]')), [['', '[['], 'a\n]]']);
22
+ assert.deepStrictEqual(inspect(parser('[[a\\\n]]')), [['', '[['], 'a\\\n]]']);
23
+ assert.deepStrictEqual(inspect(parser('[[a\nb]]')), [['', '[['], 'a\nb]]']);
24
+ assert.deepStrictEqual(inspect(parser('[[a\\\nb]]')), [['', '[['], 'a\\\nb]]']);
25
+ assert.deepStrictEqual(inspect(parser('[[*a\nb*]]')), [['', '[['], '*a\nb*]]']);
25
26
  assert.deepStrictEqual(inspect(parser('[[\\]]')), undefined);
26
27
  assert.deepStrictEqual(inspect(parser('[[a]b]]')), undefined);
27
28
  assert.deepStrictEqual(inspect(parser('[[[a]]')), undefined);
@@ -1,15 +1,16 @@
1
1
  import { undefined } from 'spica/global';
2
2
  import { ReferenceParser } from '../inline';
3
- import { Result } from '../../combinator/data/parser';
4
3
  import { union, subsequence, some, validate, guard, context, precedence, creator, recursion, surround, open, lazy, bind } from '../../combinator';
5
4
  import { inline } from '../inline';
5
+ import { optimize } from './link';
6
6
  import { str, stropt } from '../source';
7
- import { regBlankStart, trimBlankStart, trimNodeEnd, stringify } from '../util';
7
+ import { regBlankStart, startLoose, trimNode, stringify } from '../util';
8
8
  import { html, defrag } from 'typed-dom/dom';
9
9
 
10
10
  export const reference: ReferenceParser = lazy(() => validate('[[', creator(recursion(precedence(6, surround(
11
11
  '[[',
12
12
  guard(context => context.syntax?.inline?.reference ?? true,
13
+ startLoose(
13
14
  context({ syntax: { inline: {
14
15
  annotation: false,
15
16
  reference: false,
@@ -23,11 +24,11 @@ export const reference: ReferenceParser = lazy(() => validate('[[', creator(recu
23
24
  subsequence([
24
25
  abbr,
25
26
  open(stropt(/^(?=\^)/), some(inline, ']', [[/^\\?\n/, 9], [']', 3], [']]', 6]])),
26
- trimBlankStart(some(inline, ']', [[/^\\?\n/, 9], [']', 3], [']]', 6]])),
27
- ]))),
27
+ some(inline, ']', [[/^\\?\n/, 9], [']', 3], [']]', 6]]),
28
+ ])), ']')),
28
29
  ']]',
29
30
  false,
30
- ([, ns], rest) => [[html('sup', attributes(ns), [html('span', trimNodeEnd(defrag(ns)))])], rest],
31
+ ([, ns], rest) => [[html('sup', attributes(ns), [html('span', trimNode(defrag(ns)))])], rest],
31
32
  ([, ns, rest], next) => next[0] === ']' ? undefined : optimize('[[', ns, rest)))))));
32
33
 
33
34
  const abbr: ReferenceParser.AbbrParser = creator(bind(surround(
@@ -51,12 +52,3 @@ function attributes(ns: (string | HTMLElement)[]): Record<string, string | undef
51
52
  }
52
53
  : { class: 'reference' };
53
54
  }
54
-
55
- export function optimize(opener: string, ns: readonly (string | HTMLElement)[], rest: string): Result<string> {
56
- let count = 0;
57
- for (let i = 0; i < ns.length - 1; i += 2) {
58
- if (ns[i] !== '' || ns[i + 1] !== opener[0]) break;
59
- ++count;
60
- }
61
- return [[opener[0].repeat(opener.length + count)], rest.slice(count)];
62
- }
@@ -143,12 +143,12 @@ describe('Unit: parser/inline', () => {
143
143
  assert.deepStrictEqual(inspect(parser('Di$ney Micro$oft')), [['Di', '$', 'ney', ' ', 'Micro', '$', 'oft'], '']);
144
144
  assert.deepStrictEqual(inspect(parser('Di$ney, Micro$oft')), [['Di', '$', 'ney', ',', ' ', 'Micro', '$', 'oft'], '']);
145
145
  assert.deepStrictEqual(inspect(parser('(((a))')), [['', '(', '<sup class="annotation"><span>a</span></sup>'], '']);
146
- assert.deepStrictEqual(inspect(parser('((((a))')), [['((', '<sup class="annotation"><span>a</span></sup>'], '']);
146
+ assert.deepStrictEqual(inspect(parser('((((a))')), [['', '((', '<sup class="annotation"><span>a</span></sup>'], '']);
147
147
  assert.deepStrictEqual(inspect(parser('((((a))))')), [['<sup class="annotation"><span><span class="paren">((a))</span></span></sup>'], '']);
148
148
  assert.deepStrictEqual(inspect(parser('((<bdi>))')), [['<sup class="annotation"><span><span class="invalid">&lt;bdi&gt;</span></span></sup>'], '']);
149
149
  assert.deepStrictEqual(inspect(parser('"((""))')), [['"', '<sup class="annotation"><span>""</span></sup>'], '']);
150
150
  assert.deepStrictEqual(inspect(parser('[[[a]]')), [['', '[', '<sup class="reference"><span>a</span></sup>'], '']);
151
- assert.deepStrictEqual(inspect(parser('[[[[a]]')), [['[[', '<sup class="reference"><span>a</span></sup>'], '']);
151
+ assert.deepStrictEqual(inspect(parser('[[[[a]]')), [['', '[[', '<sup class="reference"><span>a</span></sup>'], '']);
152
152
  assert.deepStrictEqual(inspect(parser('[[[[a]]]]')), [['<sup class="reference"><span>[[a]]</span></sup>'], '']);
153
153
  assert.deepStrictEqual(inspect(parser('[[[$-1]]]')), [['<sup class="reference"><span><a class="label" data-label="$-1">$-1</a></span></sup>'], '']);
154
154
  assert.deepStrictEqual(inspect(parser('[[[]{a}]]')), [['<sup class="reference"><span><a href="a">a</a></span></sup>'], '']);
@@ -165,7 +165,7 @@ describe('Unit: parser/inline', () => {
165
165
  assert.deepStrictEqual(inspect(parser('[^a@b')), [['[^', '<a class="email" href="mailto:a@b">a@b</a>'], '']);
166
166
  assert.deepStrictEqual(inspect(parser('[#a*b\nc*]')), [['[', '<a href="/hashtags/a" class="hashtag">#a</a>', '<em>b<br>c</em>', ']'], '']);
167
167
  assert.deepStrictEqual(inspect(parser('[*a\nb*]{/}')), [['[', '<em>a<br>b</em>', ']', '<a href="/">/</a>'], '']);
168
- assert.deepStrictEqual(inspect(parser('[[*a\nb*]]')), [['[[', '<em>a<br>b</em>', ']', ']'], '']);
168
+ assert.deepStrictEqual(inspect(parser('[[*a\nb*]]')), [['', '[[', '<em>a<br>b</em>', ']', ']'], '']);
169
169
  assert.deepStrictEqual(inspect(parser('"[% *"*"*')), [['"', '[%', ' ', '*', '"', '*', '"', '*'], '']);
170
170
  assert.deepStrictEqual(inspect(parser('"[% "*"* %]')), [['"', '[%', ' ', '"', '*', '"', '*', ' ', '%', ']'], '']);
171
171
  });
@@ -34,6 +34,7 @@ export import MathParser = InlineParser.MathParser;
34
34
  export import ExtensionParser = InlineParser.ExtensionParser;
35
35
  export import RubyParser = InlineParser.RubyParser;
36
36
  export import LinkParser = InlineParser.LinkParser;
37
+ export import TextLinkParser = InlineParser.TextLinkParser;
37
38
  export import HTMLParser = InlineParser.HTMLParser;
38
39
  export import InsertionParser = InlineParser.InsertionParser;
39
40
  export import DeletionParser = InlineParser.DeletionParser;
@@ -182,24 +182,24 @@ export function trimBlankEnd<T extends HTMLElement | string>(parser: Parser<T>):
182
182
  parser,
183
183
  trimNodeEnd);
184
184
  }
185
- //export function trimNode(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
186
- // return trimNodeStart(trimNodeEnd(nodes));
187
- //}
188
- //function trimNodeStart(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
189
- // for (let node = nodes[0]; nodes.length > 0 && !isVisible(node = nodes[0], 0);) {
190
- // if (nodes.length === 1 && typeof node === 'object' && node.className === 'indexer') break;
191
- // if (typeof node === 'string') {
192
- // const pos = node.length - node.trimStart().length;
193
- // if (pos > 0) {
194
- // nodes[0] = node.slice(pos);
195
- // break;
196
- // }
197
- // }
198
- // nodes.shift();
199
- // }
200
- // return nodes;
201
- //}
202
- export function trimNodeEnd<T extends HTMLElement | string>(nodes: T[]): T[] {
185
+ export function trimNode<T extends HTMLElement | string>(nodes: T[]): T[] {
186
+ return trimNodeStart(trimNodeEnd(nodes));
187
+ }
188
+ function trimNodeStart<T extends HTMLElement | string>(nodes: T[]): T[] {
189
+ for (let node = nodes[0]; nodes.length > 0 && !isVisible(node = nodes[0], 0);) {
190
+ if (nodes.length === 1 && typeof node === 'object' && node.className === 'indexer') break;
191
+ if (typeof node === 'string') {
192
+ const pos = node.trimStart().length;
193
+ if (pos > 0) {
194
+ nodes[0] = node.slice(pos) as T;
195
+ break;
196
+ }
197
+ }
198
+ nodes.shift();
199
+ }
200
+ return nodes;
201
+ }
202
+ function trimNodeEnd<T extends HTMLElement | string>(nodes: T[]): T[] {
203
203
  const skip = nodes.length > 0 &&
204
204
  typeof nodes[nodes.length - 1] === 'object' &&
205
205
  nodes[nodes.length - 1]['className'] === 'indexer'