securemark 0.222.0 → 0.223.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,8 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.223.0
4
+
5
+ - Refine some parsers to allow and trim leading whitespace.
6
+
3
7
  ## 0.222.0
4
8
 
5
- - Change link parser to trim the content.
9
+ - Change link parser to trim whitespace.
6
10
 
7
11
  ## 0.221.0
8
12
 
@@ -1,4 +1,4 @@
1
- /*! securemark v0.222.0 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED */
1
+ /*! securemark v0.223.0 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED */
2
2
  require = function () {
3
3
  function r(e, n, t) {
4
4
  function o(i, f) {
@@ -6625,7 +6625,7 @@ require = function () {
6625
6625
  exports.annotation = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('((', '))', '\n', (0, combinator_1.fmap)((0, combinator_1.surround)('((', (0, combinator_1.guard)(context => {
6626
6626
  var _a, _b, _c;
6627
6627
  return (_c = (_b = (_a = context.syntax) === null || _a === void 0 ? void 0 : _a.inline) === null || _b === void 0 ? void 0 : _b.annotation) !== null && _c !== void 0 ? _c : true;
6628
- }, (0, util_1.startTight)((0, combinator_1.context)({
6628
+ }, (0, util_1.startLoose)((0, util_1.visible)((0, combinator_1.context)({
6629
6629
  syntax: {
6630
6630
  inline: {
6631
6631
  annotation: false,
@@ -6633,7 +6633,7 @@ require = function () {
6633
6633
  }
6634
6634
  },
6635
6635
  state: global_1.undefined
6636
- }, (0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, ')', /^\\?\n/)])))), '))'), ns => [(0, typed_dom_1.html)('sup', { class: 'annotation' }, (0, util_1.trimEnd)((0, typed_dom_1.defrag)(ns)))]))));
6636
+ }, (0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, ')', /^\\?\n/)]))))), '))'), ns => [(0, typed_dom_1.html)('sup', { class: 'annotation' }, (0, util_1.trimNode)((0, typed_dom_1.defrag)(ns)))]))));
6637
6637
  },
6638
6638
  {
6639
6639
  '../../combinator': 30,
@@ -7064,7 +7064,7 @@ require = function () {
7064
7064
  exports.emphasis = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.surround)((0, combinator_1.close)((0, source_1.str)('*'), /^(?!\*)/), (0, util_1.startTight)((0, combinator_1.some)((0, combinator_1.union)([
7065
7065
  strong_1.strong,
7066
7066
  (0, combinator_1.some)(inline_1.inline, '*')
7067
- ]))), (0, source_1.str)('*'), false, ([as, bs, cs], rest) => (0, util_1.verifyEndTight)(bs) ? [
7067
+ ]))), (0, source_1.str)('*'), false, ([as, bs, cs], rest) => (0, util_1.isEndTightNodes)(bs) ? [
7068
7068
  [(0, typed_dom_1.html)('em', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))],
7069
7069
  rest
7070
7070
  ] : [
@@ -7098,14 +7098,14 @@ require = function () {
7098
7098
  const array_1 = _dereq_('spica/array');
7099
7099
  exports.emstrong = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.surround)((0, source_1.str)('***'), (0, util_1.startTight)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, '*')])), (0, source_1.str)(/^\*{1,3}/), false, ([as, bs, cs], rest, context) => {
7100
7100
  var _a, _b;
7101
- if (!(0, util_1.verifyEndTight)(bs))
7101
+ if (!(0, util_1.isEndTightNodes)(bs))
7102
7102
  return [
7103
7103
  (0, array_1.unshift)(as, bs),
7104
7104
  cs[0] + rest
7105
7105
  ];
7106
7106
  switch (cs[0]) {
7107
7107
  case '*':
7108
- return (_a = (0, combinator_1.bind)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, '**')]), (ds, rest) => rest.slice(0, 2) === '**' && (0, util_1.verifyEndTight)(ds) ? [
7108
+ return (_a = (0, combinator_1.bind)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, '**')]), (ds, rest) => rest.slice(0, 2) === '**' && (0, util_1.isEndTightNodes)(ds) ? [
7109
7109
  [(0, typed_dom_1.html)('strong', (0, array_1.unshift)([(0, typed_dom_1.html)('em', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))], (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(ds))))],
7110
7110
  rest.slice(2)
7111
7111
  ] : [
@@ -7122,7 +7122,7 @@ require = function () {
7122
7122
  rest
7123
7123
  ];
7124
7124
  case '**':
7125
- return (_b = (0, combinator_1.bind)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, '*')]), (ds, rest) => rest.slice(0, 1) === '*' && (0, util_1.verifyEndTight)(ds) ? [
7125
+ return (_b = (0, combinator_1.bind)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, '*')]), (ds, rest) => rest.slice(0, 1) === '*' && (0, util_1.isEndTightNodes)(ds) ? [
7126
7126
  [(0, typed_dom_1.html)('em', (0, array_1.unshift)([(0, typed_dom_1.html)('strong', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))], (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(ds))))],
7127
7127
  rest.slice(1)
7128
7128
  ] : [
@@ -7246,7 +7246,7 @@ require = function () {
7246
7246
  }, (0, combinator_1.open)((0, source_1.str)(/^\|?/, false), (0, combinator_1.some)((0, combinator_1.union)([
7247
7247
  signature,
7248
7248
  inline_1.inline
7249
- ]), ']', /^\\?\n/), true)))), ']'), ns => [(0, typed_dom_1.html)('a', (0, util_1.trimEnd)((0, typed_dom_1.defrag)(ns)))])), ([el]) => [(0, typed_dom_1.define)(el, {
7249
+ ]), ']', /^\\?\n/), true)))), ']'), ns => [(0, typed_dom_1.html)('a', (0, util_1.trimNodeEnd)((0, typed_dom_1.defrag)(ns)))])), ([el]) => [(0, typed_dom_1.define)(el, {
7250
7250
  id: el.id ? null : global_1.undefined,
7251
7251
  class: 'index',
7252
7252
  href: el.id ? `#${ el.id }` : global_1.undefined
@@ -7485,7 +7485,7 @@ require = function () {
7485
7485
  [(0, typed_dom_1.html)(tag, attributes('html', [], attrspec[tag], as))],
7486
7486
  rest
7487
7487
  ]), ([, tag]) => tag)),
7488
- (0, combinator_1.match)(/^(?=<(sup|sub|small|bdo|bdi)(?=[^\S\n]|>))/, (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.validate)(`<${ tag }`, `</${ tag }>`, (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${ tag }`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)('>'), true), (0, util_1.startTight)((0, combinator_1.context)((() => {
7488
+ (0, combinator_1.match)(/^(?=<(sup|sub|small|bdo|bdi)(?=[^\S\n]|>))/, (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.validate)(`<${ tag }`, `</${ tag }>`, (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${ tag }`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)('>'), true), (0, util_1.startLoose)((0, util_1.visible)((0, combinator_1.context)((() => {
7489
7489
  switch (tag) {
7490
7490
  case 'sup':
7491
7491
  case 'sub':
@@ -7507,11 +7507,11 @@ require = function () {
7507
7507
  default:
7508
7508
  return {};
7509
7509
  }
7510
- })(), (0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), `</${ tag }>`))), (0, source_1.str)(`</${ tag }>`), false, ([as, bs, cs], rest, context) => [
7510
+ })(), (0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), `</${ tag }>`)))), (0, source_1.str)(`</${ tag }>`), false, ([as, bs, cs], rest, context) => [
7511
7511
  [elem(tag, as, (0, util_1.trimEndBR)((0, typed_dom_1.defrag)(bs)), cs, context)],
7512
7512
  rest
7513
7513
  ])), ([, tag]) => tag)),
