securemark 0.281.0 → 0.281.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.281.2
4
+
5
+ - Fix identifiers.
6
+
7
+ ## 0.281.1
8
+
9
+ - Fix identifiers.
10
+
3
11
  ## 0.281.0
4
12
 
5
13
  - Change identifiers.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! securemark v0.281.0 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
1
+ /*! securemark v0.281.2 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
2
2
  (function webpackUniversalModuleDefinition(root, factory) {
3
3
  if(typeof exports === 'object' && typeof module === 'object')
4
4
  module.exports = factory(require("Prism"), require("DOMPurify"));
@@ -4437,7 +4437,7 @@ exports.aside = (0, combinator_1.block)((0, combinator_1.validate)('~~~', (0, co
4437
4437
  'data-invalid-message': 'Missing the title at the first line'
4438
4438
  }, `${opener}${body}${closer}`)];
4439
4439
  return [(0, dom_1.html)('aside', {
4440
- id: (0, indexee_1.identity)(context.id, (0, indexee_1.index)(heading)),
4440
+ id: (0, indexee_1.identity)('index', context.id, heading),
4441
4441
  class: 'aside'
4442
4442
  }, [document, (0, dom_1.html)('h2', 'References'), references])];
4443
4443
  })));
@@ -5199,7 +5199,7 @@ exports.olist = (0, combinator_1.lazy)(() => (0, combinator_1.block)((0, combina
5199
5199
  exports.olist_ = (0, combinator_1.lazy)(() => (0, combinator_1.block)((0, combinator_1.union)([(0, combinator_1.match)(openers['.'], (0, memoize_1.memoize)(ms => list(type(ms[1]), '.'), ms => idx(ms[1]), [])), (0, combinator_1.match)(openers['('], (0, memoize_1.memoize)(ms => list(type(ms[1]), '('), ms => idx(ms[1]), []))])));
5200
5200
  const list = (type, form) => (0, combinator_1.fmap)((0, combinator_1.some)((0, combinator_1.creation)(1, false, (0, combinator_1.union)([(0, inline_1.indexee)((0, combinator_1.fmap)((0, combinator_1.fallback)((0, combinator_1.inits)([(0, combinator_1.line)((0, combinator_1.open)(heads[form], (0, combinator_1.subsequence)([ulist_1.checkbox, (0, combinator_1.trim)((0, visibility_1.visualize)((0, util_1.lineable)((0, visibility_1.trimBlank)((0, combinator_1.some)((0, combinator_1.union)([inline_1.indexer, inline_1.inline]))))))]), true)), (0, combinator_1.indent)((0, combinator_1.union)([ulist_1.ulist_, exports.olist_, ilist_1.ilist_]))]), ulist_1.invalid), ns => [(0, dom_1.html)('li', {
5201
5201
  'data-marker': ns.shift() || undefined
5202
- }, (0, dom_1.defrag)((0, ulist_1.fillFirstLine)(ns)))]), true)]))), es => [format((0, dom_1.html)('ol', es), type, form)]);
5202
+ }, (0, dom_1.defrag)((0, ulist_1.fillFirstLine)(ns)))]))]))), es => [format((0, dom_1.html)('ol', es), type, form)]);
5203
5203
  const heads = {
5204
5204
  '.': (0, combinator_1.focus)(openers['.'], ({
5205
5205
  source
@@ -5523,7 +5523,7 @@ const visibility_1 = __webpack_require__(6364);
5523
5523
  const array_1 = __webpack_require__(6876);
5524
5524
  const dom_1 = __webpack_require__(394);
5525
5525
  exports.ulist = (0, combinator_1.lazy)(() => (0, combinator_1.block)((0, combinator_1.validate)(/^-(?=[^\S\n]|\n[^\S\n]*\S)/, exports.ulist_)));
5526
- exports.ulist_ = (0, combinator_1.lazy)(() => (0, combinator_1.block)((0, combinator_1.fmap)((0, combinator_1.validate)(/^-(?=$|\s)/, (0, combinator_1.some)((0, combinator_1.creation)(1, false, (0, combinator_1.union)([(0, inline_1.indexee)((0, combinator_1.fmap)((0, combinator_1.fallback)((0, combinator_1.inits)([(0, combinator_1.line)((0, combinator_1.open)(/^-(?:$|\s)/, (0, combinator_1.subsequence)([exports.checkbox, (0, combinator_1.trim)((0, visibility_1.visualize)((0, util_1.lineable)((0, visibility_1.trimBlank)((0, combinator_1.some)((0, combinator_1.union)([inline_1.indexer, inline_1.inline]))))))]), true)), (0, combinator_1.indent)((0, combinator_1.union)([exports.ulist_, olist_1.olist_, ilist_1.ilist_]))]), exports.invalid), ns => [(0, dom_1.html)('li', (0, dom_1.defrag)(fillFirstLine(ns)))]), true)])))), es => [format((0, dom_1.html)('ul', es))])));
5526
+ exports.ulist_ = (0, combinator_1.lazy)(() => (0, combinator_1.block)((0, combinator_1.fmap)((0, combinator_1.validate)(/^-(?=$|\s)/, (0, combinator_1.some)((0, combinator_1.creation)(1, false, (0, combinator_1.union)([(0, inline_1.indexee)((0, combinator_1.fmap)((0, combinator_1.fallback)((0, combinator_1.inits)([(0, combinator_1.line)((0, combinator_1.open)(/^-(?:$|\s)/, (0, combinator_1.subsequence)([exports.checkbox, (0, combinator_1.trim)((0, visibility_1.visualize)((0, util_1.lineable)((0, visibility_1.trimBlank)((0, combinator_1.some)((0, combinator_1.union)([inline_1.indexer, inline_1.inline]))))))]), true)), (0, combinator_1.indent)((0, combinator_1.union)([exports.ulist_, olist_1.olist_, ilist_1.ilist_]))]), exports.invalid), ns => [(0, dom_1.html)('li', (0, dom_1.defrag)(fillFirstLine(ns)))]))])))), es => [format((0, dom_1.html)('ul', es))])));
5527
5527
  exports.checkbox = (0, combinator_1.creation)(1, false, (0, combinator_1.focus)(/^\[[xX ]\](?=$|\s)/, ({
5528
5528
  source
5529
5529
  }) => [[(0, dom_1.html)('span', {
@@ -6057,7 +6057,7 @@ exports.index = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('[#', (0
6057
6057
  })]))));
6058
6058
  exports.signature = (0, combinator_1.lazy)(() => (0, combinator_1.validate)('|', (0, combinator_1.creation)((0, combinator_1.fmap)((0, combinator_1.open)('|', (0, visibility_1.startTight)((0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.txt]), ']'))), ns => [(0, dom_1.html)('span', {
6059
6059
  class: 'indexer',
6060
- 'data-index': (0, indexee_1.identity)(undefined, ns.join('')).slice(7)
6060
+ 'data-index': (0, indexee_1.identity)('index', undefined, ns.join('')).slice(7)
6061
6061
  })]))));
6062
6062
  const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.creation)((0, combinator_1.union)([(0, combinator_1.surround)((0, source_1.str)('('), (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.txt]), ')'), (0, source_1.str)(')'), true), (0, combinator_1.surround)((0, source_1.str)('['), (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.txt]), ']'), (0, source_1.str)(']'), true), (0, combinator_1.surround)((0, source_1.str)('{'), (0, combinator_1.some)((0, combinator_1.union)([bracket, source_1.txt]), '}'), (0, source_1.str)('}'), true), (0, combinator_1.surround)((0, source_1.str)('"'), (0, combinator_1.precedence)(3, (0, combinator_1.some)(source_1.txt, '"')), (0, source_1.str)('"'), true)])));
6063
6063
 
@@ -6072,15 +6072,15 @@ const bracket = (0, combinator_1.lazy)(() => (0, combinator_1.creation)((0, comb
6072
6072
  Object.defineProperty(exports, "__esModule", ({
6073
6073
  value: true
6074
6074
  }));
6075
- exports.text = exports.signature = exports.index = exports.identity = exports.indexee = void 0;
6075
+ exports.text = exports.signature = exports.identity = exports.indexee = void 0;
6076
6076
  const combinator_1 = __webpack_require__(3484);
6077
6077
  const memoize_1 = __webpack_require__(6925);
6078
6078
  const dom_1 = __webpack_require__(394);
6079
- function indexee(parser, optional) {
6079
+ function indexee(parser) {
6080
6080
  return (0, combinator_1.fmap)(parser, ([el], _, {
6081
6081
  id
6082
6082
  }) => [(0, dom_1.define)(el, {
6083
- id: identity(id, index(el, optional))
6083
+ id: identity('index', id, el)
6084
6084
  })]);
6085
6085
  }
6086
6086
  exports.indexee = indexee;
@@ -6088,42 +6088,35 @@ const MAX = 60;
6088
6088
  const ELLIPSIS = '...';
6089
6089
  const PART = (MAX - ELLIPSIS.length) / 2 | 0;
6090
6090
  const REM = MAX - PART * 2 - ELLIPSIS.length;
6091
- function identity(id, text, type = 'index') {
6091
+ function identity(type, id, text) {
6092
6092
  if (id === '') return undefined;
6093
+ if (typeof text !== 'string') {
6094
+ const index = text.querySelector(':scope > .indexer')?.getAttribute('data-index');
6095
+ if (index === '' && text.tagName === 'LI') return undefined;
6096
+ return index ? `${type}:${id ?? ''}:${index}` : identity(type, id, signature(text));
6097
+ }
6093
6098
  text = text.trim();
6094
6099
  if (text === '') return undefined;
6095
6100
  const str = text.replace(/\s/g, '_');
6096
- if (str.length <= MAX || type === '') return `${type}:${id ?? ''}:${str}`;
6097
6101
  const cs = [...str];
6098
- if (cs.length <= MAX) return `${type}:${id ?? ''}:${str}`;
6099
- switch (type) {
6100
- case 'index':
6101
- case 'mark':
6102
- const s1 = cs.slice(0, PART + REM).join('');
6103
- const s2 = cs.slice(-PART).join('');
6104
- return `${type}:${id ?? ''}:${s1}${ELLIPSIS}${s2}=${hash(text).toString(36)}`;
6102
+ if (type === '' || cs.length <= MAX) {
6103
+ return `${type}:${id ?? ''}:${str}${/_|[^\S ]/.test(text) ? `=${hash(text)}` : ''}`;
6105
6104
  }
6105
+ const s1 = cs.slice(0, PART + REM).join('');
6106
+ const s2 = cs.slice(-PART).join('');
6107
+ return `${type}:${id ?? ''}:${s1}${ELLIPSIS}${s2}=${hash(text)}`;
6106
6108
  }
6107
6109
  exports.identity = identity;
6108
6110
  function hash(source) {
6109
- let x = 1;
6111
+ let x = 0;
6110
6112
  for (let i = 0; i < source.length; ++i) {
6111
- x ^= x << 13;
6113
+ x ^= source.charCodeAt(i) << 1 | 1; // 16+1bit
6114
+ x ^= x << 13; // shift <= 32-17bit
6112
6115
  x ^= x >>> 17;
6113
6116
  x ^= x << 15;
6114
- x ^= source.charCodeAt(i) << 11;
6115
6117
  }
6116
- return x >>> 0;
6117
- }
6118
- function index(source, optional = false) {
6119
- if (!source.firstChild) return '';
6120
- const indexer = source.querySelector(':scope > .indexer');
6121
- const index = indexer?.getAttribute('data-index');
6122
- if (index) return index.replace(/=\w+$/, '');
6123
- if (index === '' && optional) return '';
6124
- return signature(source);
6118
+ return (x >>> 0).toString(36);
6125
6119
  }
6126
- exports.index = index;
6127
6120
  function signature(source) {
6128
6121
  const target = source.cloneNode(true);
6129
6122
  for (let es = target.querySelectorAll('code[data-src], .math[data-src], .label[data-label], .remark, rt, rp, br, .annotation, .reference, .checkbox, ul, ol'), len = es.length, i = 0; i < len; ++i) {
@@ -6540,7 +6533,7 @@ exports.mark = (0, combinator_1.lazy)(() => (0, combinator_1.creation)((0, combi
6540
6533
  }) => {
6541
6534
  const el = (0, dom_1.html)('mark', (0, dom_1.defrag)(bs));
6542
6535
  return [[(0, dom_1.define)(el, {
6543
- id: (0, indexee_1.identity)(id, (0, indexee_1.signature)(el), 'mark')
6536
+ id: (0, indexee_1.identity)('mark', id, (0, indexee_1.signature)(el))
6544
6537
  }), el.id && (0, dom_1.html)('a', {
6545
6538
  href: `#${el.id}`
6546
6539
  })], rest];
@@ -7055,7 +7048,7 @@ function build(syntax, marker, splitter = '') {
7055
7048
  const content = ref.firstElementChild;
7056
7049
  content.replaceWith(content.cloneNode());
7057
7050
  const abbr = ref.getAttribute('data-abbr') ?? '';
7058
- const identifier = abbr ? (0, indexee_1.identity)(undefined, abbr.match(/^(?:\S+ )+?(?:(?:January|February|March|April|May|June|August|September|October|November|December) \d{1,2}(?:-\d{0,2})?, \d{1,4}(?:-\d{0,4})?[a-z]?|n\.d\.)(?=,|$)/)?.[0] ?? abbr.match(/^[^,\s]+(?:,? [^,\s]+)*?(?: \d{1,4}(?:-\d{0,4})?[a-z]?(?=,|$)|(?=,(?: [a-z]+\.?)? [0-9]))/)?.[0] ?? abbr, '')?.slice(2) || '' : (0, indexee_1.identity)(undefined, (0, indexee_1.signature)(content), 'mark')?.slice(6) || '';
7051
+ const identifier = abbr ? (0, indexee_1.identity)('', undefined, abbr.match(/^(?:\S+ )+?(?:(?:January|February|March|April|May|June|August|September|October|November|December) \d{1,2}(?:-\d{0,2})?, \d{1,4}(?:-\d{0,4})?[a-z]?|n\.d\.)(?=,|$)/)?.[0] ?? abbr.match(/^[^,\s]+(?:,? [^,\s]+)*?(?: \d{1,4}(?:-\d{0,4})?[a-z]?(?=,|$)|(?=,(?: [a-z]+\.?)? [0-9]))/)?.[0] ?? abbr)?.slice(2) || '' : (0, indexee_1.identity)('mark', undefined, (0, indexee_1.signature)(content))?.slice(6) || '';
7059
7052
  return {
7060
7053
  content,
7061
7054
  identifier,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.281.0",
3
+ "version": "0.281.2",
4
4
  "description": "Secure markdown renderer working on browsers for user input data.",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/falsandtru/securemark",
@@ -1,6 +1,6 @@
1
1
  import { ExtensionParser } from '../../block';
2
2
  import { block, validate, fence, fmap } from '../../../combinator';
3
- import { identity, index } from '../../inline/extension/indexee';
3
+ import { identity } from '../../inline/extension/indexee';
4
4
  import { parse } from '../../api/parse';
5
5
  import { html } from 'typed-dom/dom';
6
6
 
@@ -34,9 +34,9 @@ export const aside: ExtensionParser.AsideParser = block(validate('~~~', fmap(
34
34
  'data-invalid-type': 'content',
35
35
  'data-invalid-message': 'Missing the title at the first line',
36
36
  }, `${opener}${body}${closer}`)];
37
- assert(identity(context.id, index(heading)));
37
+ assert(identity('index', context.id, heading));
38
38
  return [
39
- html('aside', { id: identity(context.id, index(heading)), class: 'aside' }, [
39
+ html('aside', { id: identity('index', context.id, heading), class: 'aside' }, [
40
40
  document,
41
41
  html('h2', 'References'),
42
42
  references,
@@ -39,7 +39,7 @@ const list = (type: string, form: string): OListParser.ListParser => fmap(
39
39
  indent(union([ulist_, olist_, ilist_])),
40
40
  ]),
41
41
  invalid),
42
- ns => [html('li', { 'data-marker': ns.shift() as string || undefined }, defrag(fillFirstLine(ns)))]), true),
42
+ ns => [html('li', { 'data-marker': ns.shift() as string || undefined }, defrag(fillFirstLine(ns)))])),
43
43
  ]))),
44
44
  es => [format(html('ol', es), type, form)]);
45
45
 
@@ -26,7 +26,7 @@ export const ulist_: UListParser = lazy(() => block(fmap(validate(
26
26
  indent(union([ulist_, olist_, ilist_])),
27
27
  ]),
28
28
  invalid),
29
- ns => [html('li', defrag(fillFirstLine(ns)))]), true),
29
+ ns => [html('li', defrag(fillFirstLine(ns)))])),
30
30
  ])))),
31
31
  es => [format(html('ul', es))])));
32
32
 
@@ -44,13 +44,15 @@ describe('Unit: parser/inline/extension/index', () => {
44
44
  assert.deepStrictEqual(inspect(parser('[#a]')), [['<a class="index" href="#index::a">a</a>'], '']);
45
45
  assert.deepStrictEqual(inspect(parser('[#a ]')), [['<a class="index" href="#index::a">a</a>'], '']);
46
46
  assert.deepStrictEqual(inspect(parser('[#a ]')), [['<a class="index" href="#index::a">a</a>'], '']);
47
- assert.deepStrictEqual(inspect(parser('[#a b]')), [['<a class="index" href="#index::a_b">a b</a>'], '']);
48
- assert.deepStrictEqual(inspect(parser('[#a b]')), [['<a class="index" href="#index::a__b">a b</a>'], '']);
49
47
  assert.deepStrictEqual(inspect(parser('[#a \\ ]')), [['<a class="index" href="#index::a">a</a>'], '']);
50
48
  assert.deepStrictEqual(inspect(parser('[#a &nbsp;]')), [['<a class="index" href="#index::a">a</a>'], '']);
51
49
  assert.deepStrictEqual(inspect(parser('[#a <wbr>]')), [['<a class="index" href="#index::a">a</a>'], '']);
52
50
  assert.deepStrictEqual(inspect(parser('[#a [% b %]]')), [['<a class="index" href="#index::a">a <span class="remark"><input type="checkbox"><span>[% b %]</span></span></a>'], '']);
53
51
  assert.deepStrictEqual(inspect(parser('[#a\\ ]')), [['<a class="index" href="#index::a">a</a>'], '']);
52
+ assert.deepStrictEqual(inspect(parser('[#a b]')), [['<a class="index" href="#index::a_b">a b</a>'], '']);
53
+ assert.deepStrictEqual(inspect(parser('[#a b]')), [['<a class="index" href="#index::a__b">a b</a>'], '']);
54
+ assert.deepStrictEqual(inspect(parser('[#a\tb]')), [['<a class="index" href="#index::a_b=1eu1tj4">a\tb</a>'], '']);
55
+ assert.deepStrictEqual(inspect(parser('[#a_b]')), [['<a class="index" href="#index::a_b=10dxc9b">a_b</a>'], '']);
54
56
  assert.deepStrictEqual(inspect(parser('[#a\\ b]')), [['<a class="index" href="#index::a_b">a b</a>'], '']);
55
57
  assert.deepStrictEqual(inspect(parser('[#[]]')), [['<a class="index" href="#index::[]">[]</a>'], '']);
56
58
  assert.deepStrictEqual(inspect(parser('[#\\]]')), [['<a class="index" href="#index::]">]</a>'], '']);
@@ -85,6 +87,8 @@ describe('Unit: parser/inline/extension/index', () => {
85
87
  assert.deepStrictEqual(inspect(parser('[#a|*b*]')), [['<a class="index" href="#index::*b*">a<span class="indexer" data-index="*b*"></span></a>'], '']);
86
88
  assert.deepStrictEqual(inspect(parser('[#a|b c]')), [['<a class="index" href="#index::b_c">a<span class="indexer" data-index="b_c"></span></a>'], '']);
87
89
  assert.deepStrictEqual(inspect(parser('[#a|b c]')), [['<a class="index" href="#index::b__c">a<span class="indexer" data-index="b__c"></span></a>'], '']);
90
+ assert.deepStrictEqual(inspect(parser('[#a|b\tc]')), [['<a class="index" href="#index::b_c=3p5wqt">a<span class="indexer" data-index="b_c=3p5wqt"></span></a>'], '']);
91
+ assert.deepStrictEqual(inspect(parser('[#a|b_c]')), [['<a class="index" href="#index::b_c=fvw9e2">a<span class="indexer" data-index="b_c=fvw9e2"></span></a>'], '']);
88
92
  assert.deepStrictEqual(inspect(parser('[#a|[]]')), [['<a class="index" href="#index::[]">a<span class="indexer" data-index="[]"></span></a>'], '']);
89
93
  assert.deepStrictEqual(inspect(parser('[#a|&copy;]')), [['<a class="index" href="#index::&amp;copy;">a<span class="indexer" data-index="&amp;copy;"></span></a>'], '']);
90
94
  assert.deepStrictEqual(inspect(parser('[#a |b]')), [['<a class="index" href="#index::b">a<span class="indexer" data-index="b"></span></a>'], '']);
@@ -34,7 +34,7 @@ export const signature: IndexParser.SignatureParser = lazy(() => validate('|', c
34
34
  '|',
35
35
  startTight(some(union([bracket, txt]), ']'))),
36
36
  ns => [
37
- html('span', { class: 'indexer', 'data-index': identity(undefined, ns.join(''))!.slice(7) }),
37
+ html('span', { class: 'indexer', 'data-index': identity('index', undefined, ns.join(''))!.slice(7) }),
38
38
  ]))));
39
39
 
40
40
  const bracket: IndexParser.SignatureParser.BracketParser = lazy(() => creation(union([
@@ -4,9 +4,9 @@ import { fmap } from '../../../combinator';
4
4
  import { reduce } from 'spica/memoize';
5
5
  import { define } from 'typed-dom/dom';
6
6
 
7
- export function indexee<P extends Parser<unknown, MarkdownParser.Context>>(parser: P, optional?: boolean): P;
8
- export function indexee(parser: Parser<HTMLElement, MarkdownParser.Context>, optional?: boolean): Parser<HTMLElement> {
9
- return fmap(parser, ([el], _, { id }) => [define(el, { id: identity(id, index(el, optional)) })]);
7
+ export function indexee<P extends Parser<unknown, MarkdownParser.Context>>(parser: P): P;
8
+ export function indexee(parser: Parser<HTMLElement, MarkdownParser.Context>): Parser<HTMLElement> {
9
+ return fmap(parser, ([el], _, { id }) => [define(el, { id: identity('index', id, el) })]);
10
10
  }
11
11
 
12
12
  const MAX = 60;
@@ -14,65 +14,62 @@ const ELLIPSIS = '...';
14
14
  const PART = (MAX - ELLIPSIS.length) / 2 | 0;
15
15
  const REM = MAX - PART * 2 - ELLIPSIS.length;
16
16
  export function identity(
17
+ type: 'index' | 'mark' | '',
17
18
  id: string | undefined,
18
- text: string,
19
- type: 'index' | 'mark' | '' = 'index',
19
+ text: string | HTMLElement,
20
20
  ): string | undefined {
21
- assert(!id?.match(/[^0-9a-z/-]/i));
22
- assert(!text.includes('\n'));
21
+ assert(id?.match(/^[0-9a-z/-]*$/i) ?? true);
23
22
  if (id === '') return undefined;
23
+ if (typeof text !== 'string') {
24
+ const index = text.querySelector(':scope > .indexer')?.getAttribute('data-index');
25
+ if (index === '' && text.tagName === 'LI') return undefined;
26
+ return index
27
+ ? `${type}:${id ?? ''}:${index}`
28
+ : identity(type, id, signature(text));
29
+ }
24
30
  text = text.trim();
25
31
  if (text === '') return undefined;
26
32
  const str = text.replace(/\s/g, '_');
27
- if (str.length <= MAX || type === '') return `${type}:${id ?? ''}:${str}`;
28
33
  const cs = [...str];
29
- if (cs.length <= MAX) return `${type}:${id ?? ''}:${str}`;
30
- switch (type) {
31
- case 'index':
32
- case 'mark':
33
- const s1 = cs.slice(0, PART + REM).join('');
34
- const s2 = cs.slice(-PART).join('');
35
- return `${type}:${id ?? ''}:${s1}${ELLIPSIS}${s2}=${hash(text).toString(36)}`;
36
- }
37
- assert(false);
38
- }
39
- function hash(source: string): number {
40
- let x = 1;
41
- assert(x !== 0);
42
- for (let i = 0; i < source.length; ++i) {
43
- x ^= x << 13;
44
- x ^= x >>> 17;
45
- x ^= x << 15;
46
- x ^= source.charCodeAt(i) << 11;
34
+ if (type === '' || cs.length <= MAX) {
35
+ return `${type}:${id ?? ''}:${str}${/_|[^\S ]/.test(text) ? `=${hash(text)}` : ''}`;
47
36
  }
48
- return x >>> 0;
37
+ const s1 = cs.slice(0, PART + REM).join('');
38
+ const s2 = cs.slice(-PART).join('');
39
+ assert([...`${s1}${ELLIPSIS}${s2}`].length === MAX);
40
+ return `${type}:${id ?? ''}:${s1}${ELLIPSIS}${s2}=${hash(text)}`;
49
41
  }
50
42
  assert.deepStrictEqual(
51
- identity(undefined, `${'0'.repeat(MAX - 1)}1`)!.slice(7),
52
- `${'0'.repeat(MAX - 1)}1`);
43
+ identity('index', undefined, ' 0 '),
44
+ identity('index', undefined, ' 0 '.trim()));
45
+ assert.notDeepStrictEqual(
46
+ identity('index', undefined, '0 0'),
47
+ identity('index', undefined, '0_0'));
48
+ assert.notDeepStrictEqual(
49
+ identity('index', undefined, '0 0'),
50
+ identity('index', undefined, '0\t0'));
53
51
  assert.deepStrictEqual(
54
- identity(undefined, `0${'1'.repeat(MAX / 2)}${'2'.repeat(MAX / 2)}3`)!.slice(7),
55
- `0${'1'.repeat(PART + REM - 1)}${ELLIPSIS}${'2'.repeat(PART - 1)}3=x8ujbi`);
52
+ identity('index', undefined, `${'0'.repeat(MAX - 1)}1`)!.slice(7),
53
+ `${'0'.repeat(MAX - 1)}1`);
56
54
  assert.deepStrictEqual(
57
- identity(undefined, `0${'1'.repeat(MAX * 2)}${'2'.repeat(MAX * 2)}3`)!.slice(7),
58
- `0${'1'.repeat(PART + REM - 1)}${ELLIPSIS}${'2'.repeat(PART - 1)}3=1c1m3g9`);
55
+ identity('index', undefined, `0${'1'.repeat(MAX / 2)}${'2'.repeat(MAX / 2)}3`)!.slice(7),
56
+ `0${'1'.repeat(PART + REM - 1)}${ELLIPSIS}${'2'.repeat(PART - 1)}3=mhy513`);
59
57
  assert.deepStrictEqual(
60
- identity(undefined, ` ${'0 '.repeat(MAX)}`)!.slice(7),
61
- identity(undefined, ` ${'0 '.repeat(MAX)}`.trim())!.slice(7));
62
- assert.notDeepStrictEqual(
63
- identity(undefined, `${'0 '.repeat(MAX)}`)!.slice(7),
64
- identity(undefined, `${'0_'.repeat(MAX)}`)!.slice(7));
65
-
66
- export function index(source: Element, optional = false): string {
67
- assert(!source.matches('.indexer'));
68
- assert(source.querySelectorAll(':scope > .indexer').length <= 1);
69
- if (!source.firstChild) return '';
70
- const indexer = source.querySelector(':scope > .indexer');
71
- const index = indexer?.getAttribute('data-index');
72
- if (index) return index.replace(/=\w+$/, '');
73
- if (index === '' && optional) return '';
74
- return signature(source);
58
+ identity('index', undefined, `0${'1'.repeat(MAX * 2)}${'2'.repeat(MAX * 2)}3`)!.slice(7),
59
+ `0${'1'.repeat(PART + REM - 1)}${ELLIPSIS}${'2'.repeat(PART - 1)}3=12jqtiv`);
60
+ function hash(source: string): string {
61
+ let x = 0;
62
+ for (let i = 0; i < source.length; ++i) {
63
+ x ^= source.charCodeAt(i) << 1 | 1; // 16+1bit
64
+ x ^= x << 13; // shift <= 32-17bit
65
+ x ^= x >>> 17;
66
+ x ^= x << 15;
67
+ }
68
+ return (x >>> 0).toString(36);
75
69
  }
70
+ assert(hash('\x00') !== '0');
71
+ assert(hash('\x01') !== '0');
72
+ assert(hash('\x00') !== hash(String.fromCharCode(1 << 15)));
76
73
 
77
74
  export function signature(source: Element | DocumentFragment): string {
78
75
  assert(!navigator.userAgent.includes('Chrome') || !source.querySelector('br:not(:has(+ :is(ul, ol)))'));
@@ -25,6 +25,8 @@ describe('Unit: parser/inline/extension/indexer', () => {
25
25
  assert.deepStrictEqual(inspect(parser(' [|a ]')), [['<span class="indexer" data-index="a"></span>'], '']);
26
26
  assert.deepStrictEqual(inspect(parser(' [|a b]')), [['<span class="indexer" data-index="a_b"></span>'], '']);
27
27
  assert.deepStrictEqual(inspect(parser(' [|a b]')), [['<span class="indexer" data-index="a__b"></span>'], '']);
28
+ assert.deepStrictEqual(inspect(parser(' [|a\tb]')), [['<span class="indexer" data-index="a_b=1eu1tj4"></span>'], '']);
29
+ assert.deepStrictEqual(inspect(parser(' [|a_b]')), [['<span class="indexer" data-index="a_b=10dxc9b"></span>'], '']);
28
30
  assert.deepStrictEqual(inspect(parser(' [|A]')), [['<span class="indexer" data-index="A"></span>'], '']);
29
31
  assert.deepStrictEqual(inspect(parser(' [|*A*]')), [['<span class="indexer" data-index="*A*"></span>'], '']);
30
32
  assert.deepStrictEqual(inspect(parser(' [|`A`]')), [['<span class="indexer" data-index="`A`"></span>'], '']);
@@ -38,7 +38,7 @@ describe('Unit: parser/inline/mark', () => {
38
38
  it('nest', () => {
39
39
  assert.deepStrictEqual(inspect(parser('==a ==b====')), [['<mark id="mark::a_b">a <mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::a_b"></a>'], '']);
40
40
  assert.deepStrictEqual(inspect(parser('==a\\ ==b====')), [['<mark id="mark::a_b">a <mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::a_b"></a>'], '']);
41
- assert.deepStrictEqual(inspect(parser('==a&Tab;==b====')), [['<mark id="mark::a_b">a\t<mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::a_b"></a>'], '']);
41
+ assert.deepStrictEqual(inspect(parser('==a&Tab;==b====')), [['<mark id="mark::a_b=1eu1tj4">a\t<mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::a_b=1eu1tj4"></a>'], '']);
42
42
  assert.deepStrictEqual(inspect(parser('==a<wbr>==b====')), [['<mark id="mark::ab">a<wbr><mark id="mark::b">b</mark><a href="#mark::b"></a></mark>', '<a href="#mark::ab"></a>'], '']);
43
43
  assert.deepStrictEqual(inspect(parser('==*==a==*==')), [['<mark id="mark::a"><em><mark id="mark::a">a</mark><a href="#mark::a"></a></em></mark>', '<a href="#mark::a"></a>'], '']);
44
44
  });
@@ -20,7 +20,7 @@ export const mark: MarkParser = lazy(() => creation(surround(
20
20
  ([, bs], rest, { id }) => {
21
21
  const el = html('mark', defrag(bs));
22
22
  return [[
23
- define(el, { id: identity(id, signature(el), 'mark') }),
23
+ define(el, { id: identity('mark', id, signature(el)) }),
24
24
  el.id && html('a', { href: `#${el.id}` }),
25
25
  ], rest];
26
26
  },
@@ -39,14 +39,14 @@ function build(
39
39
  const abbr = ref.getAttribute('data-abbr') ?? '';
40
40
  const identifier = abbr
41
41
  ? identity(
42
+ '',
42
43
  undefined,
43
44
  (
44
45
  abbr.match(/^(?:\S+ )+?(?:(?:January|February|March|April|May|June|August|September|October|November|December) \d{1,2}(?:-\d{0,2})?, \d{1,4}(?:-\d{0,4})?[a-z]?|n\.d\.)(?=,|$)/)?.[0] ??
45
46
  abbr.match(/^[^,\s]+(?:,? [^,\s]+)*?(?: \d{1,4}(?:-\d{0,4})?[a-z]?(?=,|$)|(?=,(?: [a-z]+\.?)? [0-9]))/)?.[0] ??
46
47
  abbr
47
- ),
48
- '')?.slice(2) || ''
49
- : identity(undefined, signature(content), 'mark')?.slice(6) || '';
48
+ ))?.slice(2) || ''
49
+ : identity('mark', undefined, signature(content))?.slice(6) || '';
50
50
  return {
51
51
  content,
52
52
  identifier,