securemark 0.274.1 → 0.274.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,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.274.2
4
+
5
+ - Fix identifiers of notes.
6
+
3
7
  ## 0.274.1
4
8
 
5
9
  - Fix identifiers of notes.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! securemark v0.274.1 https://github.com/falsandtru/securemark | (c) 2017, falsandtru | UNLICENSED License */
1
+ /*! securemark v0.274.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"));
@@ -6645,6 +6645,7 @@ const link_1 = __webpack_require__(9628);
6645
6645
  const html_1 = __webpack_require__(5994);
6646
6646
  const htmlentity_1 = __webpack_require__(1562);
6647
6647
  const source_1 = __webpack_require__(6743);
6648
+ const util_1 = __webpack_require__(9437);
6648
6649
  const url_1 = __webpack_require__(2261);
6649
6650
  const array_1 = __webpack_require__(8112);
6650
6651
  const dom_1 = __webpack_require__(3252);
@@ -6692,32 +6693,19 @@ function sanitize(target, uri, alt) {
6692
6693
  case 'http:':
6693
6694
  case 'https:':
6694
6695
  if (/\/\.\.?(?:\/|$)/.test('/' + uri.source.slice(0, uri.source.search(/[?#]|$/)))) {
6695
- (0, dom_1.define)(target, {
6696
- class: void target.classList.add('invalid'),
6697
- 'data-invalid-syntax': 'media',
6698
- 'data-invalid-type': 'argument',
6699
- 'data-invalid-message': 'Dot-segments cannot be used in media paths; use subresource paths instead'
6700
- });
6696
+ (0, util_1.markInvalid)(target, 'media', 'argument', 'Dot-segments cannot be used in media paths; use subresource paths instead');
6701
6697
  return false;
6702
6698
  }
6703
6699
  break;
6704
6700
  default:
6705
- (0, dom_1.define)(target, {
6706
- class: void target.classList.add('invalid'),
6707
- 'data-invalid-syntax': 'media',
6708
- 'data-invalid-type': 'argument',
6709
- 'data-invalid-message': 'Invalid protocol'
6710
- });
6701
+ (0, util_1.markInvalid)(target, 'media', 'argument', 'Invalid protocol');
6711
6702
  return false;
6712
6703
  }
6713
6704
  if (alt.includes('\x1B')) {
6714
6705
  (0, dom_1.define)(target, {
6715
- class: void target.classList.add('invalid'),
6716
- 'data-invalid-syntax': 'media',
6717
- 'data-invalid-type': 'content',
6718
- 'data-invalid-message': `Cannot use invalid HTML entitiy "${alt.match(/&[0-9A-Za-z]+;/)[0]}"`,
6719
6706
  alt: target.getAttribute('alt')?.replace(/\x1B/g, '')
6720
6707
  });
6708
+ (0, util_1.markInvalid)(target, 'media', 'content', `Cannot use invalid HTML entitiy "${alt.match(/&[0-9A-Za-z]+;/)[0]}"`);
6721
6709
  return false;
6722
6710
  }
6723
6711
  return true;
@@ -6924,6 +6912,7 @@ Object.defineProperty(exports, "__esModule", ({
6924
6912
  }));
6925
6913
  exports.figure = void 0;
6926
6914
  const label_1 = __webpack_require__(466);
6915
+ const util_1 = __webpack_require__(9437);
6927
6916
  const queue_1 = __webpack_require__(4934);
6928
6917
  const array_1 = __webpack_require__(8112);
6929
6918
  const dom_1 = __webpack_require__(3252);
@@ -6946,11 +6935,8 @@ function* figure(target, notes, opts = {}) {
6946
6935
  const label = tagName === 'FIGURE' ? def.getAttribute('data-label') : `$-${increment(index, def)}`;
6947
6936
  if (label.endsWith('-')) continue;
6948
6937
  if (label.endsWith('-0')) {
6938
+ (0, util_1.markInvalid)(def, 'figure', 'argument', 'Invalid base index');
6949
6939
  (0, dom_1.define)(def, {
6950
- class: void def.classList.add('invalid'),
6951
- 'data-invalid-syntax': 'figure',
6952
- 'data-invalid-type': 'argument',
6953
- 'data-invalid-message': 'Invalid base index',
6954
6940
  hidden: null
6955
6941
  });
6956
6942
  continue;
@@ -6958,32 +6944,23 @@ function* figure(target, notes, opts = {}) {
6958
6944
  if (tagName === 'FIGURE' && label.endsWith('.0')) {
6959
6945
  // $-x.x.0 is disabled.
6960
6946
  if (label.lastIndexOf('.', label.length - 3) !== -1) {
6947
+ (0, util_1.markInvalid)(def, 'figure', 'argument', 'Base index must be $-x.0 format');
6961
6948
  (0, dom_1.define)(def, {
6962
- class: void def.classList.add('invalid'),
6963
- 'data-invalid-syntax': 'figure',
6964
- 'data-invalid-type': 'argument',
6965
- 'data-invalid-message': 'Base index must be $-x.0 format',
6966
6949
  hidden: null
6967
6950
  });
6968
6951
  continue;
6969
6952
  }
6970
6953
  // $-x.0 after h1-h6.
6971
6954
  if (!/^H[1-6]$/.test(def.previousElementSibling?.tagName ?? '')) {
6955
+ (0, util_1.markInvalid)(def, 'figure', 'position', messages.declaration);
6972
6956
  (0, dom_1.define)(def, {
6973
- class: void def.classList.add('invalid'),
6974
- 'data-invalid-syntax': 'figure',
6975
- 'data-invalid-type': 'position',
6976
- 'data-invalid-message': messages.declaration,
6977
6957
  hidden: null
6978
6958
  });
6979
6959
  continue;
6980
6960
  } else if (def.getAttribute('data-invalid-message') === messages.declaration) {
6961
+ (0, util_1.unmarkInvalid)(def);
6981
6962
  (0, dom_1.define)(def, {
6982
- class: void def.classList.remove('invalid'),
6983
- 'data-invalid-syntax': null,
6984
- 'data-invalid-type': null,
6985
- 'data-invalid-message': null,
6986
- hidden: ''
6963
+ hidden: null
6987
6964
  });
6988
6965
  }
6989
6966
  }
@@ -7008,31 +6985,18 @@ function* figure(target, notes, opts = {}) {
7008
6985
  if (labels.has(label)) {
7009
6986
  if (def.classList.contains('invalid')) continue;
7010
6987
  (0, dom_1.define)(def, {
7011
- id: null,
7012
- class: void def.classList.add('invalid'),
7013
- 'data-invalid-syntax': 'figure',
7014
- 'data-invalid-type': 'argument',
7015
- 'data-invalid-message': messages.duplicate
6988
+ id: null
7016
6989
  });
6990
+ (0, util_1.markInvalid)(def, 'figure', 'argument', messages.duplicate);
7017
6991
  continue;
7018
6992
  } else if (def.getAttribute('data-invalid-message') === messages.duplicate) {
7019
- (0, dom_1.define)(def, {
7020
- class: void def.classList.remove('invalid'),
7021
- 'data-invalid-syntax': null,
7022
- 'data-invalid-type': null,
7023
- 'data-invalid-message': null
7024
- });
6993
+ (0, util_1.unmarkInvalid)(def);
7025
6994
  }
7026
6995
  labels.add(label);
7027
6996
  opts.id !== '' && def.setAttribute('id', `label:${opts.id ? `${opts.id}:` : ''}${label}`);
7028
6997
  for (const ref of refs.take(label, Infinity)) {
7029
6998
  if (ref.getAttribute('data-invalid-message') === messages.reference) {
7030
- (0, dom_1.define)(ref, {
7031
- class: void ref.classList.remove('invalid'),
7032
- 'data-invalid-syntax': null,
7033
- 'data-invalid-type': null,
7034
- 'data-invalid-message': null
7035
- });
6999
+ (0, util_1.unmarkInvalid)(ref);
7036
7000
  }
7037
7001
  if (ref.hash.slice(1) === def.id && ref.innerText === figindex) continue;
7038
7002
  yield (0, dom_1.define)(ref, opts.id !== '' ? {
@@ -7044,12 +7008,7 @@ function* figure(target, notes, opts = {}) {
7044
7008
  }
7045
7009
  for (const [, ref] of refs) {
7046
7010
  if (opts.id !== '' && !ref.classList.contains('invalid')) {
7047
- (0, dom_1.define)(ref, {
7048
- class: void ref.classList.add('invalid'),
7049
- 'data-invalid-syntax': 'label',
7050
- 'data-invalid-type': 'reference',
7051
- 'data-invalid-message': messages.reference
7052
- });
7011
+ (0, util_1.markInvalid)(ref, 'label', 'reference', messages.reference);
7053
7012
  }
7054
7013
  yield ref;
7055
7014
  }
@@ -7091,6 +7050,7 @@ Object.defineProperty(exports, "__esModule", ({
7091
7050
  }));
7092
7051
  exports.reference = exports.annotation = exports.note = void 0;
7093
7052
  const indexee_1 = __webpack_require__(1269);
7053
+ const util_1 = __webpack_require__(9437);
7094
7054
  const dom_1 = __webpack_require__(3252);
7095
7055
  function* note(target, notes, opts = {}, bottom = null) {
7096
7056
  for (let es = target.querySelectorAll(`.annotations`), len = es.length, i = 0; i < len; ++i) {
@@ -7119,10 +7079,10 @@ function build(syntax, marker, splitter = '') {
7119
7079
  const titles = new Map();
7120
7080
  const defIndexes = new Map();
7121
7081
  const refSubindexes = new Map();
7122
- const defSubindexes = new Map();
7123
- let refIndex = 0;
7082
+ const defSubindexes = splitter ? new Map() : undefined;
7124
7083
  let total = 0;
7125
- let style;
7084
+ let format;
7085
+ let refIndex = 0;
7126
7086
  for (let len = refs.length, i = 0; i < len; ++i) {
7127
7087
  const ref = refs[i];
7128
7088
  if (ref.closest('[hidden]')) {
@@ -7146,41 +7106,31 @@ function build(syntax, marker, splitter = '') {
7146
7106
  refSubindexes.set(identifier, refSubindex);
7147
7107
  const refId = opts.id !== '' ? `${syntax}:${opts.id ?? ''}:ref:${identifier}:${refSubindex}` : undefined;
7148
7108
  const initial = splitter ? !defs.has(identifier) : refSubindex === 1;
7149
- const defSubindex = defSubindexes.get(identifier) + 1 || 1;
7150
- defSubindexes.set(identifier, defSubindex);
7109
+ const defSubindex = defSubindexes?.get(identifier) + +initial || 1;
7110
+ initial && defSubindexes?.set(identifier, defSubindex);
7111
+ const defId = opts.id !== '' ? `${syntax}:${opts.id ?? ''}:def:${identifier}${splitter && `:${defSubindex}`}` : undefined;
7151
7112
  const def = initial ? (0, dom_1.html)('li', {
7152
- id: opts.id !== '' ? `${syntax}:${opts.id ?? ''}:def:${identifier}:${defSubindex}` : undefined,
7153
- 'data-marker': !note ? marker(total + defs.size + 1, abbr) : undefined
7113
+ id: defId,
7114
+ 'data-marker': note ? undefined : marker(total + defs.size + 1, abbr)
7154
7115
  }, [(0, dom_1.define)(ref.firstElementChild.cloneNode(true), {
7155
7116
  hidden: null
7156
7117
  }), (0, dom_1.html)('sup')]) : defs.get(identifier);
7157
7118
  initial && defs.set(identifier, def);
7158
7119
  const defIndex = initial ? total + defs.size : defIndexes.get(def);
7159
7120
  initial && defIndexes.set(def, defIndex);
7160
- const defId = def.id || undefined;
7161
7121
  const title = initial ? (0, indexee_1.text)(ref.firstElementChild) : titles.get(identifier);
7162
7122
  initial && titles.set(identifier, title);
7163
- style ??= abbr ? 'abbr' : 'count';
7164
- if (style === 'count' ? abbr : !abbr) {
7165
- (0, dom_1.define)(ref, {
7166
- class: void ref.classList.add('invalid'),
7167
- 'data-invalid-syntax': syntax,
7168
- 'data-invalid-type': 'style',
7169
- 'data-invalid-message': `${syntax[0].toUpperCase() + syntax.slice(1)} style must be consistent`
7170
- });
7171
- } else if (ref.getAttribute('data-invalid-type') === 'style') {
7172
- (0, dom_1.define)(ref, {
7173
- class: void ref.classList.remove('invalid'),
7174
- 'data-invalid-syntax': null,
7175
- 'data-invalid-type': null,
7176
- 'data-invalid-message': null
7177
- });
7178
- }
7179
- if (!ref.firstElementChild.hasAttribute('hidden')) {
7180
- ref.firstElementChild.setAttribute('hidden', '');
7181
- } else {
7182
- ref.lastChild?.remove();
7123
+ format ??= abbr ? 'abbr' : 'number';
7124
+ if (!ref.classList.contains('invalid')) {
7125
+ if (format === 'number' ? abbr : !abbr) {
7126
+ (0, util_1.markInvalid)(ref, syntax, 'format', 'Notation format must be consistent with numbers or abbreviations');
7127
+ }
7128
+ } else switch (ref.getAttribute('data-invalid-syntax')) {
7129
+ case 'format':
7130
+ case 'content':
7131
+ (0, util_1.unmarkInvalid)(ref);
7183
7132
  }
7133
+ ref.firstElementChild.hasAttribute('hidden') ? ref.lastElementChild.remove() : ref.firstElementChild.setAttribute('hidden', '');
7184
7134
  (0, dom_1.define)(ref, {
7185
7135
  id: refId,
7186
7136
  class: opts.id !== '' ? undefined : void ref.classList.add('disabled'),
@@ -7195,12 +7145,12 @@ function build(syntax, marker, splitter = '') {
7195
7145
  yield ref.appendChild((0, dom_1.html)('a', {
7196
7146
  href: refId && defId && `#${defId}`
7197
7147
  }, marker(defIndex, abbr)));
7198
- def.lastChild.appendChild((0, dom_1.html)('a', {
7148
+ def.lastElementChild.appendChild((0, dom_1.html)('a', {
7199
7149
  href: refId && `#${refId}`,
7200
7150
  title: abbr && (0, indexee_1.text)((0, dom_1.frag)(ref.firstElementChild.cloneNode(true).childNodes)).trim() || undefined
7201
7151
  }, `^${++refIndex}`));
7202
7152
  }
7203
- if (defs.size > 0 || note) {
7153
+ if (note || defs.size > 0) {
7204
7154
  yield* proc(defs, note ?? target.insertBefore((0, dom_1.html)('ol', {
7205
7155
  class: `${syntax}s`
7206
7156
  }), splitters[0] ?? bottom));
@@ -7586,7 +7536,7 @@ exports.unescsource = (0, combinator_1.creation)(1, false, ({
7586
7536
  /***/ }),