7514
- (0, combinator_1.match)(/^(?=<([a-z]+)(?=[^\S\n]|>))/, (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.validate)(`<${ tag }`, `</${ tag }>`, (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${ tag }`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)('>'), true), (0, util_1.startTight)((0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), `</${ tag }>`)), (0, source_1.str)(`</${ tag }>`), false, ([as, bs, cs], rest) => [
7514
+ (0, combinator_1.match)(/^(?=<([a-z]+)(?=[^\S\n]|>))/, (0, memoize_1.memoize)(([, tag]) => (0, combinator_1.validate)(`<${ tag }`, `</${ tag }>`, (0, combinator_1.surround)((0, combinator_1.surround)((0, source_1.str)(`<${ tag }`), (0, combinator_1.some)(exports.attribute), (0, source_1.str)('>'), true), (0, util_1.startLoose)((0, util_1.visible)((0, combinator_1.some)((0, combinator_1.union)([inline_1.inline]), `</${ tag }>`))), (0, source_1.str)(`</${ tag }>`), false, ([as, bs, cs], rest) => [
7515
7515
  [elem(tag, as, (0, util_1.trimEndBR)((0, typed_dom_1.defrag)(bs)), cs, {})],
7516
7516
  rest
7517
7517
  ], ([as, bs], rest) => as.length === 1 ? [
@@ -7672,7 +7672,7 @@ require = function () {
7672
7672
  (0, combinator_1.context)({ syntax: { inline: { link: false } } }, (0, combinator_1.dup)((0, combinator_1.union)([
7673
7673
  (0, combinator_1.surround)('[', inline_1.media, ']'),
7674
7674
  (0, combinator_1.surround)('[', inline_1.shortmedia, ']'),
7675
- (0, combinator_1.surround)('[', (0, util_1.startTight)((0, combinator_1.context)({
7675
+ (0, combinator_1.surround)('[', (0, util_1.startLoose)((0, util_1.visible)((0, combinator_1.context)({
7676
7676
  syntax: {
7677
7677
  inline: {
7678
7678
  annotation: false,
@@ -7683,7 +7683,7 @@ require = function () {
7683
7683
  autolink: false
7684
7684
  }
7685
7685
  }
7686
- }, (0, combinator_1.some)(inline_1.inline, ']', /^\\?\n/))), ']', true)
7686
+ }, (0, combinator_1.some)(inline_1.inline, ']', /^\\?\n/)))), ']', true)
7687
7687
  ]))),
7688
7688
  (0, combinator_1.dup)((0, combinator_1.surround)(/^{(?![{}])/, (0, combinator_1.inits)([
7689
7689
  exports.uri,
@@ -7694,7 +7694,7 @@ require = function () {
7694
7694
  if ((0, parser_1.eval)((0, combinator_1.some)(autolink_1.autolink)((0, util_1.stringify)(content), context), []).some(node => typeof node === 'object'))
7695
7695
  return;
7696
7696
  const INSECURE_URI = params.shift();
7697
- const el = create(INSECURE_URI, (0, util_1.trimEnd)((0, typed_dom_1.defrag)(content)), new url_1.ReadonlyURL(resolve(INSECURE_URI, context.host || global_1.location, context.url || global_1.location), ((_a = context.host) === null || _a === void 0 ? void 0 : _a.href) || global_1.location.href), ((_b = context.host) === null || _b === void 0 ? void 0 : _b.origin) || global_1.location.origin);
7697
+ const el = create(INSECURE_URI, (0, util_1.trimNode)((0, typed_dom_1.defrag)(content)), new url_1.ReadonlyURL(resolve(INSECURE_URI, context.host || global_1.location, context.url || global_1.location), ((_a = context.host) === null || _a === void 0 ? void 0 : _a.href) || global_1.location.href), ((_b = context.host) === null || _b === void 0 ? void 0 : _b.origin) || global_1.location.origin);
7698
7698
  if (el.classList.contains('invalid'))
7699
7699
  return [
7700
7700
  [el],
@@ -7798,7 +7798,7 @@ require = function () {
7798
7798
  const util_1 = _dereq_('../util');
7799
7799
  const typed_dom_1 = _dereq_('typed-dom');
7800
7800
  const array_1 = _dereq_('spica/array');
7801
- exports.mark = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.surround)((0, source_1.str)('=='), (0, util_1.startTight)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, '==')])), (0, source_1.str)('=='), false, ([as, bs, cs], rest) => (0, util_1.verifyEndTight)(bs) ? [
7801
+ exports.mark = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.surround)((0, source_1.str)('=='), (0, util_1.startTight)((0, combinator_1.union)([(0, combinator_1.some)(inline_1.inline, '==')])), (0, source_1.str)('=='), false, ([as, bs, cs], rest) => (0, util_1.isEndTightNodes)(bs) ? [
7802
7802
  [(0, typed_dom_1.html)('mark', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))],
7803
7803
  rest
7804
7804
  ] : [
@@ -7830,7 +7830,7 @@ require = function () {
7830
7830
  const disallowedCommand = /\\(?:begin|tiny|huge|large)(?![0-9a-z])/i;
7831
7831
  exports.math = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('$', '$', '\n', (0, combinator_1.rewrite)((0, combinator_1.union)([
7832
7832
  (0, combinator_1.surround)('$', bracket, '$'),
7833
- (0, combinator_1.surround)('$', (0, combinator_1.verify)((0, source_1.str)(/^(?=[\\^_[(|]|[A-Za-z][0-9A-Za-z]*'*[ ~]?(?:\$|([\\^_(|:=<>])(?!\1)))(?:\\\$|[\x20-\x23\x25-\x7E])*/), util_1.verifyEndTight), /^\$(?![0-9A-Za-z])/)
7833
+ (0, combinator_1.surround)('$', (0, combinator_1.verify)((0, source_1.str)(/^(?=[\\^_[(|]|[A-Za-z][0-9A-Za-z]*'*[ ~]?(?:\$|([\\^_(|:=<>])(?!\1)))(?:\\\$|[\x20-\x23\x25-\x7E])*/), util_1.isEndTightNodes), /^\$(?![0-9A-Za-z])/)
7834
7834
  ]), (source, {
7835
7835
  caches: {math: cache} = {}
7836
7836
  }) => {
@@ -7874,7 +7874,6 @@ require = function () {
7874
7874
  const html_1 = _dereq_('./html');
7875
7875
  const htmlentity_1 = _dereq_('./htmlentity');
7876
7876
  const source_1 = _dereq_('../source');
7877
- const util_1 = _dereq_('../util');
7878
7877
  const typed_dom_1 = _dereq_('typed-dom');
7879
7878
  const url_1 = _dereq_('spica/url');
7880
7879
  const array_1 = _dereq_('spica/array');
@@ -7892,7 +7891,7 @@ require = function () {
7892
7891
  var _a, _b, _c;
7893
7892
  return (_c = (_b = (_a = context.syntax) === null || _a === void 0 ? void 0 : _a.inline) === null || _b === void 0 ? void 0 : _b.media) !== null && _c !== void 0 ? _c : true;
7894
7893
  }, (0, combinator_1.tails)([
7895
- (0, combinator_1.dup)((0, combinator_1.surround)(/^\[(?!\\?\s)/, (0, combinator_1.some)((0, combinator_1.union)([
7894
+ (0, combinator_1.dup)((0, combinator_1.surround)(/^\[(?!\s*\\\s)/, (0, combinator_1.some)((0, combinator_1.union)([
7896
7895
  htmlentity_1.htmlentity,
7897
7896
  bracket,
7898
7897
  source_1.txt
@@ -7902,12 +7901,12 @@ require = function () {
7902
7901
  (0, combinator_1.some)(option)
7903
7902
  ]), /^[^\S\n]?}/))
7904
7903
  ])))), ([as, bs]) => bs ? [
7905
- [(0, array_1.join)(as)],
7904
+ [(0, array_1.join)(as).trim() || (0, array_1.join)(as)],
7906
7905
  bs
7907
7906
  ] : [
7908
7907
  [''],
7909
7908
  as
7910
- ]), ([[text]]) => (0, util_1.verifyStartTight)([text || '-'])), ([[text], params], rest, context) => {
7909
+ ]), ([[text]]) => text === '' || text.trim() !== ''), ([[text], params], rest, context) => {
7911
7910
  var _a, _b, _c, _d;
7912
7911
  const INSECURE_URI = params.shift();
7913
7912
  const url = new url_1.ReadonlyURL((0, link_1.resolve)(INSECURE_URI, context.host || global_1.location, context.url || global_1.location), ((_a = context.host) === null || _a === void 0 ? void 0 : _a.href) || global_1.location.href);
@@ -7916,14 +7915,14 @@ require = function () {
7916
7915
  const el = cache && cached ? cache.get(url.href).cloneNode(true) : (0, typed_dom_1.html)('img', {
7917
7916
  class: 'media',
7918
7917
  'data-src': url.source,
7919
- alt: text.trimEnd()
7918
+ alt: text
7920
7919
  });
7921
7920
  if (!cached && !sanitize(url, el))
7922
7921
  return [
7923
7922
  [el],
7924
7923
  rest
7925
7924
  ];
7926
- cached && el.hasAttribute('alt') && el.setAttribute('alt', text.trimEnd());
7925
+ cached && el.hasAttribute('alt') && el.setAttribute('alt', text);
7927
7926
  (0, typed_dom_1.define)(el, (0, html_1.attributes)('media', (0, array_1.push)([], el.classList), optspec, params));
7928
7927
  if (((_d = (_c = context.syntax) === null || _c === void 0 ? void 0 : _c.inline) === null || _d === void 0 ? void 0 : _d.link) === false || cached && el.tagName !== 'IMG')
7929
7928
  return [
@@ -7988,7 +7987,6 @@ require = function () {
7988
7987
  {
7989
7988
  '../../combinator': 30,
7990
7989
  '../source': 131,
7991
- '../util': 137,
7992
7990
  './html': 114,
7993
7991
  './htmlentity': 115,
7994
7992
  './link': 117,
@@ -8013,7 +8011,7 @@ require = function () {
8013
8011
  exports.reference = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.validate)('[[', ']]', '\n', (0, combinator_1.fmap)((0, combinator_1.surround)('[[', (0, combinator_1.guard)(context => {
8014
8012
  var _a, _b, _c;
8015
8013
  return (_c = (_b = (_a = context.syntax) === null || _a === void 0 ? void 0 : _a.inline) === null || _b === void 0 ? void 0 : _b.reference) !== null && _c !== void 0 ? _c : true;
8016
- }, (0, util_1.startTight)((0, combinator_1.context)({
8014
+ }, (0, util_1.startLoose)((0, util_1.visible)((0, combinator_1.context)({
8017
8015
  syntax: {
8018
8016
  inline: {
8019
8017
  annotation: false,
@@ -8032,7 +8030,7 @@ require = function () {
8032
8030
  ''
8033
8031
  ]),
8034
8032
  (0, combinator_1.some)(inline_1.inline, ']', /^\\?\n/)
8035
- ])))), ']]'), ns => [(0, typed_dom_1.html)('sup', attributes(ns), (0, util_1.trimEnd)((0, typed_dom_1.defrag)(ns)))]))));
8033
+ ]))))), ']]'), ns => [(0, typed_dom_1.html)('sup', attributes(ns), (0, util_1.trimNode)((0, typed_dom_1.defrag)(ns)))]))));
8036
8034
  const abbr = (0, combinator_1.creator)((0, combinator_1.fmap)((0, combinator_1.verify)((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]+/), (_, rest, context) => (0, util_1.isStartTight)(rest, context)), ([source]) => [(0, typed_dom_1.html)('abbr', source)]));
8037
8035
  function attributes(ns) {
8038
8036
  return typeof ns[0] === 'object' && ns[0].tagName === 'ABBR' ? {
@@ -8071,7 +8069,7 @@ require = function () {
8071
8069
  exports.ruby = (0, combinator_1.lazy)(() => (0, combinator_1.creator)((0, combinator_1.bind)((0, combinator_1.verify)((0, combinator_1.validate)('[', ')', '\n', (0, combinator_1.sequence)([
8072
8070
  (0, combinator_1.surround)('[', (0, combinator_1.focus)(/^(?:\\[^\n]|[^\[\]\n])+(?=]\()/, text), ']'),
8073
8071
  (0, combinator_1.surround)('(', (0, combinator_1.focus)(/^(?:\\[^\n]|[^\(\)\n])+(?=\))/, text), ')')
8074
- ])), ([texts]) => (0, util_1.verifyStartTight)(texts)), ([texts, rubies], rest) => {
8072
+ ])), ([texts]) => (0, util_1.isStartTightNodes)(texts)), ([texts, rubies], rest) => {
8075
8073
  const tail = typeof texts[texts.length - 1] === 'object' ? [texts.pop()] : [];
8076
8074
  tail.length === 0 && texts[texts.length - 1] === '' && texts.pop();
8077
8075
  switch (true) {
@@ -8180,7 +8178,7 @@ require = function () {
8180
8178
  emphasis_1.emphasis,
8181
8179
  (0, combinator_1.some)(inline_1.inline, '*'),
8182
8180
  (0, source_1.str)('*')
8183
- ]), '**')), (0, source_1.str)('**'), false, ([as, bs, cs], rest) => (0, util_1.verifyEndTight)(bs) ? [
8181
+ ]), '**')), (0, source_1.str)('**'), false, ([as, bs, cs], rest) => (0, util_1.isEndTightNodes)(bs) ? [
8184
8182
  [(0, typed_dom_1.html)('strong', (0, typed_dom_1.defrag)((0, util_1.trimEndBR)(bs)))],
8185
8183
  rest
8186
8184
  ] : [
@@ -8944,7 +8942,7 @@ require = function () {
8944
8942
  function (_dereq_, module, exports) {
8945
8943
  'use strict';
8946
8944
  Object.defineProperty(exports, '__esModule', { value: true });
8947
- exports.stringify = exports.trimEndBR = exports.trimEnd = exports.verifyEndTight = exports.verifyStartTight = exports.isStartTight = exports.startTight = exports.visualize = void 0;
8945
+ exports.stringify = exports.trimEndBR = exports.trimNodeEnd = exports.trimNode = exports.visible = exports.isEndTightNodes = exports.isStartTightNodes = exports.isStartTight = exports.startTight = exports.startLoose = exports.visualize = void 0;
8948
8946
  const global_1 = _dereq_('spica/global');
8949
8947
  const parser_1 = _dereq_('../combinator/data/parser');
8950
8948
  const combinator_1 = _dereq_('../combinator');
@@ -9019,6 +9017,15 @@ require = function () {
9019
9017
  }
9020
9018
  return false;
9021
9019
  }
9020
+ function startLoose(parser) {
9021
+ return (source, context) => isStartLoose(source, context) ? parser(source, context) : global_1.undefined;
9022
+ }
9023
+ exports.startLoose = startLoose;
9024
+ function isStartLoose(source, context) {
9025
+ if (source === '')
9026
+ return true;
9027
+ return isStartTight(source.replace(/^[^\S\n]+/, ''), context);
9028
+ }
9022
9029
  function startTight(parser) {
9023
9030
  return (source, context) => isStartTight(source, context) ? parser(source, context) : global_1.undefined;
9024
9031
  }
@@ -9058,25 +9065,37 @@ require = function () {
9058
9065
  }
9059
9066
  }
9060
9067
  exports.isStartTight = isStartTight;
9061
- function verifyStartTight(nodes) {
9068
+ function isStartTightNodes(nodes) {
9062
9069
  if (nodes.length === 0)
9063
9070
  return true;
9064
9071
  return isVisible(nodes[0], 0);
9065
9072
  }
9066
- exports.verifyStartTight = verifyStartTight;
9067
- function verifyEndTight(nodes) {
9073
+ exports.isStartTightNodes = isStartTightNodes;
9074
+ function isEndTightNodes(nodes) {
9068
9075
  if (nodes.length === 0)
9069
9076
  return true;
9070
9077
  const last = nodes.length - 1;
9071
9078
  return typeof nodes[last] === 'string' && nodes[last].length > 1 ? isVisible(nodes[last], -1) || isVisible(nodes[last], -2) : isVisible(nodes[last], -1) || last === 0 || isVisible(nodes[last - 1], -1);
9072
9079
  }
9073
- exports.verifyEndTight = verifyEndTight;
9080
+ exports.isEndTightNodes = isEndTightNodes;
9081
+ function visible(parser) {
9082
+ return (0, combinator_1.verify)(parser, nodes => {
9083
+ if (nodes.length === 0)
9084
+ return true;
9085
+ for (let i = 0; i < nodes.length; ++i) {
9086
+ if (isVisible(nodes[i]))
9087
+ return true;
9088
+ }
9089
+ return false;
9090
+ });
9091
+ }
9092
+ exports.visible = visible;
9074
9093
  function isVisible(node, position) {
9075
9094
  if (!node)
9076
9095
  return false;
9077
9096
  switch (typeof node) {
9078
9097
  case 'string':
9079
- const char = node[position >= 0 ? position : node.length + position];
9098
+ const char = position === global_1.undefined ? node : node[position >= 0 ? position : node.length + position];
9080
9099
  switch (char) {
9081
9100
  case ' ':
9082
9101
  case '\t':
@@ -9099,7 +9118,25 @@ require = function () {
9099
9118
  }
9100
9119
  }
9101
9120
  }
9102
- function trimEnd(nodes) {
9121
+ function trimNode(nodes) {
9122
+ return trimNodeStart(trimNodeEnd(nodes));
9123
+ }
9124
+ exports.trimNode = trimNode;
9125
+ function trimNodeStart(nodes) {
9126
+ const skip = nodes.length > 0 && typeof nodes[nodes.length - 1] === 'object' && nodes[nodes.length - 1]['className'] === 'indexer' ? [nodes.pop()] : [];
9127
+ for (let first = nodes[0]; nodes.length > 0 && !isVisible(first, 0) && !(typeof first === 'object' && first.className === 'comment');) {
9128
+ if (typeof first === 'string') {
9129
+ const pos = first.length - first.trimStart().length;
9130
+ if (pos > 0) {
9131
+ nodes[0] = first.slice(pos);
9132
+ break;
9133
+ }
9134
+ }
9135
+ nodes.pop();
9136
+ }
9137
+ return (0, array_1.push)(nodes, skip);
9138
+ }
9139
+ function trimNodeEnd(nodes) {
9103
9140
  const skip = nodes.length > 0 && typeof nodes[nodes.length - 1] === 'object' && nodes[nodes.length - 1]['className'] === 'indexer' ? [nodes.pop()] : [];
9104
9141
  for (let last = nodes[0]; nodes.length > 0 && !isVisible(last = nodes[nodes.length - 1], -1) && !(typeof last === 'object' && last.className === 'comment');) {
9105
9142
  if (typeof last === 'string') {
@@ -9113,7 +9150,7 @@ require = function () {
9113
9150
  }
9114
9151
  return (0, array_1.push)(nodes, skip);
9115
9152
  }
9116
- exports.trimEnd = trimEnd;
9153
+ exports.trimNodeEnd = trimNodeEnd;
9117
9154
  function trimEndBR(nodes) {
9118
9155
  if (nodes.length === 0)
9119
9156
  return nodes;
package/package-lock.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.222.0",
3
+ "version": "0.223.0",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.222.0",
3
+ "version": "0.223.0",
4
4
  "description": "Secure markdown renderer working on browsers for user input data.",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/falsandtru/securemark",
@@ -14,8 +14,6 @@ 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))')), undefined);
18
- assert.deepStrictEqual(inspect(parser('(( a ))')), undefined);
19
17
  assert.deepStrictEqual(inspect(parser('((\n))')), undefined);
20
18
  assert.deepStrictEqual(inspect(parser('((\na))')), undefined);
21
19
  assert.deepStrictEqual(inspect(parser('((\\ a))')), undefined);
@@ -34,6 +32,8 @@ describe('Unit: parser/inline/annotation', () => {
34
32
  });
35
33
 
36
34
  it('basic', () => {
35
+ assert.deepStrictEqual(inspect(parser('(( a))')), [['<sup class="annotation">a</sup>'], '']);
36
+ assert.deepStrictEqual(inspect(parser('(( a ))')), [['<sup class="annotation">a</sup>'], '']);
37
37
  assert.deepStrictEqual(inspect(parser('((a))')), [['<sup class="annotation">a</sup>'], '']);
38
38
  assert.deepStrictEqual(inspect(parser('((a ))')), [['<sup class="annotation">a</sup>'], '']);
39
39
  assert.deepStrictEqual(inspect(parser('((a ))')), [['<sup class="annotation">a</sup>'], '']);
@@ -2,13 +2,13 @@ import { undefined } from 'spica/global';
2
2
  import { AnnotationParser } from '../inline';
3
3
  import { union, some, validate, guard, context, creator, surround, lazy, fmap } from '../../combinator';
4
4
  import { inline } from '../inline';
5
- import { startTight, trimEnd } from '../util';
5
+ import { startLoose, visible, trimNode } from '../util';
6
6
  import { html, defrag } from 'typed-dom';
7
7
 
8
8
  export const annotation: AnnotationParser = lazy(() => creator(validate('((', '))', '\n', fmap(surround(
9
9
  '((',
10
10
  guard(context => context.syntax?.inline?.annotation ?? true,
11
- startTight(
11
+ startLoose(visible(
12
12
  context({ syntax: { inline: {
13
13
  annotation: false,
14
14
  // Redundant
@@ -20,6 +20,6 @@ export const annotation: AnnotationParser = lazy(() => creator(validate('((', ')
20
20
  //link: true,
21
21
  //autolink: true,
22
22
  }}, state: undefined },
23
- union([some(inline, ')', /^\\?\n/)])))),
23
+ union([some(inline, ')', /^\\?\n/)]))))),
24
24
  '))'),
25
- ns => [html('sup', { class: 'annotation' }, trimEnd(defrag(ns)))]))));
25
+ ns => [html('sup', { class: 'annotation' }, trimNode(defrag(ns)))]))));
@@ -3,7 +3,7 @@ import { union, some, creator, surround, close, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { strong } from './strong';
5
5
  import { str } from '../source';
6
- import { startTight, verifyEndTight, trimEndBR } from '../util';
6
+ import { startTight, isEndTightNodes, trimEndBR } from '../util';
7
7
  import { html, defrag } from 'typed-dom';
8
8
  import { unshift } from 'spica/array';
9
9
 
@@ -12,7 +12,7 @@ export const emphasis: EmphasisParser = lazy(() => creator(surround(close(
12
12
  startTight(some(union([strong, some(inline, '*')]))),
13
13
  str('*'), false,
14
14
  ([as, bs, cs], rest) =>
15
- verifyEndTight(bs)
15
+ isEndTightNodes(bs)
16
16
  ? [[html('em', defrag(trimEndBR(bs)))], rest]
17
17
  : [unshift(as, bs), cs[0] + rest],
18
18
  ([as, bs], rest) => [unshift(as, bs), rest])));
@@ -2,7 +2,7 @@ import { EmStrongParser } from '../inline';
2
2
  import { union, some, creator, surround, lazy, bind } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { str } from '../source';
5
- import { startTight, verifyEndTight, trimEndBR } from '../util';
5
+ import { startTight, isEndTightNodes, trimEndBR } from '../util';
6
6
  import { html, defrag } from 'typed-dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
@@ -11,13 +11,13 @@ export const emstrong: EmStrongParser = lazy(() => creator(surround(
11
11
  startTight(union([some(inline, '*')])),
12
12
  str(/^\*{1,3}/), false,
13
13
  ([as, bs, cs], rest, context) => {
14
- if (!verifyEndTight(bs)) return [unshift(as, bs), cs[0] + rest];
14
+ if (!isEndTightNodes(bs)) return [unshift(as, bs), cs[0] + rest];
15
15
  switch (cs[0]) {
16
16
  case '*':
17
17
  return bind<EmStrongParser>(
18
18
  union([some(inline, '**')]),
19
19
  (ds, rest) =>
20
- rest.slice(0, 2) === '**' && verifyEndTight(ds)
20
+ rest.slice(0, 2) === '**' && isEndTightNodes(ds)
21
21
  ? [[html('strong', unshift([html('em', defrag(trimEndBR(bs)))], defrag(trimEndBR(ds))))], rest.slice(2)]
22
22
  : [unshift(['**', html('em', defrag(trimEndBR(bs)))], ds), rest])
23
23
  (rest, context) ?? [['**', html('em', defrag(trimEndBR(bs)))], rest];
@@ -25,7 +25,7 @@ export const emstrong: EmStrongParser = lazy(() => creator(surround(
25
25
  return bind<EmStrongParser>(
26
26
  union([some(inline, '*')]),
27
27
  (ds, rest) =>
28
- rest.slice(0, 1) === '*' && verifyEndTight(ds)
28
+ rest.slice(0, 1) === '*' && isEndTightNodes(ds)
29
29
  ? [[html('em', unshift([html('strong', defrag(trimEndBR(bs)))], defrag(trimEndBR(ds))))], rest.slice(1)]
30
30
  : [unshift(['*', html('strong', defrag(trimEndBR(bs)))], ds), rest])
31
31
  (rest, context) ?? [['*', html('strong', defrag(trimEndBR(bs)))], rest];
@@ -4,7 +4,7 @@ import { union, some, validate, guard, context, creator, surround, open, lazy, f
4
4
  import { inline } from '../../inline';
5
5
  import { indexee, identify } from './indexee';
6
6
  import { txt, str } from '../../source';
7
- import { startTight, trimEnd } from '../../util';
7
+ import { startTight, trimNodeEnd } from '../../util';
8
8
  import { html, define, defrag } from 'typed-dom';
9
9
  import { join } from 'spica/array';
10
10
 
@@ -29,7 +29,7 @@ export const index: IndexParser = lazy(() => creator(validate('[#', ']', '\n', f
29
29
  inline,
30
30
  ]), ']', /^\\?\n/), true)))),
31
31
  ']'),
32
- ns => [html('a', trimEnd(defrag(ns)))])),
32
+ ns => [html('a', trimNodeEnd(defrag(ns)))])),
33
33
  ([el]: [HTMLAnchorElement]) => [
34
34
  define(el,
35
35
  {
@@ -29,8 +29,6 @@ describe('Unit: parser/inline/html', () => {
29
29
  assert.deepStrictEqual(inspect(parser('<small>z')), undefined);
30
30
  assert.deepStrictEqual(inspect(parser('<small></small>')), undefined);
31
31
  assert.deepStrictEqual(inspect(parser('<small> </small>')), undefined);
32
- assert.deepStrictEqual(inspect(parser('<small> a</small>')), undefined);
33
- assert.deepStrictEqual(inspect(parser('<small> a </small>')), undefined);
34
32
  assert.deepStrictEqual(inspect(parser('<small>\n</small>')), undefined);
35
33
  assert.deepStrictEqual(inspect(parser('<small>\na</small>')), undefined);
36
34
  assert.deepStrictEqual(inspect(parser('<small>\\ a</small>')), undefined);
@@ -59,6 +57,8 @@ describe('Unit: parser/inline/html', () => {
59
57
  });
60
58
 
61
59
  it('basic', () => {
60
+ assert.deepStrictEqual(inspect(parser('<small> a</small>')), [['<small> a</small>'], '']);
61
+ assert.deepStrictEqual(inspect(parser('<small> a </small>')), [['<small> a </small>'], '']);
62
62
  assert.deepStrictEqual(inspect(parser('<small>a</small>')), [['<small>a</small>'], '']);
63
63
  assert.deepStrictEqual(inspect(parser('<small>a</small>a')), [['<small>a</small>'], 'a']);
64
64
  assert.deepStrictEqual(inspect(parser('<small>a </small>')), [['<small>a </small>'], '']);
@@ -5,7 +5,7 @@ import { HTMLParser } from '../inline';
5
5
  import { union, some, validate, context, creator, surround, match, lazy } from '../../combinator';
6
6
  import { inline } from '../inline';
7
7
  import { str } from '../source';
8
- import { startTight, trimEndBR } from '../util';
8
+ import { startLoose, visible, trimEndBR } from '../util';
9
9
  import { html as h, defrag } from 'typed-dom';
10
10
  import { memoize } from 'spica/memoize';
11
11
  import { Cache } from 'spica/cache';
@@ -39,7 +39,7 @@ export const html: HTMLParser = lazy(() => creator(validate('<', '>', '\n', vali
39
39
  validate(`<${tag}`, `</${tag}>`,
40
40
  surround<HTMLParser.TagParser, string>(surround(
41
41
  str(`<${tag}`), some(attribute), str('>'), true),
42
- startTight(
42
+ startLoose(visible(
43
43
  context((() => {
44
44
  switch (tag) {
45
45
  case 'sup':
@@ -63,7 +63,7 @@ export const html: HTMLParser = lazy(() => creator(validate('<', '>', '\n', vali
63
63
  return {};
64
64
  }
65
65
  })(),
66
- some(union([inline]), `</${tag}>`))),
66
+ some(union([inline]), `</${tag}>`)))),
67
67
  str(`</${tag}>`), false,
68
68
  ([as, bs, cs], rest, context) =>
69
69
  [[elem(tag, as, trimEndBR(defrag(bs)), cs, context)], rest])),
@@ -75,7 +75,7 @@ export const html: HTMLParser = lazy(() => creator(validate('<', '>', '\n', vali
75
75
  validate(`<${tag}`, `</${tag}>`,
76
76
  surround<HTMLParser.TagParser, string>(surround(
77
77
  str(`<${tag}`), some(attribute), str('>'), true),
78
- startTight(some(union([inline]), `</${tag}>`)),
78
+ startLoose(visible(some(union([inline]), `</${tag}>`))),
79
79
  str(`</${tag}>`), false,
80
80
  ([as, bs, cs], rest) =>
81
81
  [[elem(tag, as, trimEndBR(defrag(bs)), cs, {})], rest],
@@ -65,8 +65,6 @@ describe('Unit: parser/inline/link', () => {
65
65
  assert.deepStrictEqual(inspect(parser('[[]{}')), undefined);
66
66
  assert.deepStrictEqual(inspect(parser('[]]{}')), undefined);
67
67
  assert.deepStrictEqual(inspect(parser('[a]{}')), undefined);
68
- assert.deepStrictEqual(inspect(parser('[ a]{b}')), undefined);
69
- assert.deepStrictEqual(inspect(parser('[ a ]{b}')), undefined);
70
68
  assert.deepStrictEqual(inspect(parser('[a\nb]{#}')), undefined);
71
69
  assert.deepStrictEqual(inspect(parser('[a\\\nb]{#}')), undefined);
72
70
  assert.deepStrictEqual(inspect(parser('[<wbr>]{/}')), undefined);
@@ -95,11 +93,13 @@ describe('Unit: parser/inline/link', () => {
95
93
  assert.deepStrictEqual(inspect(parser('[]{^/b}')), [[`<a href="/b">^/b</a>`], '']);
96
94
  assert.deepStrictEqual(inspect(parser('[]{#}')), [['<a href="#">#</a>'], '']);
97
95
  assert.deepStrictEqual(inspect(parser('[]{#b}')), [['<a href="#b">#b</a>'], '']);
96
+ assert.deepStrictEqual(inspect(parser('[ a]{b}')), [['<a href="b">a</a>'], '']);
97
+ assert.deepStrictEqual(inspect(parser('[ a ]{b}')), [['<a href="b">a</a>'], '']);
98
+ assert.deepStrictEqual(inspect(parser('[a ]{b}')), [['<a href="b">a</a>'], '']);
99
+ assert.deepStrictEqual(inspect(parser('[a ]{b}')), [['<a href="b">a</a>'], '']);
98
100
  assert.deepStrictEqual(inspect(parser('[a]{b}')), [['<a href="b">a</a>'], '']);
99
101
  assert.deepStrictEqual(inspect(parser('[a]{#}')), [['<a href="#">a</a>'], '']);
100
102
  assert.deepStrictEqual(inspect(parser('[a]{#b}')), [['<a href="#b">a</a>'], '']);
101
- assert.deepStrictEqual(inspect(parser('[a ]{b}')), [['<a href="b">a</a>'], '']);
102
- assert.deepStrictEqual(inspect(parser('[a ]{b}')), [['<a href="b">a</a>'], '']);
103
103
  assert.deepStrictEqual(inspect(parser('[a b]{c}')), [['<a href="c">a b</a>'], '']);
104
104
  assert.deepStrictEqual(inspect(parser(`[]{?#${encodeURIComponent(':/[]{}<>?#=& ')}}`)), [['<a href="?#%3A%2F%5B%5D%7B%7D%3C%3E%3F%23%3D%26%20">?#%3A%2F[]{}&lt;&gt;%3F%23%3D%26%20</a>'], '']);
105
105
  assert.deepStrictEqual(inspect(parser('{b}')), [['<a href="b">b</a>'], '']);
@@ -7,7 +7,7 @@ import { inline, media, shortmedia } from '../inline';
7
7
  import { attributes } from './html';
8
8
  import { autolink } from '../autolink';
9
9
  import { str } from '../source';
10
- import { startTight, trimEnd, stringify } from '../util';
10
+ import { startLoose, visible, trimNode, stringify } from '../util';
11
11
  import { html, define, defrag } from 'typed-dom';
12
12
  import { ReadonlyURL } from 'spica/url';
13
13
 
@@ -28,7 +28,7 @@ export const link: LinkParser = lazy(() => creator(10, bind(reverse(
28
28
  surround('[', shortmedia, ']'),
29
29
  surround(
30
30
  '[',
31
- startTight(
31
+ startLoose(visible(
32
32
  context({ syntax: { inline: {
33
33
  annotation: false,
34
34
  reference: false,
@@ -39,7 +39,7 @@ export const link: LinkParser = lazy(() => creator(10, bind(reverse(
39
39
  media: false,
40
40
  autolink: false,
41
41
  }}},
42
- some(inline, ']', /^\\?\n/))),
42
+ some(inline, ']', /^\\?\n/)))),
43
43
  ']',
44
44
  true),
45
45
  ]))),
@@ -54,7 +54,7 @@ export const link: LinkParser = lazy(() => creator(10, bind(reverse(
54
54
  assert(!INSECURE_URI.match(/\s/));
55
55
  const el = create(
56
56
  INSECURE_URI,
57
- trimEnd(defrag(content)),
57
+ trimNode(defrag(content)),
58
58
  new ReadonlyURL(
59
59
  resolve(INSECURE_URI, context.host || location, context.url || location),
60
60
  context.host?.href || location.href),
@@ -2,7 +2,7 @@ import { MarkParser } from '../inline';
2
2
  import { union, some, creator, surround, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { str } from '../source';
5
- import { startTight, verifyEndTight, trimEndBR } from '../util';
5
+ import { startTight, isEndTightNodes, trimEndBR } from '../util';
6
6
  import { html, defrag } from 'typed-dom';
7
7
  import { unshift } from 'spica/array';
8
8
 
@@ -11,7 +11,7 @@ export const mark: MarkParser = lazy(() => creator(surround(
11
11
  startTight(union([some(inline, '==')])),
12
12
  str('=='), false,
13
13
  ([as, bs, cs], rest) =>
14
- verifyEndTight(bs)
14
+ isEndTightNodes(bs)
15
15
  ? [[html('mark', defrag(trimEndBR(bs)))], rest]
16
16
  : [unshift(as, bs), cs[0] + rest],
17
17
  ([as, bs], rest) => [unshift(as, bs), rest])));
@@ -1,7 +1,7 @@
1
1
  import { MathParser } from '../inline';
2
2
  import { union, some, validate, verify, rewrite, creator, surround, lazy } from '../../combinator';
3
3
  import { escsource, str } from '../source';
4
- import { verifyEndTight } from '../util';
4
+ import { isEndTightNodes } from '../util';
5
5
  import { html } from 'typed-dom';
6
6
 
7
7
  const disallowedCommand = /\\(?:begin|tiny|huge|large)(?![0-9a-z])/i;
@@ -21,7 +21,7 @@ export const math: MathParser = lazy(() => creator(validate('$', '$', '\n', rewr
21
21
  // $[A-z]*[,.!?()] : Incomplete syntax before texts
22
22
  // $[A-z]*\s?[!@#&*+~=`$[]{<] : Incomplete syntax in or around another syntax
23
23
  str(/^(?=[\\^_[(|]|[A-Za-z][0-9A-Za-z]*'*[ ~]?(?:\$|([\\^_(|:=<>])(?!\1)))(?:\\\$|[\x20-\x23\x25-\x7E])*/),
24
- verifyEndTight),
24
+ isEndTightNodes),
25
25
  /^\$(?![0-9A-Za-z])/),
26
26
  ]),
27
27
  (source, { caches: { math: cache } = {} }) => [[
@@ -39,9 +39,8 @@ describe('Unit: parser/inline/media', () => {
39
39
  assert.deepStrictEqual(inspect(parser('![\\ ]{#}')), undefined);
40
40
  assert.deepStrictEqual(inspect(parser('![&Tab;]{#}')), undefined);
41
41
  assert.deepStrictEqual(inspect(parser('![a]{}')), undefined);
42
- assert.deepStrictEqual(inspect(parser('![ a]{#}')), undefined);
43
- assert.deepStrictEqual(inspect(parser('![ a ]{#}')), undefined);
44
42
  assert.deepStrictEqual(inspect(parser('![\\ a ]{#}')), undefined);
43
+ assert.deepStrictEqual(inspect(parser('![ \\ a ]{#}')), undefined);
45
44
  assert.deepStrictEqual(inspect(parser('![a\nb]{#}')), undefined);
46
45
  assert.deepStrictEqual(inspect(parser('![a\\\nb]{#}')), undefined);
47
46
  assert.deepStrictEqual(inspect(parser('![]{ttp://host}')), [['<img class="media invalid" data-src="ttp://host" alt="">'], '']);
@@ -63,6 +62,8 @@ describe('Unit: parser/inline/media', () => {
63
62
  assert.deepStrictEqual(inspect(parser('![]{\\b}')), [['<a href="\\b" target="_blank"><img class="media" data-src="\\b" alt=""></a>'], '']);
64
63
  assert.deepStrictEqual(inspect(parser('![]{./b}')), [['<a href="./b" target="_blank"><img class="media" data-src="./b" alt=""></a>'], '']);
65
64
  assert.deepStrictEqual(inspect(parser('![]{^/b}')), [[`<a href="/b" target="_blank"><img class="media" data-src="/b" alt=""></a>`], '']);
65
+ assert.deepStrictEqual(inspect(parser('![ a]{b}')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="a"></a>'], '']);
66
+ assert.deepStrictEqual(inspect(parser('![ a ]{b}')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="a"></a>'], '']);
66
67
  assert.deepStrictEqual(inspect(parser('![a ]{b}')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="a"></a>'], '']);
67
68
  assert.deepStrictEqual(inspect(parser('![a ]{b}')), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="a"></a>'], '']);
68
69
  assert.deepStrictEqual(inspect(parser('![a b]{c}')), [['<a href="c" target="_blank"><img class="media" data-src="c" alt="a b"></a>'], '']);
@@ -6,7 +6,6 @@ import { link, uri, option as linkoption, resolve } from './link';
6
6
  import { attributes } from './html';
7
7
  import { htmlentity } from './htmlentity';
8
8
  import { txt, str } from '../source';
9
- import { verifyStartTight } from '../util';
10
9
  import { html, define } from 'typed-dom';
11
10
  import { ReadonlyURL } from 'spica/url';
12
11
  import { unshift, push, join } from 'spica/array';
@@ -24,11 +23,11 @@ export const media: MediaParser = lazy(() => creator(10, bind(verify(fmap(open(
24
23
  validate(['[', '{'], '}', '\n',
25
24
  guard(context => context.syntax?.inline?.media ?? true,
26
25
  tails([
27
- dup(surround(/^\[(?!\\?\s)/, some(union([htmlentity, bracket, txt]), ']', /^\\?\n/), ']', true)),
26
+ dup(surround(/^\[(?!\s*\\\s)/, some(union([htmlentity, bracket, txt]), ']', /^\\?\n/), ']', true)),
28
27
  dup(surround(/^{(?![{}])/, inits([uri, some(option)]), /^[^\S\n]?}/)),
29
28
  ])))),
30
- ([as, bs]) => bs ? [[join(as)], bs] : [[''], as]),
31
- ([[text]]) => verifyStartTight([text || '-'])),
29
+ ([as, bs]) => bs ? [[join(as).trim() || join(as)], bs] : [[''], as]),
30
+ ([[text]]) => text === '' || text.trim() !== ''),
32
31
  ([[text], params], rest, context) => {
33
32
  const INSECURE_URI = params.shift()!;
34
33
  assert(INSECURE_URI === INSECURE_URI.trim());
@@ -40,9 +39,9 @@ export const media: MediaParser = lazy(() => creator(10, bind(verify(fmap(open(
40
39
  const cached = cache?.has(url.href);
41
40
  const el = cache && cached
42
41
  ? cache.get(url.href)!.cloneNode(true)
43
- : html('img', { class: 'media', 'data-src': url.source, alt: text.trimEnd() });
42
+ : html('img', { class: 'media', 'data-src': url.source, alt: text });
44
43
  if (!cached && !sanitize(url, el)) return [[el], rest];
45
- cached && el.hasAttribute('alt') && el.setAttribute('alt', text.trimEnd());
44
+ cached && el.hasAttribute('alt') && el.setAttribute('alt', text);
46
45
  define(el, attributes('media', push([], el.classList), optspec, params));
47
46
  assert(el.matches('img') || !el.matches('.invalid'));
48
47
  if (context.syntax?.inline?.link === false || cached && el.tagName !== 'IMG') return [[el], rest];
@@ -14,8 +14,6 @@ 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]]')), undefined);
18
- assert.deepStrictEqual(inspect(parser('[[ a ]]')), undefined);
19
17
  assert.deepStrictEqual(inspect(parser('[[\n]]')), undefined);
20
18
  assert.deepStrictEqual(inspect(parser('[[\na]]')), undefined);
21
19
  assert.deepStrictEqual(inspect(parser('[[\\ a]]')), undefined);
@@ -34,6 +32,8 @@ describe('Unit: parser/inline/reference', () => {
34
32
  });
35
33
 
36
34
  it('basic', () => {
35
+ assert.deepStrictEqual(inspect(parser('[[ a]]')), [['<sup class="reference">a</sup>'], '']);
36
+ assert.deepStrictEqual(inspect(parser('[[ a ]]')), [['<sup class="reference">a</sup>'], '']);
37
37
  assert.deepStrictEqual(inspect(parser('[[a]]')), [['<sup class="reference">a</sup>'], '']);
38
38
  assert.deepStrictEqual(inspect(parser('[[a ]]')), [['<sup class="reference">a</sup>'], '']);
39
39
  assert.deepStrictEqual(inspect(parser('[[a ]]')), [['<sup class="reference">a</sup>'], '']);
@@ -3,13 +3,13 @@ import { ReferenceParser } from '../inline';
3
3
  import { union, subsequence, some, validate, verify, focus, guard, context, creator, surround, lazy, fmap } from '../../combinator';
4
4
  import { inline } from '../inline';
5
5
  import { str } from '../source';
6
- import { startTight, isStartTight, trimEnd, stringify } from '../util';
6
+ import { startLoose, isStartTight, visible, trimNode, stringify } from '../util';
7
7
  import { html, defrag } from 'typed-dom';
8
8
 
9
9
  export const reference: ReferenceParser = lazy(() => creator(validate('[[', ']]', '\n', fmap(surround(
10
10
  '[[',
11
11
  guard(context => context.syntax?.inline?.reference ?? true,
12
- startTight(
12
+ startLoose(visible(
13
13
  context({ syntax: { inline: {
14
14
  annotation: false,
15
15
  reference: false,
@@ -24,9 +24,9 @@ export const reference: ReferenceParser = lazy(() => creator(validate('[[', ']]'
24
24
  abbr,
25
25
  focus('^', c => [['', c], '']),
26
26
  some(inline, ']', /^\\?\n/),
27
- ])))),
27
+ ]))))),
28
28
  ']]'),
29
- ns => [html('sup', attributes(ns), trimEnd(defrag(ns)))]))));
29
+ ns => [html('sup', attributes(ns), trimNode(defrag(ns)))]))));
30
30
 
31
31
  const abbr: ReferenceParser.AbbrParser = creator(fmap(verify(surround(
32
32
  '^',
@@ -4,7 +4,7 @@ import { eval, exec } from '../../combinator/data/parser';
4
4
  import { sequence, validate, verify, focus, creator, surround, lazy, bind } from '../../combinator';
5
5
  import { htmlentity } from './htmlentity';
6
6
  import { text as txt } from '../source';
7
- import { verifyStartTight } from '../util';
7
+ import { isStartTightNodes } from '../util';
8
8
  import { html, defrag } from 'typed-dom';
9
9
  import { unshift, push, join } from 'spica/array';
10
10
 
@@ -14,7 +14,7 @@ export const ruby: RubyParser = lazy(() => creator(bind(verify(
14
14
  surround('[', focus(/^(?:\\[^\n]|[^\[\]\n])+(?=]\()/, text), ']'),
15
15
  surround('(', focus(/^(?:\\[^\n]|[^\(\)\n])+(?=\))/, text), ')'),
16
16
  ])),
17
- ([texts]) => verifyStartTight(texts)),
17
+ ([texts]) => isStartTightNodes(texts)),
18
18
  ([texts, rubies], rest) => {
19
19
  const tail = typeof texts[texts.length - 1] === 'object'
20
20
  ? [texts.pop()!]
@@ -3,7 +3,7 @@ import { union, some, creator, surround, close, lazy } from '../../combinator';
3
3
  import { inline } from '../inline';
4
4
  import { emphasis } from './emphasis';
5
5
  import { str } from '../source';
6
- import { startTight, verifyEndTight, trimEndBR } from '../util';
6
+ import { startTight, isEndTightNodes, trimEndBR } from '../util';
7
7
  import { html, defrag } from 'typed-dom';
8
8
  import { unshift } from 'spica/array';
9
9
 
@@ -12,7 +12,7 @@ export const strong: StrongParser = lazy(() => creator(surround(close(
12
12
  startTight(some(union([emphasis, some(inline, '*'), str('*')]), '**')),
13
13
  str('**'), false,
14
14
  ([as, bs, cs], rest) =>
15
- verifyEndTight(bs)
15
+ isEndTightNodes(bs)
16
16
  ? [[html('strong', defrag(trimEndBR(bs)))], rest]
17
17
  : [unshift(as, bs), cs[0] + rest],
18
18
  ([as, bs], rest) => [unshift(as, bs), rest])));
@@ -70,6 +70,17 @@ function hasVisible(
70
70
  return false;
71
71
  }
72
72
 
73
+ export function startLoose<P extends Parser<HTMLElement | string>>(parser: P): P;
74
+ export function startLoose<T extends HTMLElement | string>(parser: Parser<T>): Parser<T> {
75
+ return (source, context) =>
76
+ isStartLoose(source, context)
77
+ ? parser(source, context)
78
+ : undefined;
79
+ }
80
+ function isStartLoose(source: string, context: MarkdownParser.Context): boolean {
81
+ if (source === '') return true;
82
+ return isStartTight(source.replace(/^[^\S\n]+/, ''), context);
83
+ }
73
84
  export function startTight<P extends Parser<unknown>>(parser: P): P;
74
85
  export function startTight<T>(parser: Parser<T>): Parser<T> {
75
86
  return (source, context) =>
@@ -116,11 +127,11 @@ export function isStartTight(source: string, context: MarkdownParser.Context): b
116
127
  return source[0].trimStart() !== '';
117
128
  }
118
129
  }
119
- export function verifyStartTight(nodes: readonly (HTMLElement | string)[]): boolean {
130
+ export function isStartTightNodes(nodes: readonly (HTMLElement | string)[]): boolean {
120
131
  if (nodes.length === 0) return true;
121
132
  return isVisible(nodes[0], 0);
122
133
  }
123
- export function verifyEndTight(nodes: readonly (HTMLElement | string)[]): boolean {
134
+ export function isEndTightNodes(nodes: readonly (HTMLElement | string)[]): boolean {
124
135
  if (nodes.length === 0) return true;
125
136
  const last = nodes.length - 1;
126
137
  return typeof nodes[last] === 'string' && (nodes[last] as string).length > 1
@@ -129,12 +140,23 @@ export function verifyEndTight(nodes: readonly (HTMLElement | string)[]): boolea
129
140
  : isVisible(nodes[last], -1) || last === 0 ||
130
141
  isVisible(nodes[last - 1], -1);
131
142
  }
132
- function isVisible(node: HTMLElement | string, position: number): boolean {
143
+ export function visible<P extends Parser<HTMLElement | string>>(parser: P): P;
144
+ export function visible<T extends HTMLElement | string>(parser: Parser<T>): Parser<T> {
145
+ return verify(parser, nodes => {
146
+ if (nodes.length === 0) return true;
147
+ for (let i = 0; i < nodes.length; ++i) {
148
+ if (isVisible(nodes[i])) return true;
149
+ }
150
+ return false;
151
+ });
152
+ }
153
+ function isVisible(node: HTMLElement | string, position?: number): boolean {
133
154
  if (!node) return false;
134
155
  switch (typeof node) {
135
156
  case 'string':
136
- assert(node.length + position >= 0);
137
- const char = node[position >= 0 ? position : node.length + position];
157
+ const char = position === undefined
158
+ ? node
159
+ : node[position >= 0 ? position : node.length + position];
138
160
  assert(char);
139
161
  switch (char) {
140
162
  case ' ':
@@ -159,8 +181,34 @@ function isVisible(node: HTMLElement | string, position: number): boolean {
159
181
  }
160
182
  }
161
183
 
162
- export function trimEnd(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
163
- assert(verifyStartTight(nodes));
184
+ export function trimNode(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
185
+ return trimNodeStart(trimNodeEnd(nodes));
186
+ }
187
+ function trimNodeStart(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
188
+ const skip = nodes.length > 0 &&
189
+ typeof nodes[nodes.length - 1] === 'object' &&
190
+ nodes[nodes.length - 1]['className'] === 'indexer'
191
+ ? [nodes.pop()!]
192
+ : [];
193
+ for (
194
+ let first = nodes[0];
195
+ nodes.length > 0 &&
196
+ !isVisible(first, 0) &&
197
+ !(typeof first === 'object' && first.className === 'comment');
198
+ ) {
199
+ assert(nodes.length > 0);
200
+ if (typeof first === 'string') {
201
+ const pos = first.length - first.trimStart().length;
202
+ if (pos > 0) {
203
+ nodes[0] = first.slice(pos);
204
+ break;
205
+ }
206
+ }
207
+ nodes.pop();
208
+ }
209
+ return push(nodes, skip);
210
+ }
211
+ export function trimNodeEnd(nodes: (HTMLElement | string)[]): (HTMLElement | string)[] {
164
212
  const skip = nodes.length > 0 &&
165
213
  typeof nodes[nodes.length - 1] === 'object' &&
166
214
  nodes[nodes.length - 1]['className'] === 'indexer'