rapier-markdown-kit 1.2.51 → 1.2.55

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.
@@ -156,12 +156,16 @@ function initMarkdownIt() {
156
156
  md.renderer.rules.softbreak = () =>
157
157
  sourceToken('\n', '\u2060', 'rapier-source-token--softbreak');
158
158
  md.core.ruler.before('text_join', 'rapier_escape_token', state => {
159
+ if (state.src.indexOf('\\') < 0) return;
159
160
  for (let at = 0; at < state.tokens.length; at++) {
160
161
  const token = state.tokens[at];
161
- if (token.type !== 'inline' || !token.children) continue;
162
+ if (token.type !== 'inline' || !token.children || token.content.indexOf('\\') < 0) continue;
162
163
  for (let next = 0; next < token.children.length; next++) {
163
164
  const child = token.children[next];
164
- if (child.type === 'text_special' && child.info === 'escape' && child.content.length === 1 && !/[\\*`[\]_]/.test(child.content)) child.type = 'rapier_escape';
165
+ if (child.type === 'text_special' && child.info === 'escape' && child.content.length === 1) {
166
+ const c = child.content.charCodeAt(0);
167
+ if (c !== 0x5c && c !== 0x2a && c !== 0x60 && c !== 0x5b && c !== 0x5d && c !== 0x5f) child.type = 'rapier_escape';
168
+ }
165
169
  }
166
170
  }
167
171
  });
@@ -320,15 +324,27 @@ function initMarkdownIt() {
320
324
  };
321
325
 
322
326
  md.core.ruler.push('rapier_setext_rule', function (state) {
323
- let lines = null;
327
+ let starts = null;
324
328
  for (let at = 0; at < state.tokens.length; at++) {
325
329
  const token = state.tokens[at];
326
330
  if (token.type !== 'heading_open') continue;
327
331
  if (token.markup !== '=' && token.markup !== '-') continue;
328
332
  if (!token.map) continue;
329
- if (lines === null) lines = state.src.split('\n');
330
- const underline = lines[token.map[1] - 1];
331
- if (typeof underline === 'string' && /^ {0,3}(?:=+|-+)[ \t]*$/.test(underline)) {
333
+ if (starts === null) {
334
+ starts = state._lineStarts;
335
+ if (!starts) {
336
+ starts = [0];
337
+ for (let i = state.src.indexOf('\n'); i >= 0; i = state.src.indexOf('\n', i + 1)) starts.push(i + 1);
338
+ state._lineStarts = starts;
339
+ }
340
+ }
341
+ const lineIdx = token.map[1] - 1;
342
+ if (lineIdx < 0 || lineIdx >= starts.length) continue;
343
+ const lineStart = starts[lineIdx];
344
+ const nextStart = starts[lineIdx + 1];
345
+ const lineEnd = nextStart !== undefined ? nextStart - 1 : state.src.length;
346
+ const underline = state.src.slice(lineStart, lineEnd);
347
+ if (/^ {0,3}(?:=+|-+)[ \t]*$/.test(underline)) {
332
348
  token.attrSet('data-rapier-heading-rule', underline);
333
349
  }
334
350
  }
@@ -920,15 +936,15 @@ function _rapierInstallSemanticProbe(instance) {
920
936
  return false;
921
937
  }
922
938
  const firstEnd = state.md.helpers.parseLinkLabel(state, labelStart, false);
923
- if (firstEnd < 0) return false;
924
- const firstLabel = src.slice(labelStart + 1, firstEnd);
939
+ if (firstEnd < 0 || firstEnd === labelStart + 1 && src.charCodeAt(firstEnd + 1) !== 0x5b && src.charCodeAt(firstEnd + 1) !== 0x28) return false;
925
940
  const after = firstEnd + 1;
941
+ const afterCode = src.charCodeAt(after);
926
942
 
927
- if (src.charCodeAt(after) === 0x5b) {
943
+ if (afterCode === 0x5b) {
928
944
  const secondEnd = state.md.helpers.parseLinkLabel(state, after, false);
929
945
  if (secondEnd < 0) return false;
930
946
  const explicitLabel = src.slice(after + 1, secondEnd);
931
- const rawKey = explicitLabel.length ? explicitLabel : firstLabel;
947
+ const rawKey = explicitLabel.length ? explicitLabel : src.slice(labelStart + 1, firstEnd);
932
948
  record({
933
949
  kind: image ? 'image-reference-use' : 'link-reference-use', rawKey,
934
950
  localStart: pos, localEnd: secondEnd + 1,
@@ -938,9 +954,13 @@ function _rapierInstallSemanticProbe(instance) {
938
954
  return false;
939
955
  }
940
956
 
941
- if (src.charCodeAt(after) === 0x28) {
957
+ if (afterCode === 0x28) {
942
958
  let destinationPos = after + 1;
943
- while (destinationPos < max && /[ \t\n]/.test(src.charAt(destinationPos))) destinationPos++;
959
+ while (destinationPos < max) {
960
+ const c = src.charCodeAt(destinationPos);
961
+ if (c === 0x20 || c === 0x09 || c === 0x0a) destinationPos++;
962
+ else break;
963
+ }
944
964
  const first = src.charCodeAt(destinationPos);
945
965
  if (first !== 0x23 && first !== 0x3c && first !== 0x5c && first !== 0x26) return false;
946
966
  const destination = state.md.helpers.parseLinkDestination(src, destinationPos, max);
@@ -957,9 +977,12 @@ function _rapierInstallSemanticProbe(instance) {
957
977
  return false;
958
978
  }
959
979
 
980
+ if (!image && !state.env.references) return false;
981
+ const firstLabel = src.slice(labelStart + 1, firstEnd);
982
+ if (!firstLabel) return false;
960
983
  const normalize = state.md.utils && state.md.utils.normalizeReference;
961
984
  const normalized = typeof normalize === 'function' ? normalize(firstLabel) : firstLabel.trim().replace(/\s+/g, ' ').toUpperCase();
962
- if ((image || (state.env.references && state.env.references[normalized])) && firstLabel) {
985
+ if (image || (state.env.references && state.env.references[normalized])) {
963
986
  record({
964
987
  kind: image ? 'image-reference-use' : 'link-reference-use', rawKey: firstLabel,
965
988
  localStart: pos, localEnd: firstEnd + 1,
@@ -971,6 +994,7 @@ function _rapierInstallSemanticProbe(instance) {
971
994
 
972
995
  try {
973
996
  instance.core.ruler.before('footnote_tail', 'rapier_footnote_definition_probe', function (state) {
997
+ if (!state.env?.footnotes) { state.env.__rapierFootnoteDefinitions = []; return; }
974
998
  const out = [];
975
999
  const tokens = state && Array.isArray(state.tokens) ? state.tokens : [];
976
1000
  const signatureFor = values => {
@@ -121,7 +121,7 @@ function prepareMathSource(input, DOMParser) {
121
121
  return sourceRoot ? root.outerHTML : root;
122
122
  }
123
123
 
124
- function createInertHtmlSanitizer({parseFragment, serialize, sanitizeHtml, safeRasterDataUrl, chromeOwnsId = id => /^rapier-(?!anchor-)/.test(id)}) {
124
+ function createInertHtmlSanitizer({parseFragment, serialize, sanitizeHtml, safeRasterDataUrl, chromeOwnsId = id => /^(?:rapier-(?!anchor-)|Rapier[A-Z]|_rapier)/.test(id)}) {
125
125
  if (![parseFragment, serialize, sanitizeHtml, safeRasterDataUrl, chromeOwnsId].every(port => typeof port === 'function'))
126
126
  throw new TypeError('The inert HTML sanitizer requires every parser and filtering port');
127
127
  const uri = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i;
@@ -184,7 +184,7 @@ function createInertHtmlSanitizer({parseFragment, serialize, sanitizeHtml, safeR
184
184
  const key = name.toLowerCase();
185
185
  const value = name === 'value' ? inputValue : inputValue.trim();
186
186
  if (forbiddenAttributes.has(key) || key.startsWith('on')) continue;
187
- if (key === 'id' && chromeOwnsId(value)) continue;
187
+ if ((key === 'id' || key === 'name') && chromeOwnsId(value)) continue;
188
188
  if (/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i.test(value)
189
189
  || key === 'attributename' && value.includes('href')) continue;
190
190
  const data = /^data-[\-\w.\u00b7-\uffff]+$/.test(key), aria = /^aria-[\-\w]+$/.test(key);
@@ -372,7 +372,7 @@ function _rapierInstallDocumentSanitizeHooks() {
372
372
  data.keepAttr = !!data.attrValue;
373
373
  return;
374
374
  }
375
- if (String(data.attrName || '').toLowerCase() === 'id' && _rapierChromeOwnsId(value)) { data.keepAttr = false; return; }
375
+ if (/^(?:id|name)$/.test(String(data.attrName || '').toLowerCase()) && _rapierChromeOwnsId(value)) { data.keepAttr = false; return; }
376
376
  if (_rapierRemoteContent.allowed || _rapierSanitizeRuntime.context === 'source') return;
377
377
  const attribute = String(data.attrName || '').toLowerCase();
378
378
  if (attribute === 'alt' || attribute === 'title') return;
@@ -502,7 +502,7 @@ function escapeRapierHtmlText(value) {
502
502
 
503
503
  function _rapierChromeOwnsId(id) {
504
504
  if (!_rapierSanitizeRuntime.chromeIds) _rapierSanitizeRuntime.chromeIds = new Set(Array.from(typeof document?.querySelectorAll === 'function' ? document.querySelectorAll('[id]') : [], element => element.id));
505
- return _rapierSanitizeRuntime.chromeIds.has(String(id)) || /^rapier-(?!anchor-)/.test(id);
505
+ return _rapierSanitizeRuntime.chromeIds.has(String(id)) || /^(?:rapier-(?!anchor-)|Rapier[A-Z]|_rapier)/.test(id);
506
506
  }
507
507
 
508
508
  const RAPIER_BIDI_CONTROL = /[\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/;
@@ -1,32 +1,71 @@
1
1
  // SPDX-License-Identifier: MIT
2
+ function countLines(source, from, to) {
3
+ let lines = 0;
4
+ for (let i = from; i < to; i++) {
5
+ const code = source.charCodeAt(i);
6
+ if (code === 0x0a || code === 0x0d) lines++;
7
+ }
8
+ return lines;
9
+ }
10
+
2
11
  export function createMathMatcher(source, {singleDollar = false, brackets = true} = {}) {
3
12
  if (typeof source !== 'string') throw new TypeError('Math boundaries require source text.');
4
- const markers = new Map(), next = Object.create(null);
5
- let slashes = 0, line = 0;
6
- for (let at = 0; at < source.length;) {
7
- const char = source[at];
8
- if (char === '\\') { slashes++; at++; continue; }
9
- if (char === '\n' || char === '\r') line++;
10
- if (char === '$') {
13
+ const len = source.length;
14
+ if (len === 0 || (source.indexOf('$') < 0 && (!brackets || source.indexOf('\\') < 0))) {
15
+ return () => null;
16
+ }
17
+ const markers = new Map(), list = [], next = Object.create(null);
18
+ if (!brackets) {
19
+ let line = 0, lastAt = 0;
20
+ for (let at = source.indexOf('$'); at >= 0; at = source.indexOf('$', at)) {
21
+ line += countLines(source, lastAt, at);
22
+ let slashes = 0;
23
+ for (let s = at - 1; s >= 0 && source.charCodeAt(s) === 0x5c; s--) slashes++;
11
24
  let end = at + 1;
12
- while (source[end] === '$') end++;
25
+ while (end < len && source.charCodeAt(end) === 0x24) end++;
13
26
  const count = end - at;
14
- if (!(slashes & 1) && (count === 2 || count === 1 && singleDollar))
15
- markers.set(at, {at, marker: count === 2 ? '$$' : '$', line});
27
+ if (!(slashes & 1) && (count === 2 || count === 1 && singleDollar)) {
28
+ const entry = {at, marker: count === 2 ? '$$' : '$', line, close: undefined};
29
+ markers.set(at, entry);
30
+ list.push(entry);
31
+ }
32
+ lastAt = end;
16
33
  at = end;
17
- } else {
18
- if (brackets && slashes & 1 && '()[]'.includes(char))
19
- markers.set(at - 1, {at: at - 1, marker: '\\' + char, line});
20
- at++;
21
34
  }
22
- slashes = 0;
35
+ } else {
36
+ let slashes = 0, line = 0;
37
+ for (let at = 0; at < len;) {
38
+ const code = source.charCodeAt(at);
39
+ if (code === 0x5c) { slashes++; at++; continue; }
40
+ if (code === 0x0a || code === 0x0d) line++;
41
+ if (code === 0x24) {
42
+ let end = at + 1;
43
+ while (end < len && source.charCodeAt(end) === 0x24) end++;
44
+ const count = end - at;
45
+ if (!(slashes & 1) && (count === 2 || count === 1 && singleDollar)) {
46
+ const entry = {at, marker: count === 2 ? '$$' : '$', line, close: undefined};
47
+ markers.set(at, entry);
48
+ list.push(entry);
49
+ }
50
+ at = end;
51
+ } else {
52
+ if (slashes & 1 && (code === 0x28 || code === 0x29 || code === 0x5b || code === 0x5d)) {
53
+ const entry = {at: at - 1, marker: '\\' + source[at], line, close: undefined};
54
+ markers.set(at - 1, entry);
55
+ list.push(entry);
56
+ }
57
+ at++;
58
+ }
59
+ slashes = 0;
60
+ }
23
61
  }
24
- for (const entry of [...markers.values()].reverse()) {
62
+ for (let i = list.length - 1; i >= 0; i--) {
63
+ const entry = list[i];
25
64
  const closing = entry.marker === '\\(' ? '\\)' : entry.marker === '\\[' ? '\\]' : entry.marker;
26
65
  entry.close = next[closing];
27
66
  next[entry.marker] = entry;
28
67
  }
29
- return function match(start, limit = source.length) {
68
+ return function match(start, limit = len) {
30
69
  const open = markers.get(start), close = open?.close;
31
70
  if (!close || open.marker === '\\)' || open.marker === '\\]' || close.at + close.marker.length > limit) return null;
32
71
  const bodyStart = start + open.marker.length, bodyEnd = close.at;
@@ -1,6 +1,19 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  import {createMathMatcher} from './delimiters.mjs';
3
3
 
4
+ function isMathFenceInfo(info) {
5
+ if (!info || info.length < 4) return false;
6
+ let start = 0;
7
+ const len = info.length;
8
+ while (start < len && info.charCodeAt(start) <= 0x20) start++;
9
+ if (start + 4 > len) return false;
10
+ if ((info.charCodeAt(start) | 32) !== 0x6d ||
11
+ (info.charCodeAt(start + 1) | 32) !== 0x61 ||
12
+ (info.charCodeAt(start + 2) | 32) !== 0x74 ||
13
+ (info.charCodeAt(start + 3) | 32) !== 0x68) return false;
14
+ return start + 4 === len || info.charCodeAt(start + 4) <= 0x20;
15
+ }
16
+
4
17
  export function installMathRules(md, {singleDollar = false, brackets = true, ruleName = 'rapier_math', inLink = false} = {}) {
5
18
  const authored = new WeakMap();
6
19
  md.core.ruler.before('normalize', ruleName + '_source', state => {
@@ -9,13 +22,13 @@ export function installMathRules(md, {singleDollar = false, brackets = true, rul
9
22
  md.core.ruler.after('block', ruleName + '_source_release', state => authored.delete(state.tokens));
10
23
  const tokenize = md.block.tokenize;
11
24
  md.block.tokenize = function(state, startLine, endLine) {
12
- const first = state.tokens.length;
25
+ const first = Math.max(state.tokens.length, state._mathScanned || 0);
13
26
  try { return tokenize.call(this, state, startLine, endLine); }
14
27
  finally {
15
- for (let index = first; index < state.tokens.length; index++) {
28
+ const len = state.tokens.length;
29
+ for (let index = first; index < len; index++) {
16
30
  const token = state.tokens[index];
17
- if (token.type !== 'fence' || !token.map || token.meta?.mathSource !== undefined ||
18
- String(token.info || '').trim().split(/\s+/)[0].toLowerCase() !== 'math') continue;
31
+ if (token.type !== 'fence' || !token.map || token.meta?.mathSource !== undefined || !isMathFenceInfo(token.info)) continue;
19
32
  const held = authored.get(state.tokens), source = held?.source ?? state.src;
20
33
  let value;
21
34
  if (!state.rapierSourceSlice && source === state.src) value = state.getLines(token.map[0], token.map[1], state.blkIndent, false);
@@ -42,6 +55,7 @@ export function installMathRules(md, {singleDollar = false, brackets = true, rul
42
55
  }
43
56
  token.meta = {...token.meta, mathSource: value};
44
57
  }
58
+ state._mathScanned = len;
45
59
  }
46
60
  };
47
61
  const renderText = md.renderer.renderInlineAsText;
@@ -62,7 +76,8 @@ export function installMathRules(md, {singleDollar = false, brackets = true, rul
62
76
  }
63
77
  md.inline.ruler.before('escape', ruleName, (state, silent) => {
64
78
  if (!inLink && state.linkLevel > 0) return false;
65
- if (state.src[state.pos] !== '$' && state.src[state.pos] !== '\\') return false;
79
+ const ch = state.src.charCodeAt(state.pos);
80
+ if (ch !== 0x24 && (!brackets || ch !== 0x5c)) return false;
66
81
  const span = match(state, state.pos, state.posMax);
67
82
  if (!span) return false;
68
83
  if (!silent) {
@@ -77,7 +92,8 @@ export function installMathRules(md, {singleDollar = false, brackets = true, rul
77
92
  md.block.ruler.before('fence', ruleName, (state, startLine, endLine, silent) => {
78
93
  if (state.sCount[startLine] - state.blkIndent >= 4) return false;
79
94
  const start = state.bMarks[startLine] + state.tShift[startLine];
80
- if (state.src.slice(start, start + 2) !== '$$' && (!brackets || state.src.slice(start, start + 2) !== '\\[')) return false;
95
+ const c0 = state.src.charCodeAt(start), c1 = state.src.charCodeAt(start + 1);
96
+ if (!(c0 === 0x24 && c1 === 0x24) && (!brackets || !(c0 === 0x5c && c1 === 0x5b))) return false;
81
97
  const span = match(state, start, state.eMarks[endLine - 1]);
82
98
  if (!span?.displayMode) return false;
83
99
  let last = startLine;
@@ -1,6 +1,7 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  // Pure text and markdown-it tokens: no DOM, no byte decoding, no globalThis.Rapier* (MIT kit; decoders stay in images/assets.mjs).
3
3
  import {isWordSidecarBlock} from './word-sidecar.mjs';
4
+ import {tuneMarkdownEngine} from './md-engine.mjs';
4
5
  import {sourceFacts, sourceView, sourceEdit} from '../kit/ledger/source.mjs';
5
6
 
6
7
  export const IMAGE_LIMITS = Object.freeze({bytes: 16 * 1024 * 1024, sourceChars: 64 * 1024 * 1024, dimension: 16384, pixels: 24000000, assets: 1024});
@@ -66,27 +67,22 @@ function parserFor(factory = configured || globalThis.markdownit) {
66
67
  if (configured && factory === globalThis.markdownit) return configured;
67
68
  if (typeof factory !== 'function') return fail('markdown_parser_unavailable');
68
69
  let parser = parsers.get(factory);
69
- if (!parser) { parser = installMarkdownImages(factory({html: true, maxNesting: 20})); parsers.set(factory, parser); }
70
+ if (!parser) { parser = installMarkdownImages(tuneMarkdownEngine(factory({html: true, maxNesting: 20}))); parsers.set(factory, parser); }
70
71
  return parser;
71
72
  }
72
73
  export {parserFor as markdownParser};
73
74
  export const markdownBodyOffset = source => splitSource ? splitSource(source).bodyOffset : 0;
74
75
 
75
- const BLOCK_WINDOW = 1024, TABLE_STEP = 128, WINDOW_END = Symbol('block window end');
76
- const LINE_TABLES = ['bMarks', 'eMarks', 'tShift', 'sCount', 'bsCount'];
77
- const lineIndex = key => typeof key === 'string' && /^(?:0|[1-9]\d*)$/.test(key);
78
- let unheldLine = null;
79
- const unheldLines = new Proxy(Array.prototype, {
80
- get: (target, key, table) => unheldLine && lineIndex(key) ? unheldLine(table, Number(key)) : Reflect.get(target, key, table),
81
- set: (target, key, value, table) => unheldLine && lineIndex(key) ? unheldLine(table, Number(key), true, value) : Reflect.set(target, key, value, table),
82
- });
76
+ const BLOCK_WINDOW = 1024, WINDOW_END = Symbol('block window end');
83
77
 
84
78
  export function eachBlockWindow(source, bodyOffset, parser, env, visit, keepNul = false) {
85
79
  for (const window of blockWindows(source, bodyOffset, parser, env, keepNul)) visit(window);
86
80
  }
87
81
 
88
82
  export function* blockWindows(source, bodyOffset, parser, env, keepNul = false) {
89
- const lineEnds = source.slice(bodyOffset).replace(/\r\n?/g, '\n'), body = keepNul ? lineEnds : lineEnds.replace(/\0/g, '\ufffd');
83
+ const rawSlice = bodyOffset ? source.slice(bodyOffset) : source;
84
+ const lineEnds = rawSlice.indexOf('\r') >= 0 ? rawSlice.replace(/\r\n?/g, '\n') : rawSlice;
85
+ const body = keepNul || lineEnds.indexOf('\0') < 0 ? lineEnds : lineEnds.replace(/\0/g, '\ufffd');
90
86
  let pair = source.indexOf('\r\n', bodyOffset), shift = bodyOffset;
91
87
  const sourceOffset = at => {
92
88
  while (pair >= 0 && at + shift > pair) { shift++; pair = source.indexOf('\r\n', pair + 2); }
@@ -94,89 +90,52 @@ export function* blockWindows(source, bodyOffset, parser, env, keepNul = false)
94
90
  return at + shift;
95
91
  };
96
92
  const tokens = [];
97
- let origin = 0, first = 0, state;
93
+ const state = new parser.block.State(body, parser, env, tokens);
94
+ state.rapierSourceSlice = (from, to) => source.slice(sourceOffset(from), sourceOffset(to));
95
+ const totalLines = state.lineMax;
96
+ let origin = 0, first = 0, windowStartLine = 0;
98
97
  const report = () => ({tokens, firstLine: first, lineStart: line => {
99
98
  if (!line) return sourceOffset(origin);
100
- const end = Object.hasOwn(state.eMarks, line - 1) ? state.eMarks[line - 1] : body.length;
99
+ const absLine = first + line - 1;
100
+ const end = absLine >= 0 && absLine < totalLines ? state.eMarks[absLine] : body.length;
101
101
  return end < body.length ? sourceOffset(end + 1) : undefined;
102
102
  }});
103
- let lines = 0;
104
- for (let at = body.indexOf('\n'); at !== -1; at = body.indexOf('\n', at + 1)) lines++;
105
- if (lines <= BLOCK_WINDOW) {
106
- state = new parser.block.State(body, parser, env, tokens);
107
- state.rapierSourceSlice = (from, to) => source.slice(sourceOffset(from), sourceOffset(to));
108
- parser.block.tokenize(state, state.line, state.lineMax);
103
+ if (totalLines <= BLOCK_WINDOW) {
104
+ parser.block.tokenize(state, state.line, totalLines);
109
105
  yield report(); return;
110
106
  }
111
- state = new parser.block.State(body.slice(body.lastIndexOf('\n') + 1), parser, env, tokens);
112
- state.rapierSourceSlice = (from, to) => source.slice(sourceOffset(from), sourceOffset(to));
113
- lines += state.lineMax;
114
- state.src = body;
115
- const tables = new WeakMap();
116
- for (const name of LINE_TABLES) { state[name].length = 0; tables.set(Object.setPrototypeOf(state[name], unheldLines), name); }
117
- let frontier = 0, ended = false, betweenBlocks = false;
118
- const fill = line => {
119
- while (!ended && line >= state.bMarks.length) {
120
- const held = state.bMarks.length;
121
- let until = frontier;
122
- for (let more = held ? Math.max(TABLE_STEP, held - BLOCK_WINDOW) : BLOCK_WINDOW; more && until < body.length; more--) {
123
- const end = body.indexOf('\n', until);
124
- until = end < 0 ? body.length : end + 1;
125
- }
126
- const scanned = new parser.block.State(body.slice(frontier, until), parser, env, []);
127
- ended = until === body.length;
128
- const added = ended ? scanned.lineMax + 1 : scanned.lineMax;
129
- for (const name of LINE_TABLES) {
130
- const old = state[name], next = scanned[name], offset = name === 'bMarks' || name === 'eMarks' ? frontier : 0, table = [];
131
- for (let at = 0; at < held; at++) table.push(old[at]);
132
- for (let at = 0; at < added; at++) table.push(next[at] + offset);
133
- old.length = 0;
134
- tables.set(table, name);
135
- state[name] = Object.setPrototypeOf(table, unheldLines);
136
- }
137
- frontier = until;
138
- }
139
- };
140
- const answer = (table, line, write, value) => {
141
- const name = tables.get(table);
142
- if (line >= state[name].length) {
143
- if (betweenBlocks) throw WINDOW_END;
144
- fill(line);
145
- }
146
- const current = state[name], held = Object.hasOwn(current, line);
147
- if (!write) return held ? current[line] : undefined;
148
- if (held) current[line] = value;
149
- else Object.defineProperty(current, line, {value, writable: true, enumerable: true, configurable: true});
150
- return true;
151
- };
152
107
  const skipEmptyLines = state.skipEmptyLines;
153
108
  state.skipEmptyLines = function (line) {
154
- if (this.level || line !== this.line || !line) return skipEmptyLines.call(this, line);
155
- if (line >= BLOCK_WINDOW) throw WINDOW_END;
156
- betweenBlocks = true;
157
- line = skipEmptyLines.call(this, line);
158
- betweenBlocks = false;
159
- return line;
160
- };
161
- for (;;) {
162
- for (let at = origin; at < body.length; at++) {
163
- const code = body.charCodeAt(at);
164
- if (code === 10) { origin = at + 1; first++; }
165
- else if (!parser.utils.isSpace(code)) break;
109
+ if (this.level || line !== this.line || line === windowStartLine) return skipEmptyLines.call(this, line);
110
+ if (line - windowStartLine >= BLOCK_WINDOW) throw WINDOW_END;
111
+ const next = skipEmptyLines.call(this, line);
112
+ if (next - windowStartLine >= BLOCK_WINDOW) {
113
+ this.line = windowStartLine + BLOCK_WINDOW;
114
+ throw WINDOW_END;
166
115
  }
167
- frontier = origin; ended = false; betweenBlocks = false;
168
- state.line = 0; state.lineMax = lines - first; state.rapierFirstLine = first;
169
- const outer = unheldLine;
170
- unheldLine = answer;
116
+ return next;
117
+ };
118
+ for (let startLine = 0; startLine < totalLines;) {
119
+ startLine = skipEmptyLines.call(state, startLine);
120
+ if (startLine >= totalLines) break;
121
+ windowStartLine = startLine;
122
+ first = startLine;
123
+ origin = startLine === 0 ? 0 : state.eMarks[startLine - 1] + 1;
124
+ state.line = startLine;
125
+ state.rapierFirstLine = first;
171
126
  let finished = true;
172
- try { parser.block.tokenize(state, 0, state.lineMax); }
127
+ try { parser.block.tokenize(state, startLine, totalLines); }
173
128
  catch (error) { if (error !== WINDOW_END) throw error; finished = false; }
174
- finally { unheldLine = outer; }
129
+ if (first !== 0) {
130
+ for (let i = 0; i < tokens.length; i++) {
131
+ const m = tokens[i].map;
132
+ if (m) { m[0] -= first; m[1] -= first; }
133
+ }
134
+ }
175
135
  yield report();
176
136
  if (finished) return;
177
137
  tokens.length = 0;
178
- origin = state.eMarks[state.line - 1] + 1; first += state.line;
179
- for (const name of LINE_TABLES) state[name].length = 0;
138
+ startLine = state.line;
180
139
  }
181
140
  }
182
141
 
@@ -184,7 +143,7 @@ const INVALID_PERCENT_PAYLOAD = /[^A-Za-z0-9._~:\/?@+$,;=%-]/;
184
143
  const isHexUnit = code => code >= 48 && code <= 57 || code >= 65 && code <= 70 || code >= 97 && code <= 102;
185
144
  let lastScan = null;
186
145
  export function dataImage(url) {
187
- if (typeof url !== 'string' || url.length > IMAGE_LIMITS.sourceChars) return null;
146
+ if (typeof url !== 'string' || url.length < 15 || (url.charCodeAt(0) | 32) !== 0x64 || url.charCodeAt(4) !== 0x3a || url.length > IMAGE_LIMITS.sourceChars) return null;
188
147
  if (url.length > 4096) {
189
148
  if (lastScan?.url !== url) lastScan = {url, image: scanDataImage(url)};
190
149
  return lastScan.image && {...lastScan.image};