7587
7537
 
7588
7538
  /***/ 9437:
7589
- /***/ ((__unused_webpack_module, exports) => {
7539
+ /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
7590
7540
 
7591
7541
  "use strict";
7592
7542
 
@@ -7594,7 +7544,26 @@ exports.unescsource = (0, combinator_1.creation)(1, false, ({
7594
7544
  Object.defineProperty(exports, "__esModule", ({
7595
7545
  value: true
7596
7546
  }));
7597
- exports.stringify = void 0;
7547
+ exports.stringify = exports.unmarkInvalid = exports.markInvalid = void 0;
7548
+ const dom_1 = __webpack_require__(3252);
7549
+ function markInvalid(el, syntax, type, message) {
7550
+ return (0, dom_1.define)(el, {
7551
+ class: void el.classList.add('invalid'),
7552
+ 'data-invalid-syntax': syntax,
7553
+ 'data-invalid-type': type,
7554
+ 'data-invalid-message': message
7555
+ });
7556
+ }
7557
+ exports.markInvalid = markInvalid;
7558
+ function unmarkInvalid(el) {
7559
+ return (0, dom_1.define)(el, {
7560
+ class: void el.classList.remove('invalid'),
7561
+ 'data-invalid-syntax': null,
7562
+ 'data-invalid-type': null,
7563
+ 'data-invalid-message': null
7564
+ });
7565
+ }
7566
+ exports.unmarkInvalid = unmarkInvalid;
7598
7567
  function stringify(nodes) {
7599
7568
  let acc = '';
7600
7569
  for (let i = 0; i < nodes.length; ++i) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securemark",
3
- "version": "0.274.1",
3
+ "version": "0.274.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",
@@ -194,34 +194,34 @@ describe('Unit: parser/api/bind', () => {
194
194
  html('p', [
195
195
  html('sup', { class: "reference", id: "reference::ref:1:1", title: "1" }, [
196
196
  html('span', { hidden: '' }, '1'),
197
- html('a', { href: "#reference::def:1:1" }, '[1]'),
197
+ html('a', { href: "#reference::def:1" }, '[1]'),
198
198
  ]),
199
199
  ]).outerHTML,
200
200
  html('p', [
201
201
  html('sup', { class: "reference", id: "reference::ref:2:1", title: "2" }, [
202
202
  html('span', { hidden: '' }, '2'),
203
- html('a', { href: "#reference::def:2:1" }, '[2]'),
203
+ html('a', { href: "#reference::def:2" }, '[2]'),
204
204
  ]),
205
205
  ]).outerHTML,
206
206
  html('p', [
207
207
  html('sup', { class: "reference", id: "reference::ref:3:1", title: "3" }, [
208
208
  html('span', { hidden: '' }, '3'),
209
- html('a', { href: "#reference::def:3:1" }, '[3]'),
209
+ html('a', { href: "#reference::def:3" }, '[3]'),
210
210
  ]),
211
211
  ]).outerHTML,
212
212
  ]);
213
213
  assert.deepStrictEqual(
214
214
  cfgs.notes.references?.outerHTML,
215
215
  html('ol', [
216
- html('li', { id: 'reference::def:1:1' }, [
216
+ html('li', { id: 'reference::def:1' }, [
217
217
  html('span', '1'),
218
218
  html('sup', [html('a', { href: '#reference::ref:1:1' }, '^1')]),
219
219
  ]),
220
- html('li', { id: 'reference::def:2:1' }, [
220
+ html('li', { id: 'reference::def:2' }, [
221
221
  html('span', '2'),
222
222
  html('sup', [html('a', { href: '#reference::ref:2:1' }, '^2')]),
223
223
  ]),
224
- html('li', { id: 'reference::def:3:1' }, [
224
+ html('li', { id: 'reference::def:3' }, [
225
225
  html('span', '3'),
226
226
  html('sup', [html('a', { href: '#reference::ref:3:1' }, '^3')]),
227
227
  ]),
@@ -207,12 +207,12 @@ describe('Unit: parser/api/parse', () => {
207
207
  [...parse('$-a\n$$\n$$\n\n(($-a[[^b]]))[[^b|$-a]]', { notes }).children].map(el => el.outerHTML),
208
208
  [
209
209
  '<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>',
210
- '<p><sup class="annotation" id="annotation::ref:(1):1" title="(1)"><span hidden=""><a class="label" data-label="$-a" href="#label:$-a">(1)</a><sup class="reference" data-abbr="b"><span></span></sup></span><a href="#annotation::def:(1):1">*1</a></sup><sup class="reference" data-abbr="b" id="reference::ref:b:1" title="(1)"><span hidden=""><a class="label" data-label="$-a" href="#label:$-a">(1)</a></span><a href="#reference::def:b:1">[b]</a></sup></p>',
211
- '<ol class="annotations"><li id="annotation::def:(1):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)"><span hidden=""></span><a href="#reference::def:b:1">[b]</a></sup></span><sup><a href="#annotation::ref:(1):1">^1</a></sup></li></ol>',
210
+ '<p><sup class="annotation" id="annotation::ref:(1):1" title="(1)"><span hidden=""><a class="label" data-label="$-a" href="#label:$-a">(1)</a><sup class="reference" data-abbr="b"><span></span></sup></span><a href="#annotation::def:(1):1">*1</a></sup><sup class="reference" data-abbr="b" id="reference::ref:b:1" title="(1)"><span hidden=""><a class="label" data-label="$-a" href="#label:$-a">(1)</a></span><a href="#reference::def:b">[b]</a></sup></p>',
211
+ '<ol class="annotations"><li id="annotation::def:(1):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)"><span hidden=""></span><a href="#reference::def:b">[b]</a></sup></span><sup><a href="#annotation::ref:(1):1">^1</a></sup></li></ol>',
212
212
  ]);
213
213
  assert.deepStrictEqual(
214
214
  notes.references.outerHTML,
215
- '<ol><li id="reference::def:b:1"><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>');
215
+ '<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>');
216
216
  });
217
217
 
218
218
  it('normalize', () => {
@@ -5,6 +5,7 @@ import { attributes } from './html';
5
5
  import { unsafehtmlentity } from './htmlentity';
6
6
  import { txt, linebreak, str } from '../source';
7
7
  import { Syntax, State } from '../context';
8
+ import { markInvalid } from '../util';
8
9
  import { ReadonlyURL } from 'spica/url';
9
10
  import { unshift, push } from 'spica/array';
10
11
  import { html, define } from 'typed-dom/dom';
@@ -87,32 +88,19 @@ function sanitize(target: HTMLElement, uri: ReadonlyURL, alt: string): boolean {
87
88
  case 'https:':
88
89
  assert(uri.host);
89
90
  if (/\/\.\.?(?:\/|$)/.test('/' + uri.source.slice(0, uri.source.search(/[?#]|$/)))) {
90
- define(target, {
91
- class: void target.classList.add('invalid'),
92
- 'data-invalid-syntax': 'media',
93
- 'data-invalid-type': 'argument',
94
- 'data-invalid-message': 'Dot-segments cannot be used in media paths; use subresource paths instead',
95
- });
91
+ markInvalid(target, 'media', 'argument',
92
+ 'Dot-segments cannot be used in media paths; use subresource paths instead');
96
93
  return false;
97
94
  }
98
95
  break;
99
96
  default:
100
- define(target, {
101
- class: void target.classList.add('invalid'),
102
- 'data-invalid-syntax': 'media',
103
- 'data-invalid-type': 'argument',
104
- 'data-invalid-message': 'Invalid protocol',
105
- });
97
+ markInvalid(target, 'media', 'argument', 'Invalid protocol');
106
98
  return false;
107
99
  }
108
100
  if (alt.includes('\x1B')) {
109
- define(target, {
110
- class: void target.classList.add('invalid'),
111
- 'data-invalid-syntax': 'media',
112
- 'data-invalid-type': 'content',
113
- 'data-invalid-message': `Cannot use invalid HTML entitiy "${alt.match(/&[0-9A-Za-z]+;/)![0]}"`,
114
- alt: target.getAttribute('alt')?.replace(/\x1B/g, ''),
115
- });
101
+ define(target, { alt: target.getAttribute('alt')?.replace(/\x1B/g, '') });
102
+ markInvalid(target, 'media', 'content',
103
+ `Cannot use invalid HTML entitiy "${alt.match(/&[0-9A-Za-z]+;/)![0]}"`);
116
104
  return false;
117
105
  }
118
106
  return true;
@@ -1,4 +1,5 @@
1
1
  import { number as calculate, isFixed } from '../inline/extension/label';
2
+ import { markInvalid, unmarkInvalid } from '../util';
2
3
  import { MultiQueue } from 'spica/queue';
3
4
  import { push } from 'spica/array';
4
5
  import { define } from 'typed-dom/dom';
@@ -32,46 +33,26 @@ export function* figure(
32
33
  : `$-${increment(index, def as HTMLHeadingElement)}`;
33
34
  if (label.endsWith('-')) continue;
34
35
  if (label.endsWith('-0')) {
35
- define(def, {
36
- class: void def.classList.add('invalid'),
37
- 'data-invalid-syntax': 'figure',
38
- 'data-invalid-type': 'argument',
39
- 'data-invalid-message': 'Invalid base index',
40
- hidden: null,
41
- });
36
+ markInvalid(def, 'figure', 'argument', 'Invalid base index');
37
+ define(def, { hidden: null });
42
38
  continue;
43
39
  }
44
40
  if (tagName === 'FIGURE' && label.endsWith('.0')) {
45
41
  // $-x.x.0 is disabled.
46
42
  if (label.lastIndexOf('.', label.length - 3) !== -1) {
47
- define(def, {
48
- class: void def.classList.add('invalid'),
49
- 'data-invalid-syntax': 'figure',
50
- 'data-invalid-type': 'argument',
51
- 'data-invalid-message': 'Base index must be $-x.0 format',
52
- hidden: null,
53
- });
43
+ markInvalid(def, 'figure', 'argument', 'Base index must be $-x.0 format');
44
+ define(def, { hidden: null });
54
45
  continue;
55
46
  }
56
47
  // $-x.0 after h1-h6.
57
48
  if (!/^H[1-6]$/.test(def.previousElementSibling?.tagName ?? '')) {
58
- define(def, {
59
- class: void def.classList.add('invalid'),
60
- 'data-invalid-syntax': 'figure',
61
- 'data-invalid-type': 'position',
62
- 'data-invalid-message': messages.declaration,
63
- hidden: null,
64
- });
49
+ markInvalid(def, 'figure', 'position', messages.declaration);
50
+ define(def, { hidden: null });
65
51
  continue;
66
52
  }
67
53
  else if (def.getAttribute('data-invalid-message') === messages.declaration) {
68
- define(def, {
69
- class: void def.classList.remove('invalid'),
70
- 'data-invalid-syntax': null,
71
- 'data-invalid-type': null,
72
- 'data-invalid-message': null,
73
- hidden: '',
74
- });
54
+ unmarkInvalid(def);
55
+ define(def, { hidden: null });
75
56
  }
76
57
  }
77
58
  const group = label.split('-', 1)[0];
@@ -120,33 +101,18 @@ export function* figure(
120
101
  group === '$' ? figindex : `${figindex}. `);
121
102
  if (labels.has(label)) {
122
103
  if (def.classList.contains('invalid')) continue;
123
- define(def, {
124
- id: null,
125
- class: void def.classList.add('invalid'),
126
- 'data-invalid-syntax': 'figure',
127
- 'data-invalid-type': 'argument',
128
- 'data-invalid-message': messages.duplicate,
129
- });
104
+ define(def, { id: null });
105
+ markInvalid(def, 'figure', 'argument', messages.duplicate);
130
106
  continue;
131
107
  }
132
108
  else if (def.getAttribute('data-invalid-message') === messages.duplicate) {
133
- define(def, {
134
- class: void def.classList.remove('invalid'),
135
- 'data-invalid-syntax': null,
136
- 'data-invalid-type': null,
137
- 'data-invalid-message': null,
138
- });
109
+ unmarkInvalid(def);
139
110
  }
140
111
  labels.add(label);
141
112
  opts.id !== '' && def.setAttribute('id', `label:${opts.id ? `${opts.id}:` : ''}${label}`);
142
113
  for (const ref of refs.take(label, Infinity)) {
143
114
  if (ref.getAttribute('data-invalid-message') === messages.reference) {
144
- define(ref, {
145
- class: void ref.classList.remove('invalid'),
146
- 'data-invalid-syntax': null,
147
- 'data-invalid-type': null,
148
- 'data-invalid-message': null,
149
- });
115
+ unmarkInvalid(ref);
150
116
  }
151
117
  if (ref.hash.slice(1) === def.id && ref.innerText === figindex) continue;
152
118
  yield define(ref,
@@ -156,12 +122,7 @@ export function* figure(
156
122
  }
157
123
  for (const [, ref] of refs) {
158
124
  if (opts.id !== '' && !ref.classList.contains('invalid')) {
159
- define(ref, {
160
- class: void ref.classList.add('invalid'),
161
- 'data-invalid-syntax': 'label',
162
- 'data-invalid-type': 'reference',
163
- 'data-invalid-message': messages.reference,
164
- });
125
+ markInvalid(ref, 'label', 'reference', messages.reference);
165
126
  }
166
127
  yield ref;
167
128
  }
@@ -162,7 +162,7 @@ describe('Unit: parser/processor/note', () => {
162
162
  });
163
163
 
164
164
  it('split', () => {
165
- const target = parse('((1))\n\n## a\n\n((2))((1))((3))((2))');
165
+ const target = parse('((1))\n\n## a\n\n((2))((1))((3))((2))\n\n## b\n\n((2))');
166
166
  for (let i = 0; i < 3; ++i) {
167
167
  [...note(target)];
168
168
  assert.deepStrictEqual(
@@ -216,6 +216,19 @@ describe('Unit: parser/processor/note', () => {
216
216
  html('sup', [html('a', { href: '#annotation::ref:3:1' }, '^4')]),
217
217
  ]),
218
218
  ]).outerHTML,
219
+ html('h2', { id: 'index::b' }, 'b').outerHTML,
220
+ html('p', [
221
+ html('sup', { class: 'annotation', id: 'annotation::ref:2:3', title: '2' }, [
222
+ html('span', { hidden: '' }, '2'),
223
+ html('a', { href: '#annotation::def:2:2' }, '*5')
224
+ ]),
225
+ ]).outerHTML,
226
+ html('ol', { class: 'annotations' }, [
227
+ html('li', { id: 'annotation::def:2:2', 'data-marker': '*5' }, [
228
+ html('span', '2'),
229
+ html('sup', [html('a', { href: '#annotation::ref:2:3' }, '^6')]),
230
+ ]),
231
+ ]).outerHTML,
219
232
  ]);
220
233
  }
221
234
  });
@@ -260,14 +273,14 @@ describe('Unit: parser/processor/note', () => {
260
273
  html('p', [
261
274
  html('sup', { class: 'reference', id: 'reference::ref:a_b:1', title: 'a b' }, [
262
275
  html('span', { hidden: '' }, 'a b'),
263
- html('a', { href: '#reference::def:a_b:1' }, '[1]')
276
+ html('a', { href: '#reference::def:a_b' }, '[1]')
264
277
  ]),
265
278
  ]).outerHTML,
266
279
  ]);
267
280
  assert.deepStrictEqual(
268
281
  note.outerHTML,
269
282
  html('ol', [
270
- html('li', { id: 'reference::def:a_b:1' }, [
283
+ html('li', { id: 'reference::def:a_b' }, [
271
284
  html('span', 'a b'),
272
285
  html('sup', [html('a', { href: '#reference::ref:a_b:1' }, '^1')]),
273
286
  ]),
@@ -286,22 +299,22 @@ describe('Unit: parser/processor/note', () => {
286
299
  html('p', [
287
300
  html('sup', { class: 'reference', 'data-abbr': 'a', id: 'reference::ref:a:1', title: 'b' }, [
288
301
  html('span', { hidden: '' }, 'b'),
289
- html('a', { href: '#reference::def:a:1' }, '[a]')
302
+ html('a', { href: '#reference::def:a' }, '[a]')
290
303
  ]),
291
304
  html('sup', { class: 'reference', 'data-abbr': 'a', id: 'reference::ref:a:2', title: 'b' }, [
292
305
  html('span', { hidden: '' }),
293
- html('a', { href: '#reference::def:a:1' }, '[a]')
306
+ html('a', { href: '#reference::def:a' }, '[a]')
294
307
  ]),
295
308
  html('sup', { class: 'reference', 'data-abbr': 'a', id: 'reference::ref:a:3', title: 'b' }, [
296
309
  html('span', { hidden: '' }),
297
- html('a', { href: '#reference::def:a:1' }, '[a]')
310
+ html('a', { href: '#reference::def:a' }, '[a]')
298
311
  ]),
299
312
  ]).outerHTML,
300
313
  ]);
301
314
  assert.deepStrictEqual(
302
315
  note.outerHTML,
303
316
  html('ol', [
304
- html('li', { id: 'reference::def:a:1' }, [
317
+ html('li', { id: 'reference::def:a' }, [
305
318
  html('span', 'b'),
306
319
  html('sup', [
307
320
  html('a', { href: '#reference::ref:a:1', title: 'b' }, '^1'),
@@ -334,7 +347,7 @@ describe('Unit: parser/processor/note', () => {
334
347
  ]),
335
348
  html('sup', { class: 'reference', 'data-abbr': 'b', id: 'reference::ref:b:1', title: 'c' }, [
336
349
  html('span', { hidden: '' }, 'c'),
337
- html('a', { href: '#reference::def:b:1' }, '[b]')
350
+ html('a', { href: '#reference::def:b' }, '[b]')
338
351
  ]),
339
352
  ]).outerHTML,
340
353
  html('ol', { class: 'annotations' }, [
@@ -343,7 +356,7 @@ describe('Unit: parser/processor/note', () => {
343
356
  'a',
344
357
  html('sup', { class: 'reference', 'data-abbr': 'b', id: 'reference::ref:b:2', title: 'c' }, [
345
358
  html('span', { hidden: '' }),
346
- html('a', { href: '#reference::def:b:1' }, '[b]')
359
+ html('a', { href: '#reference::def:b' }, '[b]')
347
360
  ]),
348
361
  ]),
349
362
  html('sup', [html('a', { href: '#annotation::ref:a:1' }, '^1')])
@@ -353,7 +366,7 @@ describe('Unit: parser/processor/note', () => {
353
366
  assert.deepStrictEqual(
354
367
  note.outerHTML,
355
368
  html('ol', [
356
- html('li', { id: 'reference::def:b:1' }, [
369
+ html('li', { id: 'reference::def:b' }, [
357
370
  html('span', 'c'),
358
371
  html('sup', [
359
372
  html('a', { href: '#reference::ref:b:1', title: 'c' }, '^1'),
@@ -1,9 +1,13 @@
1
1
  import { identity, text } from '../inline/extension/indexee';
2
+ import { markInvalid, unmarkInvalid } from '../util';
2
3
  import { frag, html, define } from 'typed-dom/dom';
3
4
 
4
5
  export function* note(
5
6
  target: ParentNode & Node,
6
- notes?: { readonly annotations?: HTMLOListElement; readonly references: HTMLOListElement; },
7
+ notes?: {
8
+ readonly annotations?: HTMLOListElement;
9
+ readonly references: HTMLOListElement;
10
+ },
7
11
  opts: { readonly id?: string; } = {},
8
12
  bottom: Node | null = null,
9
13
  ): Generator<HTMLAnchorElement | HTMLLIElement | undefined, undefined, undefined> {
@@ -46,10 +50,10 @@ function build(
46
50
  const titles = new Map<string, string>();
47
51
  const defIndexes = new Map<HTMLLIElement, number>();
48
52
  const refSubindexes = new Map<string, number>();
49
- const defSubindexes = new Map<string, number>();
50
- let refIndex = 0;
53
+ const defSubindexes = splitter ? new Map<string, number>() : undefined;
51
54
  let total = 0;
52
- let style: 'count' | 'abbr';
55
+ let format: 'number' | 'abbr';
56
+ let refIndex = 0;
53
57
  for (let len = refs.length, i = 0; i < len; ++i) {
54
58
  const ref = refs[i];
55
59
  if (ref.closest('[hidden]')) {
@@ -61,6 +65,7 @@ function build(
61
65
  if (defs.size > 0) {
62
66
  total += defs.size;
63
67
  yield* proc(defs, target.insertBefore(html('ol', { class: `${syntax}s` }), splitters[0]));
68
+ assert(defs.size === 0);
64
69
  }
65
70
  else if (splitters.length % 100 === 0) {
66
71
  yield;
@@ -77,51 +82,44 @@ function build(
77
82
  const initial = splitter
78
83
  ? !defs.has(identifier)
79
84
  : refSubindex === 1;
80
- const defSubindex = defSubindexes.get(identifier)! + 1 || 1;
81
- defSubindexes.set(identifier, defSubindex);
85
+ const defSubindex = defSubindexes?.get(identifier)! + +initial || 1;
86
+ initial && defSubindexes?.set(identifier, defSubindex);
87
+ const defId = opts.id !== ''
88
+ ? `${syntax}:${opts.id ?? ''}:def:${identifier}${splitter && `:${defSubindex}`}`
89
+ : undefined;
82
90
  const def = initial
83
91
  ? html('li',
84
92
  {
85
- id: opts.id !== '' ? `${syntax}:${opts.id ?? ''}:def:${identifier}:${defSubindex}` : undefined,
86
- 'data-marker': !note ? marker(total + defs.size + 1, abbr) : undefined,
93
+ id: defId,
94
+ 'data-marker': note ? undefined : marker(total + defs.size + 1, abbr),
87
95
  },
88
96
  [define(ref.firstElementChild!.cloneNode(true), { hidden: null }), html('sup')])
89
97
  : defs.get(identifier)!;
90
98
  initial && defs.set(identifier, def);
91
- assert(def.lastChild);
99
+ assert(def.lastElementChild?.matches('sup'));
92
100
  const defIndex = initial
93
101
  ? total + defs.size
94
102
  : defIndexes.get(def)!;
95
103
  initial && defIndexes.set(def, defIndex);
96
- const defId = def.id || undefined;
97
104
  const title = initial
98
105
  ? text(ref.firstElementChild!)
99
106
  : titles.get(identifier)!;
100
107
  initial && titles.set(identifier, title);
101
108
  assert(syntax !== 'annotation' || title);
102
- style ??= abbr ? 'abbr' : 'count';
103
- if (style === 'count' ? abbr : !abbr) {
104
- define(ref, {
105
- class: void ref.classList.add('invalid'),
106
- 'data-invalid-syntax': syntax,
107
- 'data-invalid-type': 'style',
108
- 'data-invalid-message': `${syntax[0].toUpperCase() + syntax.slice(1)} style must be consistent`,
109
- });
110
- }
111
- else if (ref.getAttribute('data-invalid-type') === 'style') {
112
- define(ref, {
113
- class: void ref.classList.remove('invalid'),
114
- 'data-invalid-syntax': null,
115
- 'data-invalid-type': null,
116
- 'data-invalid-message': null,
117
- });
118
- }
119
- if (!ref.firstElementChild!.hasAttribute('hidden')) {
120
- ref.firstElementChild!.setAttribute('hidden', '');
109
+ format ??= abbr ? 'abbr' : 'number';
110
+ if (!ref.classList.contains('invalid')) {
111
+ if (format === 'number' ? abbr : !abbr) {
112
+ markInvalid(ref, syntax, 'format', 'Notation format must be consistent with numbers or abbreviations');
113
+ }
121
114
  }
122
- else {
123
- ref.lastChild?.remove();
115
+ else switch (ref.getAttribute('data-invalid-syntax')) {
116
+ case 'format':
117
+ case 'content':
118
+ unmarkInvalid(ref);
124
119
  }
120
+ ref.firstElementChild!.hasAttribute('hidden')
121
+ ? ref.lastElementChild!.remove()
122
+ : ref.firstElementChild!.setAttribute('hidden', '');
125
123
  define(ref, {
126
124
  id: refId,
127
125
  class: opts.id !== '' ? undefined : void ref.classList.add('disabled'),
@@ -135,7 +133,7 @@ function build(
135
133
  });
136
134
  yield ref.appendChild(html('a', { href: refId && defId && `#${defId}` }, marker(defIndex, abbr)));
137
135
  assert(ref.title || ref.matches('.invalid'));
138
- def.lastChild!.appendChild(
136
+ def.lastElementChild!.appendChild(
139
137
  html('a',
140
138
  {
141
139
  href: refId && `#${refId}`,
@@ -143,7 +141,7 @@ function build(
143
141
  },
144
142
  `^${++refIndex}`));
145
143
  }
146
- if (defs.size > 0 || note) {
144
+ if (note || defs.size > 0) {
147
145
  yield* proc(defs, note ?? target.insertBefore(html('ol', { class: `${syntax}s` }), splitters[0] ?? bottom));
148
146
  }
149
147
  return;
@@ -1,3 +1,29 @@
1
+ import { define } from 'typed-dom/dom';
2
+
3
+ export function markInvalid<T extends Element>(
4
+ el: T,
5
+ syntax: string,
6
+ type: string,
7
+ message: string,
8
+ ): T {
9
+ assert(!message.endsWith('.'));
10
+ return define(el, {
11
+ class: void el.classList.add('invalid'),
12
+ 'data-invalid-syntax': syntax,
13
+ 'data-invalid-type': type,
14
+ 'data-invalid-message': message,
15
+ });
16
+ }
17
+
18
+ export function unmarkInvalid<T extends Element>(el: T): T {
19
+ return define(el, {
20
+ class: void el.classList.remove('invalid'),
21
+ 'data-invalid-syntax': null,
22
+ 'data-invalid-type': null,
23
+ 'data-invalid-message': null,
24
+ });
25
+ }
26
+
1
27
  export function stringify(nodes: readonly (HTMLElement | string)[]): string {
2
28
  let acc = '';
3
29
  for (let i = 0; i < nodes.length; ++i) {