securemark 0.297.2 → 0.297.3

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.297.3
4
+
5
+ - Fix note processing.
6
+
3
7
  ## 0.297.2
4
8
 
5
9
  - Refactoring.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! securemark v0.297.2 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
1
+ /*! securemark v0.297.3 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"));
@@ -7129,7 +7129,7 @@ function baseR(n, r) {
7129
7129
  return acc;
7130
7130
  }
7131
7131
  function signature(target) {
7132
- for (let es = target.querySelectorAll('code[data-src], .math[data-src], .remark, rt, rp, br, .annotation, .reference, :is(.annotation, .reference) > a, .checkbox, ul, ol, .label[data-label]'), len = es.length, i = 0; i < len; ++i) {
7132
+ for (let es = target.querySelectorAll('code[data-src], .math[data-src], .remark, rt, rp, br, .annotation, .reference, :is(.annotation, .reference) > a, .checkbox, ul, ol, .label[data-label]'), i = es.length; i--;) {
7133
7133
  const el = es[i];
7134
7134
  switch (el.className) {
7135
7135
  case 'math':
@@ -7138,10 +7138,15 @@ function signature(target) {
7138
7138
  case 'label':
7139
7139
  el.replaceWith(`[$${el.getAttribute('data-label').replace('$', '')}]`);
7140
7140
  continue;
7141
- case 'checkbox':
7142
- case 'remark':
7143
7141
  case 'annotation':
7142
+ el.replaceWith(`((${el.textContent}))`);
7143
+ continue;
7144
7144
  case 'reference':
7145
+ const abbr = el.getAttribute('data-abbr');
7146
+ el.replaceWith(`[[${abbr ? `^${abbr}` : el.textContent}]]`);
7147
+ continue;
7148
+ case 'checkbox':
7149
+ case 'remark':
7145
7150
  el.remove();
7146
7151
  continue;
7147
7152
  }
@@ -7165,16 +7170,21 @@ function signature(target) {
7165
7170
  }
7166
7171
  exports.signature = signature;
7167
7172
  function text(target) {
7168
- for (let es = target.querySelectorAll('code[data-src], .math[data-src], .remark, rt, rp, br, .annotation, .reference, :is(.annotation, .reference) > a, .checkbox, ul, ol'), len = es.length, i = 0; i < len; ++i) {
7173
+ for (let es = target.querySelectorAll('code[data-src], .math[data-src], .remark, rt, rp, br, .annotation, .reference, :is(.annotation, .reference) > a, .checkbox, ul, ol'), i = es.length; i--;) {
7169
7174
  const el = es[i];
7170
7175
  switch (el.className) {
7171
7176
  case 'math':
7172
7177
  el.replaceWith(el.getAttribute('data-src'));
7173
7178
  continue;
7174
- case 'checkbox':
7175
- case 'remark':
7176
7179
  case 'annotation':
7180
+ el.replaceWith(`((${el.textContent}))`);
7181
+ continue;
7177
7182
  case 'reference':
7183
+ const abbr = el.getAttribute('data-abbr');
7184
+ el.replaceWith(`[[${abbr ? `^${abbr}` : el.textContent}]]`);
7185
+ continue;
7186
+ case 'checkbox':
7187
+ case 'remark':
7178
7188
  el.remove();
7179
7189
  continue;
7180
7190
  }
@@ -8271,25 +8281,33 @@ function capitalize(label) {
8271
8281
  Object.defineProperty(exports, "__esModule", ({
8272
8282
  value: true
8273
8283
  }));
8274
- exports.reference = exports.annotation = exports.note = void 0;
8284
+ exports.note = void 0;
8275
8285
  const indexee_1 = __webpack_require__(7610);
8276
8286
  const util_1 = __webpack_require__(4992);
8277
8287
  const memoize_1 = __webpack_require__(6925);
8278
8288
  const dom_1 = __webpack_require__(394);
8279
8289
  function* note(target, notes, opts = {}, bottom = null) {
8280
- yield* (0, exports.annotation)(target, notes?.annotations, opts, bottom);
8281
- yield* (0, exports.reference)(target, notes?.references, opts, bottom);
8290
+ const referenceRefMemory = referenceRefsMemoryCaller(target);
8291
+ const annotationRefMemory = annotationRefsMemoryCaller(target);
8292
+ for (const memory of [referenceRefMemory, annotationRefMemory]) {
8293
+ for (const [ref, {
8294
+ content
8295
+ }] of memory) {
8296
+ ref.replaceChildren(content);
8297
+ }
8298
+ memory.clear();
8299
+ }
8300
+ yield* reference(referenceRefMemory, target, notes?.references, opts, bottom);
8301
+ yield* annotation(annotationRefMemory, target, notes?.annotations, opts, bottom);
8282
8302
  }
8283
8303
  exports.note = note;
8284
- exports.annotation = build('annotation', 'annotations', n => `*${n}`, 'h1, h2, h3, h4, h5, h6, aside.aside, hr');
8285
- exports.reference = build('reference', 'references', (n, abbr) => `[${abbr || n}]`);
8286
- // Referenceを含むAnnotationの重複排除は両構文が互いに処理済みであることを必要とするため
8287
- // 構文ごとに各1回の処理では不可能
8288
- function build(syntax, plural, marker, splitter = '') {
8289
- splitter &&= `${splitter}, .${plural}`;
8290
- const refMemoryCaller = (0, memoize_1.memoize)(target => new Map() ?? target, new WeakMap());
8291
- return function* (target, note, opts = {}, bottom = null) {
8292
- const refMemory = refMemoryCaller(target);
8304
+ const annotationRefsMemoryCaller = (0, memoize_1.memoize)(target => new Map() ?? target, new WeakMap());
8305
+ const referenceRefsMemoryCaller = (0, memoize_1.memoize)(target => new Map() ?? target, new WeakMap());
8306
+ const annotation = build('annotation', 'annotations', '.annotation:not(:is(.annotations, .references) .annotation, .disabled)', n => `*${n}`, 'h1, h2, h3, h4, h5, h6, aside.aside, hr');
8307
+ const reference = build('reference', 'references', '.reference:not(:is(.annotations, .references) .reference, .disabled)', (n, abbr) => `[${abbr || n}]`);
8308
+ function build(syntax, list, query, marker, splitter = '') {
8309
+ splitter &&= `${splitter}, .${list}`;
8310
+ return function* (memory, target, note, opts = {}, bottom = null) {
8293
8311
  const refInfoCaller = (0, memoize_1.memoize)(ref => {
8294
8312
  const content = ref.firstElementChild;
8295
8313
  const abbr = ref.getAttribute('data-abbr') ?? '';
@@ -8302,15 +8320,9 @@ function build(syntax, plural, marker, splitter = '') {
8302
8320
  abbr,
8303
8321
  text: txt
8304
8322
  };
8305
- }, refMemory);
8306
- for (const [ref, {
8307
- content
8308
- }] of refMemory) {
8309
- ref.replaceChildren(content);
8310
- }
8311
- refMemory.clear();
8323
+ }, memory);
8312
8324
  const defs = new Map();
8313
- const refs = target.querySelectorAll(`.${syntax}:not(.disabled)`);
8325
+ const refs = target.querySelectorAll(query);
8314
8326
  const identifierInfoCaller = (0, memoize_1.memoize)(identifier => ({
8315
8327
  defIndex: 0,
8316
8328
  defSubindex: 0,
@@ -8326,18 +8338,19 @@ function build(syntax, plural, marker, splitter = '') {
8326
8338
  let refIndex = 0;
8327
8339
  for (let len = refs.length, i = 0; i < len; ++i) {
8328
8340
  const ref = refs[i];
8329
- if (splitter) for (let el; el = splitters[iSplitters], el?.compareDocumentPosition(ref) & Node.DOCUMENT_POSITION_FOLLOWING; ++iSplitters) {
8341
+ if (splitter) for (let splitter; splitter = splitters[iSplitters]; ++iSplitters) {
8342
+ const pos = splitter?.compareDocumentPosition(ref) ?? 0;
8343
+ if (pos & (Node.DOCUMENT_POSITION_PRECEDING | Node.DOCUMENT_POSITION_DISCONNECTED)) break;
8330
8344
  if (~iSplitters << 32 - 8 === 0) yield;
8331
- if (!scope && el.parentNode !== target) continue;
8332
- if (el.tagName === 'OL' && (el.nextElementSibling !== splitters[iSplitters + 1] || defs.size === 0)) {
8333
- el.remove();
8345
+ if (splitter.classList.contains(list) && defs.size === 0) {
8346
+ splitter.remove();
8334
8347
  continue;
8335
8348
  }
8336
8349
  if (defs.size > 0) {
8337
8350
  total += defs.size;
8338
- const note = el.tagName === 'OL' ? el : target.insertBefore((0, dom_1.html)('ol', {
8339
- class: plural
8340
- }), el);
8351
+ const note = splitter.classList.contains(list) ? splitter : target.insertBefore((0, dom_1.html)('ol', {
8352
+ class: list
8353
+ }), splitter);
8341
8354
  yield* proc(defs, note);
8342
8355
  }
8343
8356
  }
@@ -8399,17 +8412,15 @@ function build(syntax, plural, marker, splitter = '') {
8399
8412
  }, `^${++refIndex}`));
8400
8413
  }
8401
8414
  if (note || defs.size > 0) {
8402
- const el = splitters[iSplitters];
8403
- note ??= el?.tagName === 'OL' && el.nextElementSibling == splitters[iSplitters + 1] ? (++iSplitters, el) : target.insertBefore((0, dom_1.html)('ol', {
8404
- class: plural
8405
- }), splitters[iSplitters] ?? bottom);
8406
- yield* proc(defs, note);
8415
+ const splitter = splitters[iSplitters++];
8416
+ yield* proc(defs, note ?? (splitter?.classList.contains(list) ? splitter : target.insertBefore((0, dom_1.html)('ol', {
8417
+ class: list
8418
+ }), splitter ?? bottom)));
8407
8419
  }
8408
- if (splitter) for (let el; el = splitters[iSplitters]; ++iSplitters) {
8420
+ if (splitter) for (let splitter; splitter = splitters[iSplitters]; ++iSplitters) {
8409
8421
  if (~iSplitters << 32 - 8 === 0) yield;
8410
- if (!scope && el.parentNode !== target) continue;
8411
- if (el.tagName === 'OL') {
8412
- el.remove();
8422
+ if (splitter.classList.contains(list)) {
8423
+ splitter.remove();
8413
8424
  }
8414
8425
  }
8415
8426
  };
@@ -9345,7 +9356,7 @@ const math_1 = __webpack_require__(3165);
9345
9356
  const media_1 = __webpack_require__(3567);
9346
9357
  const memoize_1 = __webpack_require__(6925);
9347
9358
  const query_1 = __webpack_require__(2282);
9348
- const selector = 'img.media:not(.invalid):not([src])[data-src], a > :not(img).media:not(.invalid), pre.code:not(.invalid), .math:not(.invalid)';
9359
+ const selector = ':not(.invalid):is(.media:is(img:not([src])[data-src], a > :not(img).media), pre.code, .math)';
9349
9360
  const extend = (0, memoize_1.reduce)(opts => ({
9350
9361
  code: code_1.code,
9351
9362
  math: math_1.math,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.297.2",
3
+ "version": "0.297.3",
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",
@@ -216,12 +216,12 @@ describe('Unit: parser/api/parse', () => {
216
216
  [...parse('$-a\n$$\n$$\n\n(($-a[[^B]]))[[^B|$-a]]', { notes }).children].map(el => el.outerHTML),
217
217
  [
218
218
  '<figure data-type="math" data-label="$-a" data-group="$" data-number="1" id="label:$-a"><figcaption><span class="figindex">(1)</span><span class="figtext"></span></figcaption><div><div class="math" translate="no">$$\n$$</div></div></figure>',
219
- '<p><sup class="annotation" id="annotation::ref:[$-a]:1" title="(1)"><a href="#annotation::def:[$-a]:1">*1</a></sup><sup class="reference" data-abbr="B" id="reference::ref:B:1" title="(1)"><a href="#reference::def:B">[B]</a></sup></p>',
220
- '<ol class="annotations"><li id="annotation::def:[$-a]:1" data-marker="*1"><span><a class="label" data-label="$-a" href="#label:$-a">(1)</a><sup class="reference" data-abbr="B" id="reference::ref:B:2" title="(1)"><a href="#reference::def:B">[B]</a></sup></span><sup><a href="#annotation::ref:[$-a]:1">^1</a></sup></li></ol>',
219
+ '<p><sup class="annotation" id="annotation::ref:[$-a][[^B]]:1" title="(1)[[^B]]"><a href="#annotation::def:[$-a][[^B]]:1">*1</a></sup><sup class="reference" data-abbr="B" id="reference::ref:B:2" title="(1)"><a href="#reference::def:B">[B]</a></sup></p>',
220
+ '<ol class="annotations"><li id="annotation::def:[$-a][[^B]]:1" data-marker="*1"><span><a class="label" data-label="$-a" href="#label:$-a">(1)</a><sup class="reference" data-abbr="B" id="reference::ref:B:1" title="(1)"><a href="#reference::def:B">[B]</a></sup></span><sup><a href="#annotation::ref:[$-a][[^B]]:1">^1</a></sup></li></ol>',
221
221
  ]);
222
222
  assert.deepStrictEqual(
223
223
  notes.references.outerHTML,
224
- '<ol><li id="reference::def:B"><span><a class="label" data-label="$-a" href="#label:$-a">(1)</a></span><sup><a href="#reference::ref:B:1" title="(1)">^1</a><a href="#reference::ref:B:2">^2</a></sup></li></ol>');
224
+ '<ol><li id="reference::def:B"><span><a class="label" data-label="$-a" href="#label:$-a">(1)</a></span><sup><a href="#reference::ref:B:1">^1</a><a href="#reference::ref:B:2" title="(1)">^2</a></sup></li></ol>');
225
225
  assert.deepStrictEqual(
226
226
  [...parse([
227
227
  '[[^A 1|b]]',
@@ -110,7 +110,7 @@ export function signature(target: Element | DocumentFragment): string {
110
110
  assert(!target.parentNode);
111
111
  assert(!target.querySelector('br:not(:has(+ :is(ul, ol)))') || target.nodeName === 'MARK');
112
112
  for (let es = target.querySelectorAll('code[data-src], .math[data-src], .remark, rt, rp, br, .annotation, .reference, :is(.annotation, .reference) > a, .checkbox, ul, ol, .label[data-label]'),
113
- len = es.length, i = 0; i < len; ++i) {
113
+ i = es.length; i--;) {
114
114
  const el = es[i];
115
115
  switch (el.className) {
116
116
  case 'math':
@@ -119,10 +119,15 @@ export function signature(target: Element | DocumentFragment): string {
119
119
  case 'label':
120
120
  el.replaceWith(`[$${el.getAttribute('data-label')!.replace('$', '')}]`);
121
121
  continue;
122
- case 'checkbox':
123
- case 'remark':
124
122
  case 'annotation':
123
+ el.replaceWith(`((${el.textContent}))`);
124
+ continue;
125
125
  case 'reference':
126
+ const abbr = el.getAttribute('data-abbr');
127
+ el.replaceWith(`[[${abbr ? `^${abbr}` : el.textContent}]]`);
128
+ continue;
129
+ case 'checkbox':
130
+ case 'remark':
126
131
  el.remove();
127
132
  continue;
128
133
  }
@@ -149,16 +154,21 @@ export function text(target: Element | DocumentFragment): string {
149
154
  assert(!target.parentNode);
150
155
  assert(!target.querySelector('br:not(:has(+ :is(ul, ol)))'));
151
156
  for (let es = target.querySelectorAll('code[data-src], .math[data-src], .remark, rt, rp, br, .annotation, .reference, :is(.annotation, .reference) > a, .checkbox, ul, ol'),
152
- len = es.length, i = 0; i < len; ++i) {
157
+ i = es.length; i--;) {
153
158
  const el = es[i];
154
159
  switch (el.className) {
155
160
  case 'math':
156
161
  el.replaceWith(el.getAttribute('data-src')!);
157
162
  continue;
158
- case 'checkbox':
159
- case 'remark':
160
163
  case 'annotation':
164
+ el.replaceWith(`((${el.textContent}))`);
165
+ continue;
161
166
  case 'reference':
167
+ const abbr = el.getAttribute('data-abbr');
168
+ el.replaceWith(`[[${abbr ? `^${abbr}` : el.textContent}]]`);
169
+ continue;
170
+ case 'checkbox':
171
+ case 'remark':
162
172
  el.remove();
163
173
  continue;
164
174
  }
@@ -1,4 +1,4 @@
1
- import { note, annotation, reference } from './note';
1
+ import { note } from './note';
2
2
  import { parse as parse_ } from '../../parser';
3
3
  import { html } from 'typed-dom/dom';
4
4
 
@@ -8,7 +8,7 @@ describe('Unit: parser/processor/note', () => {
8
8
  describe('annotation', () => {
9
9
  it('empty', () => {
10
10
  const target = parse('');
11
- [...annotation(target)];
11
+ [...note(target)];
12
12
  assert.deepStrictEqual(
13
13
  [...target.children].map(el => el.outerHTML),
14
14
  []);
@@ -17,7 +17,7 @@ describe('Unit: parser/processor/note', () => {
17
17
  it('1', () => {
18
18
  const target = parse('((a b))');
19
19
  for (let i = 0; i < 3; ++i) {
20
- assert.deepStrictEqual([...annotation(target)].length, i === 0 ? 2 : 3);
20
+ assert.deepStrictEqual([...note(target)].length, i === 0 ? 2 : 3);
21
21
  assert.deepStrictEqual(
22
22
  [...target.children].map(el => el.outerHTML),
23
23
  [
@@ -39,7 +39,7 @@ describe('Unit: parser/processor/note', () => {
39
39
  it('2', () => {
40
40
  const target = parse('((1))((12345678901234567890))');
41
41
  for (let i = 0; i < 3; ++i) {
42
- assert.deepStrictEqual([...annotation(target)].length, i === 0 ? 4 : 6);
42
+ assert.deepStrictEqual([...note(target)].length, i === 0 ? 4 : 6);
43
43
  assert.deepStrictEqual(
44
44
  [...target.children].map(el => el.outerHTML),
45
45
  [
@@ -68,7 +68,7 @@ describe('Unit: parser/processor/note', () => {
68
68
  it('unify', () => {
69
69
  const target = parse('((1))((2))((3))((2))((4))');
70
70
  for (let i = 0; i < 3; ++i) {
71
- [...annotation(target)];
71
+ [...note(target)];
72
72
  assert.deepStrictEqual(
73
73
  [...target.children].map(el => el.outerHTML),
74
74
  [
@@ -121,7 +121,7 @@ describe('Unit: parser/processor/note', () => {
121
121
  '((4))',
122
122
  ].join('\n\n')).children);
123
123
  for (let i = 0; i < 3; ++i) {
124
- [...annotation(target)];
124
+ [...note(target)];
125
125
  assert.deepStrictEqual(
126
126
  [...target.children].map(el => el.outerHTML),
127
127
  [
@@ -202,7 +202,7 @@ describe('Unit: parser/processor/note', () => {
202
202
  it('id', () => {
203
203
  const target = parse('((a b))');
204
204
  for (let i = 0; i < 3; ++i) {
205
- assert.deepStrictEqual([...annotation(target, undefined, { id: '0' })].length, i === 0 ? 2 : 3);
205
+ assert.deepStrictEqual([...note(target, undefined, { id: '0' })].length, i === 0 ? 2 : 3);
206
206
  assert.deepStrictEqual(
207
207
  [...target.children].map(el => el.outerHTML),
208
208
  [
@@ -222,37 +222,39 @@ describe('Unit: parser/processor/note', () => {
222
222
  });
223
223
 
224
224
  it('nest', () => {
225
- const target = parse('((a((b((c))))))((a))((b))');
225
+ const target = parse('((a((b((c))))))((a))((b))((c))');
226
226
  for (let i = 0; i < 3; ++i) {
227
- [...annotation(target)];
227
+ [...note(target)];
228
228
  assert.deepStrictEqual(
229
229
  [...target.children].map(el => el.outerHTML),
230
230
  [
231
231
  html('p', [
232
+ html('sup', { class: 'annotation', id: 'annotation::ref:a((b((c)))):1', title: 'a((b((c))))' }, [
233
+ html('a', { href: '#annotation::def:a((b((c)))):1' }, '*1')
234
+ ]),
232
235
  html('sup', { class: 'annotation', id: 'annotation::ref:a:1', title: 'a' }, [
233
- html('a', { href: '#annotation::def:a:1' }, '*1')
236
+ html('a', { href: '#annotation::def:a:1' }, '*4')
234
237
  ]),
235
- html('sup', { class: 'annotation', id: 'annotation::ref:a:2', title: 'a' }, [
236
- html('a', { href: '#annotation::def:a:1' }, '*1')
238
+ html('sup', { class: 'annotation', id: 'annotation::ref:b:1', title: 'b' }, [
239
+ html('a', { href: '#annotation::def:b:1' }, '*5')
237
240
  ]),
238
- html('sup', { class: 'annotation', id: 'annotation::ref:b:2', title: 'b' }, [
239
- html('a', { href: '#annotation::def:b:1' }, '*2')
241
+ html('sup', { class: 'annotation', id: 'annotation::ref:c:2', title: 'c' }, [
242
+ html('a', { href: '#annotation::def:c:1' }, '*3')
240
243
  ]),
241
244
  ]).outerHTML,
242
245
  html('ol', { class: 'annotations' }, [
243
- html('li', { id: 'annotation::def:a:1', 'data-marker': '*1' }, [
246
+ html('li', { id: 'annotation::def:a((b((c)))):1', 'data-marker': '*1' }, [
244
247
  html('span', [
245
248
  'a',
246
- html('sup', { class: 'annotation', id: 'annotation::ref:b:1', title: 'b' }, [
247
- html('a', { href: '#annotation::def:b:1' }, '*2')
249
+ html('sup', { class: 'annotation', id: 'annotation::ref:b((c)):1', title: 'b((c))' }, [
250
+ html('a', { href: '#annotation::def:b((c)):1' }, '*2')
248
251
  ]),
249
252
  ]),
250
253
  html('sup', [
251
- html('a', { href: '#annotation::ref:a:1' }, '^1'),
252
- html('a', { href: '#annotation::ref:a:2' }, '^4'),
254
+ html('a', { href: '#annotation::ref:a((b((c)))):1' }, '^1'),
253
255
  ])
254
256
  ]),
255
- html('li', { id: 'annotation::def:b:1', 'data-marker': '*2' }, [
257
+ html('li', { id: 'annotation::def:b((c)):1', 'data-marker': '*2' }, [
256
258
  html('span', [
257
259
  'b',
258
260
  html('sup', { class: 'annotation', id: 'annotation::ref:c:1', title: 'c' }, [
@@ -260,8 +262,7 @@ describe('Unit: parser/processor/note', () => {
260
262
  ]),
261
263
  ]),
262
264
  html('sup', [
263
- html('a', { href: '#annotation::ref:b:1' }, '^2'),
264
- html('a', { href: '#annotation::ref:b:2' }, '^5'),
265
+ html('a', { href: '#annotation::ref:b((c)):1' }, '^2'),
265
266
  ])
266
267
  ]),
267
268
  html('li', { id: 'annotation::def:c:1', 'data-marker': '*3' }, [
@@ -270,6 +271,23 @@ describe('Unit: parser/processor/note', () => {
270
271
  ]),
271
272
  html('sup', [
272
273
  html('a', { href: '#annotation::ref:c:1' }, '^3'),
274
+ html('a', { href: '#annotation::ref:c:2' }, '^6'),
275
+ ])
276
+ ]),
277
+ html('li', { id: 'annotation::def:a:1', 'data-marker': '*4' }, [
278
+ html('span', [
279
+ 'a',
280
+ ]),
281
+ html('sup', [
282
+ html('a', { href: '#annotation::ref:a:1' }, '^4'),
283
+ ])
284
+ ]),
285
+ html('li', { id: 'annotation::def:b:1', 'data-marker': '*5' }, [
286
+ html('span', [
287
+ 'b',
288
+ ]),
289
+ html('sup', [
290
+ html('a', { href: '#annotation::ref:b:1' }, '^5'),
273
291
  ])
274
292
  ]),
275
293
  ]).outerHTML,
@@ -282,9 +300,9 @@ describe('Unit: parser/processor/note', () => {
282
300
  describe('reference', () => {
283
301
  it('1', () => {
284
302
  const target = parse('[[a b]]');
285
- const note = html('ol');
303
+ const notes = { references: html('ol') };
286
304
  for (let i = 0; i < 3; ++i) {
287
- [...reference(target, note)];
305
+ [...note(target, notes)];
288
306
  assert.deepStrictEqual(
289
307
  [...target.children].map(el => el.outerHTML),
290
308
  [
@@ -295,7 +313,7 @@ describe('Unit: parser/processor/note', () => {
295
313
  ]).outerHTML,
296
314
  ]);
297
315
  assert.deepStrictEqual(
298
- [note.outerHTML],
316
+ [notes.references.outerHTML],
299
317
  [
300
318
  html('ol', [
301
319
  html('li', { id: 'reference::def:a_b' }, [
@@ -309,9 +327,9 @@ describe('Unit: parser/processor/note', () => {
309
327
 
310
328
  it('abbr', () => {
311
329
  const target = parse('[[^A 1]][[^A 1|b]][[^A 1]]');
312
- const note = html('ol');
330
+ const notes = { references: html('ol') };
313
331
  for (let i = 0; i < 3; ++i) {
314
- [...reference(target, note)];
332
+ [...note(target, notes)];
315
333
  assert.deepStrictEqual(
316
334
  [...target.children].map(el => el.outerHTML),
317
335
  [
@@ -328,7 +346,7 @@ describe('Unit: parser/processor/note', () => {
328
346
  ]).outerHTML,
329
347
  ]);
330
348
  assert.deepStrictEqual(
331
- [note.outerHTML],
349
+ [notes.references.outerHTML],
332
350
  [
333
351
  html('ol', [
334
352
  html('li', { id: 'reference::def:A_1' }, [
@@ -346,42 +364,41 @@ describe('Unit: parser/processor/note', () => {
346
364
 
347
365
  it('nest', () => {
348
366
  const target = parse('((a[[^B]]))[[^B|c]]');
349
- const note = html('ol');
367
+ const notes = { references: html('ol') };
350
368
  for (let i = 0; i < 3; ++i) {
351
- [...annotation(target)];
352
- [...reference(target, note)];
369
+ [...note(target, notes)];
353
370
  assert.deepStrictEqual(
354
371
  [...target.children].map(el => el.outerHTML),
355
372
  [
356
373
  html('p', [
357
- html('sup', { class: 'annotation', id: 'annotation::ref:a:1', title: 'a' }, [
358
- html('a', { href: '#annotation::def:a:1' }, '*1')
374
+ html('sup', { class: 'annotation', id: 'annotation::ref:a[[^B]]:1', title: 'a[[^B]]' }, [
375
+ html('a', { href: '#annotation::def:a[[^B]]:1' }, '*1')
359
376
  ]),
360
- html('sup', { class: 'reference', 'data-abbr': 'B', id: 'reference::ref:B:1', title: 'c' }, [
377
+ html('sup', { class: 'reference', 'data-abbr': 'B', id: 'reference::ref:B:2', title: 'c' }, [
361
378
  html('a', { href: '#reference::def:B' }, '[B]')
362
379
  ]),
363
380
  ]).outerHTML,
364
381
  html('ol', { class: 'annotations' }, [
365
- html('li', { id: 'annotation::def:a:1', 'data-marker': '*1' }, [
382
+ html('li', { id: 'annotation::def:a[[^B]]:1', 'data-marker': '*1' }, [
366
383
  html('span', [
367
384
  'a',
368
- html('sup', { class: 'reference', 'data-abbr': 'B', id: 'reference::ref:B:2', title: 'c' }, [
385
+ html('sup', { class: 'reference', 'data-abbr': 'B', id: 'reference::ref:B:1', title: 'c' }, [
369
386
  html('a', { href: '#reference::def:B' }, '[B]')
370
387
  ]),
371
388
  ]),
372
- html('sup', [html('a', { href: '#annotation::ref:a:1' }, '^1')])
389
+ html('sup', [html('a', { href: '#annotation::ref:a[[^B]]:1' }, '^1')])
373
390
  ]),
374
391
  ]).outerHTML,
375
392
  ]);
376
393
  assert.deepStrictEqual(
377
- [note.outerHTML],
394
+ [notes.references.outerHTML],
378
395
  [
379
396
  html('ol', [
380
397
  html('li', { id: 'reference::def:B' }, [
381
398
  html('span', 'c'),
382
399
  html('sup', [
383
- html('a', { href: '#reference::ref:B:1', title: 'c' }, '^1'),
384
- html('a', { href: '#reference::ref:B:2' }, '^2'),
400
+ html('a', { href: '#reference::ref:B:1' }, '^1'),
401
+ html('a', { href: '#reference::ref:B:2', title: 'c' }, '^2'),
385
402
  ]),
386
403
  ]),
387
404
  ]).outerHTML,
@@ -12,45 +12,66 @@ export function* note(
12
12
  opts: { readonly id?: string; } = {},
13
13
  bottom: Node | null = null,
14
14
  ): Generator<HTMLAnchorElement | HTMLLIElement | undefined, undefined, undefined> {
15
- yield* annotation(target, notes?.annotations, opts, bottom);
16
- yield* reference(target, notes?.references, opts, bottom);
15
+ const referenceRefMemory = referenceRefsMemoryCaller(target);
16
+ const annotationRefMemory = annotationRefsMemoryCaller(target);
17
+ for (const memory of [referenceRefMemory, annotationRefMemory]) {
18
+ for (const [ref, { content }] of memory) {
19
+ ref.replaceChildren(content);
20
+ }
21
+ memory.clear();
22
+ }
23
+ yield* reference(referenceRefMemory, target, notes?.references, opts, bottom);
24
+ yield* annotation(annotationRefMemory, target, notes?.annotations, opts, bottom);
25
+ }
26
+
27
+ interface RefMemory {
28
+ readonly content: Element;
29
+ readonly identifier: string;
30
+ readonly abbr: string;
31
+ readonly text: string;
17
32
  }
18
33
 
19
- export const annotation = build(
34
+ const annotationRefsMemoryCaller = memoize((target: Node) =>
35
+ new Map<HTMLElement, RefMemory>() ?? target,
36
+ new WeakMap());
37
+
38
+ const referenceRefsMemoryCaller = memoize((target: Node) =>
39
+ new Map<HTMLElement, {
40
+ readonly content: Element;
41
+ readonly identifier: string;
42
+ readonly abbr: string;
43
+ readonly text: string;
44
+ }>() ?? target,
45
+ new WeakMap());
46
+
47
+ const annotation = build(
20
48
  'annotation',
21
49
  'annotations',
50
+ '.annotation:not(:is(.annotations, .references) .annotation, .disabled)',
22
51
  n => `*${n}`,
23
52
  'h1, h2, h3, h4, h5, h6, aside.aside, hr');
24
- export const reference = build(
53
+ const reference = build(
25
54
  'reference',
26
55
  'references',
56
+ '.reference:not(:is(.annotations, .references) .reference, .disabled)',
27
57
  (n, abbr) => `[${abbr || n}]`);
28
58
 
29
- // Referenceを含むAnnotationの重複排除は両構文が互いに処理済みであることを必要とするため
30
- // 構文ごとに各1回の処理では不可能
31
59
  function build(
32
60
  syntax: string,
33
- plural: string,
61
+ list: string,
62
+ query: string,
34
63
  marker: (index: number, abbr: string) => string,
35
64
  splitter: string = '',
36
65
  ) {
37
66
  assert(syntax.match(/^[a-z]+$/));
38
- splitter &&= `${splitter}, .${plural}`;
39
- const refMemoryCaller = memoize((target: Node) =>
40
- new Map<HTMLElement, {
41
- readonly content: Element;
42
- readonly identifier: string;
43
- readonly abbr: string;
44
- readonly text: string;
45
- }>() ?? target,
46
- new WeakMap());
67
+ splitter &&= `${splitter}, .${list}`;
47
68
  return function* (
69
+ memory: Map<HTMLElement, RefMemory>,
48
70
  target: ParentNode & Node,
49
71
  note?: HTMLOListElement,
50
72
  opts: { readonly id?: string } = {},
51
73
  bottom: Node | null = null,
52
74
  ): Generator<HTMLAnchorElement | HTMLLIElement | undefined, undefined, undefined> {
53
- const refMemory = refMemoryCaller(target);
54
75
  const refInfoCaller = memoize((ref: HTMLElement) => {
55
76
  const content = ref.firstElementChild!;
56
77
  const abbr = ref.getAttribute('data-abbr') ?? '';
@@ -72,13 +93,9 @@ function build(
72
93
  abbr,
73
94
  text: txt,
74
95
  };
75
- }, refMemory);
76
- for (const [ref, { content }] of refMemory) {
77
- ref.replaceChildren(content);
78
- }
79
- refMemory.clear();
96
+ }, memory);
80
97
  const defs = new Map<string, HTMLLIElement>();
81
- const refs = target.querySelectorAll<HTMLElement>(`.${syntax}:not(.disabled)`);
98
+ const refs = target.querySelectorAll<HTMLElement>(query);
82
99
  const identifierInfoCaller = memoize((identifier: string) => ({
83
100
  defIndex: 0,
84
101
  defSubindex: 0,
@@ -94,23 +111,21 @@ function build(
94
111
  let refIndex = 0;
95
112
  for (let len = refs.length, i = 0; i < len; ++i) {
96
113
  const ref = refs[i];
97
- if (splitter) for (
98
- let el: Element;
99
- el = splitters[iSplitters],
100
- el?.compareDocumentPosition(ref) & Node.DOCUMENT_POSITION_FOLLOWING;
101
- ++iSplitters) {
114
+ if (splitter) for (let splitter; splitter = splitters[iSplitters]; ++iSplitters) {
115
+ const pos = splitter?.compareDocumentPosition(ref) ?? 0;
116
+ if (pos & (Node.DOCUMENT_POSITION_PRECEDING | Node.DOCUMENT_POSITION_DISCONNECTED)) break;
102
117
  if (~iSplitters << 32 - 8 === 0) yield;
103
- if (!scope && el.parentNode !== target) continue;
104
- if (el.tagName === 'OL' && (el.nextElementSibling !== splitters[iSplitters + 1] || defs.size === 0)) {
105
- assert(el.matches(`.${plural}`));
106
- el.remove();
118
+ if (splitter.classList.contains(list) && defs.size === 0) {
119
+ assert(splitter.matches(`.${list}`));
120
+ splitter.remove();
107
121
  continue;
108
122
  }
109
123
  if (defs.size > 0) {
110
124
  total += defs.size;
111
- const note = el.tagName === 'OL'
112
- ? el as HTMLOListElement
113
- : target.insertBefore(html('ol', { class: plural }), el);
125
+ assert(splitter.parentNode);
126
+ const note = splitter.classList.contains(list)
127
+ ? splitter as HTMLOListElement
128
+ : target.insertBefore(html('ol', { class: list }), splitter);
114
129
  assert(note.parentNode);
115
130
  yield* proc(defs, note);
116
131
  assert(defs.size === 0);
@@ -183,19 +198,16 @@ function build(
183
198
  `^${++refIndex}`));
184
199
  }
185
200
  if (note || defs.size > 0) {
186
- const el = splitters[iSplitters];
187
- note ??= el?.tagName === 'OL' && el.nextElementSibling == splitters[iSplitters + 1]
188
- ? (++iSplitters, el as HTMLOListElement)
189
- : target.insertBefore(html('ol', { class: plural }), splitters[iSplitters] ?? bottom);
190
- yield* proc(defs, note);
201
+ const splitter = splitters[iSplitters++];
202
+ yield* proc(defs, note ?? (splitter?.classList.contains(list)
203
+ ? splitter as HTMLOListElement
204
+ : target.insertBefore(html('ol', { class: list }), splitter ?? bottom)));
191
205
  assert(defs.size === 0);
192
206
  }
193
- if (splitter) for (let el: Element; el = splitters[iSplitters]; ++iSplitters) {
207
+ if (splitter) for (let splitter; splitter = splitters[iSplitters]; ++iSplitters) {
194
208
  if (~iSplitters << 32 - 8 === 0) yield;
195
- if (!scope && el.parentNode !== target) continue;
196
- if (el.tagName === 'OL') {
197
- assert(el.matches(`.${plural}`));
198
- el.remove();
209
+ if (splitter.classList.contains(list)) {
210
+ splitter.remove();
199
211
  }
200
212
  }
201
213
  }
@@ -5,7 +5,7 @@ import { media } from './render/media';
5
5
  import { reduce } from 'spica/memoize';
6
6
  import { querySelectorAllWith } from 'typed-dom/query';
7
7
 
8
- const selector = 'img.media:not(.invalid):not([src])[data-src], a > :not(img).media:not(.invalid), pre.code:not(.invalid), .math:not(.invalid)';
8
+ const selector = ':not(.invalid):is(.media:is(img:not([src])[data-src], a > :not(img).media), pre.code, .math)';
9
9
 
10
10
  const extend = reduce((opts: RenderingOptions): RenderingOptions =>
11
11
  ({ code, math, media: {}, ...opts }));