rapier-markdown-kit 1.1.101 → 1.1.104
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/LICENSE +2 -2
- package/README.md +213 -53
- package/dist/agent/vendor/hyphen-en-us/LICENSE +14 -0
- package/dist/agent/vendor/hyphen-en-us/SOURCE.json +33 -0
- package/dist/agent/vendor/hyphen-en-us/hyph-en-us.hyp.txt +14 -0
- package/dist/agent/vendor/hyphen-en-us/hyph-en-us.pat.txt +4938 -0
- package/dist/agent/vendor/hyphen-en-us/patterns.js +25 -0
- package/dist/agent/vendor/pretext/LICENSE +208 -0
- package/dist/agent/vendor/pretext/NOTICE +26 -13
- package/dist/agent/vendor/pretext/SOURCE.json +213 -45
- package/dist/agent/vendor/pretext/analysis.js +675 -1224
- package/dist/agent/vendor/pretext/entry-geometry.js +183 -0
- package/dist/agent/vendor/pretext/gecko-line-breaks.js +688 -0
- package/dist/agent/vendor/pretext/generated/engine-break-data.js +57 -0
- package/dist/agent/vendor/pretext/generated/webkit-generic-families.js +80 -0
- package/dist/agent/vendor/pretext/graphemes.js +98 -0
- package/dist/agent/vendor/pretext/han-kerning.js +253 -0
- package/dist/agent/vendor/pretext/layout.js +352 -290
- package/dist/agent/vendor/pretext/line-break.js +1154 -588
- package/dist/agent/vendor/pretext/line-breaks.js +892 -0
- package/dist/agent/vendor/pretext/line-text.js +81 -39
- package/dist/agent/vendor/pretext/measurement.js +1105 -176
- package/dist/agent/vendor/pretext/prepare.js +807 -0
- package/dist/agent/vendor/pretext/rich-inline.js +1242 -273
- package/dist/agent/will.mjs +2 -0
- package/dist/kit/assets.mjs +5 -1
- package/dist/kit/conformance/run.mjs +4 -4
- package/dist/kit/export-fonts.mjs +26 -11
- package/dist/kit/index.mjs +1 -0
- package/dist/kit/ledger/carried.mjs +9 -7
- package/dist/kit/ledger/data.mjs +73 -13
- package/dist/kit/ledger/format.mjs +166 -53
- package/dist/kit/ledger/hash.mjs +75 -18
- package/dist/kit/ledger/history.mjs +143 -115
- package/dist/kit/ledger/inverse-source.mjs +158 -19
- package/dist/kit/ledger/journal-records.mjs +292 -67
- package/dist/kit/model.mjs +1 -1
- package/dist/kit/pdf-glyphs.mjs +4 -4
- package/dist/kit/render-markdown.mjs +259 -122
- package/dist/kit/render-sanitize.mjs +24 -4
- package/dist/kit/render-work.mjs +99 -20
- package/dist/kit/render.mjs +101 -65
- package/dist/kit/typography.mjs +2 -0
- package/dist/layout/hyphenation.mjs +127 -0
- package/dist/layout/line-plan.mjs +88 -96
- package/dist/layout/model.mjs +61 -80
- package/dist/layout/paragraph-fit.mjs +191 -0
- package/dist/layout/preparation.mjs +373 -0
- package/dist/layout/prepared-paragraph.mjs +223 -0
- package/dist/layout/typography.mjs +345 -111
- package/dist/packages/rapier-diff/index.mjs +288 -196
- package/dist/spec/document-fonts.mjs +13 -4
- package/dist/spec/md-assets.mjs +149 -30
- package/dist/spec/md-layout.mjs +5 -2
- package/dist/spec/md-marks.mjs +218 -3
- package/dist/spec/md-source.mjs +122 -0
- package/package.json +2 -1
- package/style.css +9 -0
- package/dist/agent/vendor/pretext/bidi.js +0 -151
- package/dist/agent/vendor/pretext/generated/bidi-data.js +0 -3831
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
+
import {typography} from '../spec/md-marks.mjs';
|
|
2
3
|
const RAPIER_SANITIZE_FORBID_TAGS = Object.freeze(['form', 'style', 'dialog', 'template', 'iframe']);
|
|
3
4
|
const RAPIER_SANITIZE_FORBID_ATTR = Object.freeze(['action', 'formaction', 'form', 'for']);
|
|
4
5
|
// The document profiles use one allowlist in a browser and in an inert server parser.
|
|
@@ -166,7 +167,7 @@ function createInertHtmlSanitizer({parseFragment, serialize, sanitizeHtml, safeR
|
|
|
166
167
|
if (!_rapierImageDataAttributeAllowed(tag, key, compact, safeRasterDataUrl)) continue;
|
|
167
168
|
} else if (!uri.test(compact)) continue;
|
|
168
169
|
}
|
|
169
|
-
attribs[name] = value;
|
|
170
|
+
attribs[name] = selected === 'raw' && tag === 'span' && key === 'style' ? typography.style(typography.parseStyle(value)) : value;
|
|
170
171
|
}
|
|
171
172
|
if ((tag === 'a' || tag === 'area') && attribs.target?.trim().toLowerCase() === '_blank') attribs.rel = 'noopener noreferrer';
|
|
172
173
|
return {tagName, attribs};
|
|
@@ -323,6 +324,11 @@ function _rapierInstallSanitizeHooks() {
|
|
|
323
324
|
});
|
|
324
325
|
DOMPurify.addHook('uponSanitizeAttribute', (node, data) => {
|
|
325
326
|
const value = String(data.attrValue == null ? '' : data.attrValue);
|
|
327
|
+
if (_rapierSanitizeRuntime.context === 'raw' && node?.nodeName === 'SPAN' && data.attrName?.toLowerCase() === 'style') {
|
|
328
|
+
data.attrValue = typography.style(typography.parseStyle(value));
|
|
329
|
+
data.keepAttr = !!data.attrValue;
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
326
332
|
// A document's own HTML never carries an id the chrome owns, remote content allowed or not: the
|
|
327
333
|
// allow is about what the page fetches, and this rule is about what the page's chrome finds.
|
|
328
334
|
if (String(data.attrName || '').toLowerCase() === 'id' && _rapierSanitizeRuntime.context === 'raw' && _rapierChromeOwnsId(value)) { data.keepAttr = false; return; }
|
|
@@ -371,6 +377,16 @@ function _rapierInstallSanitizeHooks() {
|
|
|
371
377
|
}
|
|
372
378
|
|
|
373
379
|
function sanitizeRapierHtml(value, context = 'render') {
|
|
380
|
+
return sanitize(value, context, false);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// The same parser, profile and hooks admit both results. A private renderer can
|
|
384
|
+
// adopt the sanitized nodes without serializing and parsing them again.
|
|
385
|
+
function sanitizeRapierFragment(value, context = 'render') {
|
|
386
|
+
return sanitize(value, context, true);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function sanitize(value, context, fragment) {
|
|
374
390
|
// 'export' is the render profile under the export hooks; 'raw' is a document's own HTML (initMarkdownIt, html_block):
|
|
375
391
|
// the render profile without data-* attributes, which Rapier's delegated handlers read as their own controls, and
|
|
376
392
|
// (the hook) without an id the chrome owns. 'source' is Markdown source HTML, written and never shown: the render
|
|
@@ -389,6 +405,8 @@ function sanitizeRapierHtml(value, context = 'render') {
|
|
|
389
405
|
DOMPurify.addHook('uponSanitizeAttribute', diagramLabelAttribute);
|
|
390
406
|
DOMPurify.addHook('afterSanitizeAttributes', diagramLabelContained);
|
|
391
407
|
}
|
|
408
|
+
if (fragment) options = {...options, RETURN_DOM_FRAGMENT: true};
|
|
409
|
+
const previous = _rapierSanitizeRuntime.context;
|
|
392
410
|
_rapierSanitizeRuntime.context = selected;
|
|
393
411
|
try {
|
|
394
412
|
return DOMPurify.sanitize(value == null ? '' : String(value), options);
|
|
@@ -398,7 +416,7 @@ function sanitizeRapierHtml(value, context = 'render') {
|
|
|
398
416
|
DOMPurify.removeHook('uponSanitizeAttribute', diagramLabelAttribute);
|
|
399
417
|
DOMPurify.removeHook('afterSanitizeAttributes', diagramLabelContained);
|
|
400
418
|
}
|
|
401
|
-
_rapierSanitizeRuntime.context =
|
|
419
|
+
_rapierSanitizeRuntime.context = previous;
|
|
402
420
|
}
|
|
403
421
|
}
|
|
404
422
|
|
|
@@ -472,7 +490,7 @@ function _rapierStyleWithoutRemoteUrls(value) {
|
|
|
472
490
|
if (!raw) return raw;
|
|
473
491
|
const declaration = _rapierCssDeclaration();
|
|
474
492
|
declaration.cssText = raw;
|
|
475
|
-
if (!declaration.length) return
|
|
493
|
+
if (!declaration.length) return typography.style(typography.parseStyle(raw));
|
|
476
494
|
return _rapierDropRemoteDeclarations(declaration) ? declaration.cssText : raw;
|
|
477
495
|
}
|
|
478
496
|
|
|
@@ -510,6 +528,8 @@ function _rapierVerifyRasterBytes(bytes, mime) {
|
|
|
510
528
|
return false;
|
|
511
529
|
}
|
|
512
530
|
|
|
513
|
-
return {_rapierVerifyRasterBytes, _rapierInstallSanitizeHooks, sanitizeRapierHtml,
|
|
531
|
+
return {_rapierVerifyRasterBytes, _rapierInstallSanitizeHooks, sanitizeRapierHtml,
|
|
532
|
+
sanitizeRapierFragment: typeof DOMPurify?.sanitize === 'function' ? sanitizeRapierFragment : undefined,
|
|
533
|
+
diagramLabelStyle, escapeRapierHtmlText, _rapierChromeOwnsId, _rapierSafeRasterDataUrl, _rapierCssPresentationIsRemote, _rapierRemoteSubresourceOrigin, _rapierStyleWithoutRemoteUrls, _rapierStylesheetWithoutRemoteUrls, _rapierDropRemoteRules};
|
|
514
534
|
}
|
|
515
535
|
export {RAPIER_SANITIZE_FORBID_TAGS as forbidTags, RAPIER_SANITIZE_FORBID_ATTR as forbidAttributes, createRenderSanitizer, createInertHtmlSanitizer, RAPIER_SANITIZE_OPTIONS as options, prepareMathSource};
|
package/dist/kit/render-work.mjs
CHANGED
|
@@ -7,6 +7,7 @@ export function finish(iterator) {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function check(work) {
|
|
10
|
+
work?.check?.();
|
|
10
11
|
if (work?.signal?.aborted) throw Object.assign(new Error('The operation was cancelled.'), {name: 'AbortError', code: 'cancelled'});
|
|
11
12
|
}
|
|
12
13
|
|
|
@@ -72,16 +73,18 @@ export function htmlBoundary() {
|
|
|
72
73
|
|
|
73
74
|
export async function finishAsync(iterator, work) {
|
|
74
75
|
let began = performance.now(), next;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
76
|
+
try {
|
|
77
|
+
do {
|
|
78
|
+
check(work);
|
|
79
|
+
next = iterator.next();
|
|
80
|
+
if (!next.done && performance.now() - began >= 8) {
|
|
81
|
+
await pause(work);
|
|
82
|
+
began = performance.now();
|
|
83
|
+
}
|
|
84
|
+
} while (!next.done);
|
|
85
|
+
check(work);
|
|
86
|
+
return next.value;
|
|
87
|
+
} finally { if (!next?.done) iterator.return?.(); }
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
export function* cloneTree(root) {
|
|
@@ -98,25 +101,98 @@ export function* cloneTree(root) {
|
|
|
98
101
|
return copy;
|
|
99
102
|
}
|
|
100
103
|
|
|
101
|
-
|
|
104
|
+
// Null entries are traversal checkpoints, never fragment boundaries. A large
|
|
105
|
+
// nested unit is serialized cooperatively but is still admitted as one complete
|
|
106
|
+
// HTML/namespace unit by the synchronous parser and sanitizer.
|
|
107
|
+
export function* htmlParts(root, limit = 16384, consume = false) {
|
|
108
|
+
limit = Math.max(2, limit);
|
|
102
109
|
const text = root.ownerDocument.createElement('div');
|
|
110
|
+
const small = node => {
|
|
111
|
+
let count = 0, chars = 0, at = node;
|
|
112
|
+
while (at) {
|
|
113
|
+
if (++count > 128) return false;
|
|
114
|
+
if (at.nodeType === 1) for (const attribute of at.attributes) chars += attribute.value.length + attribute.name.length;
|
|
115
|
+
else chars += at.nodeValue?.length || 0;
|
|
116
|
+
if (chars > limit) return false;
|
|
117
|
+
if (at.firstChild) at = at.firstChild;
|
|
118
|
+
else {
|
|
119
|
+
while (at !== node && !at.nextSibling) at = at.parentNode;
|
|
120
|
+
at = at === node ? null : at.nextSibling;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
};
|
|
125
|
+
const nativeHtml = node => {
|
|
126
|
+
if (node.nodeType === 1) return node.outerHTML;
|
|
127
|
+
text.replaceChildren(node.cloneNode(false)); return text.innerHTML;
|
|
128
|
+
};
|
|
129
|
+
const nodeHtml = function* (node) {
|
|
130
|
+
if (small(node)) return nativeHtml(node);
|
|
131
|
+
const pieces = [], stack = [];
|
|
132
|
+
let at = node;
|
|
133
|
+
while (at) {
|
|
134
|
+
yield null;
|
|
135
|
+
if (at.nodeType === 1) {
|
|
136
|
+
if (small(at)) pieces.push(at.outerHTML);
|
|
137
|
+
else {
|
|
138
|
+
// Native shallow serialization owns tag/attribute spelling and
|
|
139
|
+
// escaping. Native text serialization below keeps its parent context.
|
|
140
|
+
const empty = at.cloneNode(false);
|
|
141
|
+
// A foreign element with children must retain its native closing tag
|
|
142
|
+
// even in an inert DOM that self-closes an empty SVG element.
|
|
143
|
+
if (at.firstChild) empty.appendChild(root.ownerDocument.createTextNode(''));
|
|
144
|
+
const shell = empty.outerHTML, close = /<\/[^<>]+>$/.exec(shell)?.[0] || '';
|
|
145
|
+
if (close && at.firstChild) {
|
|
146
|
+
pieces.push(shell.slice(0, -close.length));
|
|
147
|
+
stack.push({node: at, close}); at = at.firstChild; continue;
|
|
148
|
+
}
|
|
149
|
+
pieces.push(shell);
|
|
150
|
+
}
|
|
151
|
+
} else if (at.nodeType === 3 && at.nodeValue.length > limit) {
|
|
152
|
+
const parent = at !== node && at.parentNode?.nodeType === 1 ? at.parentNode.cloneNode(false) : text;
|
|
153
|
+
const value = at.nodeValue;
|
|
154
|
+
for (let start = 0; start < value.length;) {
|
|
155
|
+
let end = Math.min(start + limit, value.length);
|
|
156
|
+
if (end < value.length && value.charCodeAt(end - 1) >= 0xd800 && value.charCodeAt(end - 1) <= 0xdbff) end--;
|
|
157
|
+
parent.replaceChildren(root.ownerDocument.createTextNode(value.slice(start, end)));
|
|
158
|
+
pieces.push(parent.innerHTML); start = end; yield null;
|
|
159
|
+
}
|
|
160
|
+
parent.replaceChildren();
|
|
161
|
+
} else if (at.nodeType === 3 && at.parentNode?.nodeType === 1) {
|
|
162
|
+
const parent = at.parentNode.cloneNode(false);
|
|
163
|
+
parent.appendChild(at.cloneNode(false)); pieces.push(parent.innerHTML);
|
|
164
|
+
} else pieces.push(nativeHtml(at));
|
|
165
|
+
while (at !== node && !at.nextSibling) { const frame = stack.pop(); pieces.push(frame.close); at = frame.node; }
|
|
166
|
+
at = at === node ? null : at.nextSibling;
|
|
167
|
+
}
|
|
168
|
+
return pieces.join('');
|
|
169
|
+
};
|
|
103
170
|
let part = '';
|
|
104
|
-
for (let node = root.firstChild; node;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
else { text.replaceChildren(node.cloneNode(false)); html = text.innerHTML; }
|
|
171
|
+
for (let node = root.firstChild; node;) {
|
|
172
|
+
const next = node.nextSibling;
|
|
173
|
+
const html = yield* nodeHtml(node);
|
|
108
174
|
if (part && part.length + html.length > limit) { yield part; part = ''; }
|
|
109
175
|
part += html;
|
|
176
|
+
if (consume) root.removeChild(node);
|
|
177
|
+
node = next;
|
|
110
178
|
}
|
|
111
179
|
if (part) yield part;
|
|
112
180
|
}
|
|
113
181
|
|
|
114
|
-
export function
|
|
182
|
+
export function admitHtml(root, html, sanitize, fragment = null, context = 'export') {
|
|
183
|
+
if (fragment) root.appendChild(fragment(html, context));
|
|
184
|
+
else {
|
|
185
|
+
// Inert string-only hosts retain their explicitly supported admission path.
|
|
186
|
+
const part = root.ownerDocument.createElement('div');
|
|
187
|
+
part.innerHTML = sanitize(html, context);
|
|
188
|
+
while (part.firstChild) root.appendChild(part.firstChild);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function* cleanTree(root, sanitize, context = 'export', fragment = null) {
|
|
115
193
|
const clean = root.ownerDocument.createElement('div');
|
|
116
|
-
const part = root.ownerDocument.createElement('div');
|
|
117
194
|
for (const html of htmlParts(root)) {
|
|
118
|
-
|
|
119
|
-
while (part.firstChild) clean.appendChild(part.firstChild);
|
|
195
|
+
if (html !== null) admitHtml(clean, html, sanitize, fragment, context);
|
|
120
196
|
yield;
|
|
121
197
|
}
|
|
122
198
|
return clean;
|
|
@@ -124,7 +200,10 @@ export function* cleanTree(root, sanitize, context = 'export') {
|
|
|
124
200
|
|
|
125
201
|
export function* serializeTree(root, sanitize = null, trim = false) {
|
|
126
202
|
const parts = [];
|
|
127
|
-
for (const html of htmlParts(root)) {
|
|
203
|
+
for (const html of htmlParts(root)) {
|
|
204
|
+
if (html !== null) parts.push(sanitize ? sanitize(html, 'export') : html);
|
|
205
|
+
yield;
|
|
206
|
+
}
|
|
128
207
|
const result = parts.join('');
|
|
129
208
|
return trim ? result.trim() : result;
|
|
130
209
|
}
|
package/dist/kit/render.mjs
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
import {htmlImageRecord, mountHtmlBlocks} from '../spec/html-blocks.mjs';
|
|
3
3
|
import {finish, finishAsync, cloneTree, cleanTree, serializeTree, encodeUtf8Steps, pause} from './render-work.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import {fontFamilyCss} from '../spec/md-marks.mjs';
|
|
5
5
|
import {readFontAssets, documentFontsCss} from '../spec/document-fonts.mjs';
|
|
6
6
|
import {fontRequests, requireDocumentFonts} from './export-fonts.mjs';
|
|
7
7
|
import {documentLanguage, documentSettingsFonts} from '../spec/document-settings.mjs';
|
|
8
|
-
import {createTypography} from '../layout/typography.mjs';
|
|
9
8
|
import {createLineMeasurer, linesHeight} from '../layout/model.mjs';
|
|
10
9
|
import {markPrintCapitals, createPrintCapitals, followingPrintProse} from './print-capitals.mjs';
|
|
11
10
|
import {freezePublishedInk} from './render-ink.mjs';
|
|
@@ -158,8 +157,8 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
158
157
|
styledRoot.setAttribute('lang', language);
|
|
159
158
|
if (completePage && typeof _rapierFinalizeExportCode === 'function') await _rapierFinalizeExportCode(styledRoot);
|
|
160
159
|
// _rapierProjectStyledRoot already returns the sanitized projection.
|
|
161
|
-
// Print
|
|
162
|
-
//
|
|
160
|
+
// Print runs shared paragraph typography, but keeps its existing picture layout;
|
|
161
|
+
// picture-shape annotations have no consumer in that artifact.
|
|
163
162
|
if (!opts.print) _rapierAnnotateExportBoxPolygons(styledRoot);
|
|
164
163
|
if (completePage && !opts.print) {
|
|
165
164
|
if (work) {
|
|
@@ -173,6 +172,7 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
173
172
|
? 'light'
|
|
174
173
|
: _rapierArtifactPreference('theme', ['system', 'light', 'dark']);
|
|
175
174
|
const highlights = _rapierArtifactPreference('highlights', ['accent', 'standard']);
|
|
175
|
+
const invertImageAlpha = _rapierArtifactPreference('invertImageAlpha', ['true', 'false']) !== 'false';
|
|
176
176
|
const versionMeta = document.querySelector('meta[name="rapier-version"]');
|
|
177
177
|
const version = versionMeta && versionMeta.content ? versionMeta.content.trim() : '0.0.0';
|
|
178
178
|
const bodyClass = theme === 'light' ? 'light' : '';
|
|
@@ -225,12 +225,11 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
225
225
|
// The reflow script is written first, because whether it exists decides the policy. A page that carries no script says so: script-src 'none' refuses every
|
|
226
226
|
// script outright. Declaring a nonce a page has no use for is a widening, small but real -- anything that could inject markup into the file could read the
|
|
227
227
|
// nonce out of it and be admitted.
|
|
228
|
-
const layoutScript =
|
|
228
|
+
const layoutScript = opts.scripts !== false ? _rapierArtifactLayoutScript(styledRoot, nonce, {pictures:!opts.print}) : '';
|
|
229
229
|
const inkScript = !opts.print && opts.scripts !== false ? _rapierArtifactInkScript(styledRoot, nonce) : '';
|
|
230
|
-
const typographyScript = opts.scripts !== false ? _rapierArtifactTypographyScript(styledRoot, nonce) : '';
|
|
231
230
|
const htmlScript = opts.scripts !== false && styledRoot.querySelector('[data-rapier-html]') ? '<script nonce="' + nonce + '">(' + mountHtmlBlocks.toString().replace(/<\/script/gi, '<\\/script') + ')(document);</script>' : '';
|
|
232
231
|
const page = '<!DOCTYPE html>\n'
|
|
233
|
-
+ '<html lang="' + escapeRapierHtmlText(language) + '" data-rapier-theme="' + theme + '" data-highlights="' + highlights + '">\n'
|
|
232
|
+
+ '<html lang="' + escapeRapierHtmlText(language) + '" data-rapier-theme="' + theme + '" data-highlights="' + highlights + '" data-invert-image-alpha="' + (invertImageAlpha ? 'on' : 'off') + '">\n'
|
|
234
233
|
+ '<head>\n<meta charset="UTF-8">\n' + (carrier ? RapierPageReturnAddress.PAGE_SEED + '\n' : '')
|
|
235
234
|
+ '<meta name="viewport" content="width=device-width, initial-scale=1">\n'
|
|
236
235
|
// Chrome 154's responsively sized frames: a host frame styled `frame-sizing: content-height` sizes
|
|
@@ -238,14 +237,14 @@ async function _rapierBuildArtifact(options, providedContext) {
|
|
|
238
237
|
// someone's page shows whole, with no inner scroll. Any origin may read the height: the page is the
|
|
239
238
|
// document, published by the person who exported it.
|
|
240
239
|
+ '<meta name="responsive-embedded-sizing" content="allow-origins=*">\n'
|
|
241
|
-
+ '<meta http-equiv="Content-Security-Policy" content="' + _rapierExportedPageCsp(layoutScript || inkScript ||
|
|
240
|
+
+ '<meta http-equiv="Content-Security-Policy" content="' + _rapierExportedPageCsp(layoutScript || inkScript || htmlScript ? nonce : '', includeFonts || !!fontCss || !!factoryFontCss) + '">\n'
|
|
242
241
|
+ '<meta name="referrer" content="no-referrer">\n'
|
|
243
242
|
+ '<link rel="icon" href="data:,">\n'
|
|
244
243
|
+ '<meta name="generator" content="Rapier ' + escapeRapierHtmlText(version) + '">\n'
|
|
245
244
|
+ '<title>' + escapeRapierHtmlText(pageTitle) + '</title>\n'
|
|
246
245
|
+ '<style>' + css + '</style>\n</head>\n'
|
|
247
246
|
+ '<body class="' + bodyClass + '"><main class="' + docClass + '" data-md-theme="' + theme + '"' + (settings?.space ? ' data-md-space' : '') + '>'
|
|
248
|
-
+ bodyHtml + '</main>' + carrier + layoutScript + inkScript +
|
|
247
|
+
+ bodyHtml + '</main>' + carrier + layoutScript + inkScript + htmlScript + '</body>\n</html>';
|
|
249
248
|
|
|
250
249
|
return {
|
|
251
250
|
html: page,
|
|
@@ -1381,17 +1380,10 @@ function _rapierImageOpenRunsList(tokens) {
|
|
|
1381
1380
|
const stack = [];
|
|
1382
1381
|
for (const token of block.children) {
|
|
1383
1382
|
if (token.type === 'image') { list.push(stack.slice()); continue; }
|
|
1384
|
-
if (token.
|
|
1385
|
-
|
|
1386
|
-
stack.push({open: _rapierFormatColorOpen(hex), close: RAPIER_COLOR_CLOSE});
|
|
1387
|
-
continue;
|
|
1388
|
-
}
|
|
1389
|
-
if (token.type === 'rapier_color_close') { stack.pop(); continue; }
|
|
1390
|
-
if (token.type === 'rapier_font_open') {
|
|
1391
|
-
stack.push({open: token.meta?.mdFont?.source || formatFontOpen(token.attrGet('data-md-font')), close: FONT_CLOSE});
|
|
1392
|
-
continue;
|
|
1383
|
+
if (token.meta?.typography && token.nesting === 1) {
|
|
1384
|
+
stack.push({open: token.meta.typography.open, close: token.meta.typography.close}); continue;
|
|
1393
1385
|
}
|
|
1394
|
-
if (token.
|
|
1386
|
+
if (token.meta?.typography && token.nesting === -1) { stack.pop(); continue; }
|
|
1395
1387
|
if (token.type === 'strong_open' || token.type === 'em_open' || token.type === 's_open') {
|
|
1396
1388
|
stack.push({open: token.markup, close: token.markup});
|
|
1397
1389
|
continue;
|
|
@@ -1407,10 +1399,11 @@ function _rapierArtifactFactoryModules(dependencies, group) {
|
|
|
1407
1399
|
return paths.map(path => 'modules[' + JSON.stringify(path) + '] = (' + Function.prototype.toString.call(dependencies.factories[path]) + ')();').join('\n');
|
|
1408
1400
|
}
|
|
1409
1401
|
|
|
1410
|
-
function _rapierProjectArtifactLayout(root, metadata, geometry,
|
|
1411
|
-
if (!root || !metadata || !geometry || !
|
|
1402
|
+
function _rapierProjectArtifactLayout(root, metadata, geometry, preparation) {
|
|
1403
|
+
if (!root || !metadata || !geometry || !preparation) return null;
|
|
1412
1404
|
const doc = root.ownerDocument, view = doc.defaultView;
|
|
1413
1405
|
const styles = new Map(), originals = new Map(), spacers = [], floats = [], steps = [], profiles = new WeakMap();
|
|
1406
|
+
const prepared = preparation.createPreparationScope(doc);
|
|
1414
1407
|
const css = node => view.getComputedStyle(node);
|
|
1415
1408
|
const box = node => node.getBoundingClientRect();
|
|
1416
1409
|
const pixels = value => (Number.isFinite(value) ? Math.round(value * 100) / 100 : 0) + 'px';
|
|
@@ -1437,48 +1430,72 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1437
1430
|
steps.length = 0;
|
|
1438
1431
|
}
|
|
1439
1432
|
|
|
1440
|
-
//
|
|
1433
|
+
// One prepared rich paragraph retains original item text. Unsupported shaping keeps native flow.
|
|
1441
1434
|
function prepare(paragraph) {
|
|
1435
|
+
const refuse = () => { prepared.forget(paragraph); return null; };
|
|
1442
1436
|
if (!/^(P|H[1-6])$/.test(paragraph.tagName) || !paragraph.textContent.trim() || paragraph.textContent.length > 8192 ||
|
|
1443
|
-
paragraph.
|
|
1437
|
+
[...paragraph.querySelectorAll('[id]')].some(element => /^[\s\u00ad\u200b]*$/u.test(element.textContent)) ||
|
|
1438
|
+
paragraph.querySelector('img,svg,math,br,input,button,iframe,canvas,.math-rendered')) return refuse();
|
|
1444
1439
|
const computed = css(paragraph);
|
|
1445
|
-
|
|
1446
|
-
|
|
1440
|
+
const language = paragraph.closest('[lang]')?.getAttribute('lang') || '';
|
|
1441
|
+
for (let parent = paragraph; parent; parent = parent.parentElement) {
|
|
1442
|
+
const inherited = css(parent);
|
|
1443
|
+
if (parent.style.textWrapStyle === 'pretty' || inherited.transform !== 'none' ||
|
|
1444
|
+
!['1', 'normal'].includes(inherited.zoom)) return refuse();
|
|
1445
|
+
}
|
|
1446
|
+
if (computed.textAlign === 'justify' || computed.textWrapStyle === 'balance' ||
|
|
1447
|
+
!preparation.supportsRichStyle(computed, language) || computed.whiteSpace !== 'normal' ||
|
|
1448
|
+
computed.transform !== 'none' ||
|
|
1449
|
+
['::before', '::after'].some(pseudo => !['none', 'normal'].includes(view.getComputedStyle(paragraph, pseudo).content)) ||
|
|
1447
1450
|
// Lines start at the padding box's top-left: left, right or top inset is refused.
|
|
1448
1451
|
['paddingLeft', 'paddingRight', 'paddingTop', 'borderLeftWidth', 'borderRightWidth', 'borderTopWidth']
|
|
1449
|
-
.some(key => parseFloat(computed[key]) > 0)) return
|
|
1452
|
+
.some(key => parseFloat(computed[key]) > 0)) return refuse();
|
|
1450
1453
|
const runs = [], items = [], walker = doc.createTreeWalker(paragraph, 4);
|
|
1451
1454
|
for (let node; (node = walker.nextNode());) {
|
|
1452
|
-
if (runs.length === 192) return
|
|
1455
|
+
if (runs.length === 192) return refuse();
|
|
1453
1456
|
const parents = [];
|
|
1454
1457
|
for (let parent = node.parentElement; parent !== paragraph; parent = parent.parentElement) {
|
|
1455
|
-
if (!parent || !/^(A|SPAN|EM|I|STRONG|B|DEL|S|MARK|CODE|U|INS|ABBR|SUP|SUB)$/.test(parent.tagName)
|
|
1458
|
+
if (!parent || !/^(A|SPAN|EM|I|STRONG|B|DEL|S|MARK|CODE|KBD|SAMP|U|INS|ABBR|SUP|SUB)$/.test(parent.tagName) ||
|
|
1459
|
+
parent.hidden || parent.getAttribute('aria-hidden') === 'true' ||
|
|
1460
|
+
parent.style.textWrapStyle === 'pretty' ||
|
|
1461
|
+
parent.hasAttribute('lang') && parent.getAttribute('lang').toLowerCase() !== (language || '').toLowerCase()) return refuse();
|
|
1462
|
+
const parentStyle = css(parent);
|
|
1463
|
+
if (!preparation.supportsRichStyle(parentStyle, language) || parentStyle.display !== 'inline' ||
|
|
1464
|
+
parentStyle.cssFloat !== 'none' || !['static', 'relative'].includes(parentStyle.position) ||
|
|
1465
|
+
parentStyle.transform !== 'none' || !['1', 'normal'].includes(parentStyle.zoom) ||
|
|
1466
|
+
['::before', '::after'].some(pseudo => !['none', 'normal'].includes(view.getComputedStyle(parent, pseudo).content))) return refuse();
|
|
1467
|
+
if (!/^(CODE|KBD|SAMP)$/.test(parent.tagName) &&
|
|
1468
|
+
['paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth', 'marginLeft', 'marginRight']
|
|
1469
|
+
.some(key => Math.abs(parseFloat(parentStyle[key]) || 0) > .01)) return refuse();
|
|
1456
1470
|
parents.unshift(parent);
|
|
1457
1471
|
}
|
|
1458
1472
|
const computedRun = css(node.parentElement);
|
|
1459
|
-
if (
|
|
1473
|
+
if (!preparation.supportsRichStyle(computedRun, language) ||
|
|
1474
|
+
computedRun.whiteSpace !== computed.whiteSpace && computedRun.whiteSpace !== 'nowrap') return refuse();
|
|
1460
1475
|
const font = `${computedRun.fontStyle} ${computedRun.fontWeight} ${computedRun.fontSize} ${computedRun.fontFamily}`;
|
|
1461
1476
|
const letterSpacing = parseFloat(computedRun.letterSpacing) || 0;
|
|
1462
|
-
const
|
|
1463
|
-
if (
|
|
1464
|
-
// A chip's box is repeated by each piece of it, and it breaks as the browser breaks inline code, as the editor's projection does.
|
|
1465
|
-
const boxed = parents.some(parent => parent.tagName === 'CODE');
|
|
1477
|
+
const boxed = parents.some(parent => /^(CODE|KBD|SAMP)$/.test(parent.tagName));
|
|
1478
|
+
if (boxed && (!/^(CODE|KBD|SAMP)$/.test(node.parentElement.tagName) || node.parentElement.childNodes.length !== 1)) return refuse();
|
|
1466
1479
|
const extraWidth = boxed ? ['paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth', 'marginLeft', 'marginRight']
|
|
1467
1480
|
.reduce((width, key) => width + (parseFloat(computedRun[key]) || 0), 0) : 0;
|
|
1468
|
-
runs.push({parents,
|
|
1469
|
-
|
|
1481
|
+
runs.push({parents, raw: node.data, font, letterSpacing,
|
|
1482
|
+
directions: parents.map(parent => parent.getAttribute('dir') === 'auto' ? css(parent).direction : null)});
|
|
1483
|
+
items.push({text: node.data, font, letterSpacing,
|
|
1484
|
+
break: computedRun.whiteSpace === 'nowrap' || parents.some(parent => parent.hasAttribute('data-rapier-source')) ? 'never' : 'normal', extraWidth});
|
|
1470
1485
|
}
|
|
1471
|
-
if (!runs.length) return
|
|
1486
|
+
if (!runs.length) return refuse();
|
|
1487
|
+
const options = {whiteSpace: computed.whiteSpace, wordBreak: computed.wordBreak, language};
|
|
1488
|
+
const flow = prepared.prepare(paragraph, items, options);
|
|
1489
|
+
if (!flow) return refuse();
|
|
1472
1490
|
const fontSize = parseFloat(computed.fontSize) || 16;
|
|
1473
|
-
return {paragraph, runs, flow:
|
|
1491
|
+
return {paragraph, runs, flow, whiteSpace: computed.whiteSpace, direction: computed.direction,
|
|
1474
1492
|
align: computed.textAlign, fontSize, lineHeight: parseFloat(computed.lineHeight) || fontSize * 1.65,
|
|
1475
|
-
balance:
|
|
1493
|
+
balance: 0};
|
|
1476
1494
|
}
|
|
1477
1495
|
|
|
1478
1496
|
// An id survives on the first fragment only.
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
const run = record.runs[fragment.itemIndex], mapped = geometry.mapFragment(run?.prepared, fragment);
|
|
1497
|
+
function fragmentNode(record, fragment, idsKept) {
|
|
1498
|
+
const run = record.runs[fragment.itemIndex], mapped = geometry.mapFragment(run, fragment, {whiteSpace: record.whiteSpace});
|
|
1482
1499
|
if (!mapped) return null;
|
|
1483
1500
|
// A line ends on the white space it broke at. A chip, a link or a highlight must not paint that space, so it stands after the shell.
|
|
1484
1501
|
const kept = run.parents.length ? mapped.text.replace(/[ \t\n\r\f]+$/, '') : mapped.text;
|
|
@@ -1497,10 +1514,12 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1497
1514
|
}
|
|
1498
1515
|
|
|
1499
1516
|
function project(record, width, top, obstacles) {
|
|
1517
|
+
if (!prepared.current(record.flow)) return false;
|
|
1500
1518
|
const plan = geometry.flowLines(record.flow, width, top, obstacles, record.lineHeight,
|
|
1501
1519
|
metadata.wrapColumnFloor(record.fontSize), record.direction, record.balance, record.align);
|
|
1502
1520
|
if (!plan) return false;
|
|
1503
1521
|
const output = doc.createDocumentFragment();
|
|
1522
|
+
const idsKept = new Set();
|
|
1504
1523
|
let previous = null;
|
|
1505
1524
|
for (const line of plan.lines) {
|
|
1506
1525
|
const element = doc.createElement('span');
|
|
@@ -1508,17 +1527,21 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1508
1527
|
element.style.textAlign = ['left', 'center', 'right', 'start', 'end'].includes(record.align)
|
|
1509
1528
|
? record.align : record.direction === 'rtl' ? 'right' : 'left';
|
|
1510
1529
|
for (const fragment of line.fragments) {
|
|
1511
|
-
const child = fragmentNode(record, fragment);
|
|
1530
|
+
const child = fragmentNode(record, fragment, idsKept);
|
|
1512
1531
|
if (!child) return false;
|
|
1513
1532
|
if (hasGap(record, previous, fragment)) {
|
|
1514
1533
|
const gap = doc.createElement('span');
|
|
1534
|
+
const gapRun = record.runs[fragment.gapItemIndex];
|
|
1535
|
+
if (!gapRun && fragment.gapBefore !== 0) return false;
|
|
1515
1536
|
gap.style.cssText = 'display:inline-block;width:' + pixels(fragment.gapBefore);
|
|
1537
|
+
if (gapRun) { gap.style.font = gapRun.font; gap.style.letterSpacing = pixels(gapRun.letterSpacing); }
|
|
1516
1538
|
gap.textContent = ' '; element.append(gap);
|
|
1517
1539
|
}
|
|
1518
1540
|
element.append(child); previous = fragment;
|
|
1519
1541
|
}
|
|
1520
1542
|
output.append(element);
|
|
1521
1543
|
}
|
|
1544
|
+
if (!prepared.current(record.flow)) return false;
|
|
1522
1545
|
originals.set(record.paragraph, [...record.paragraph.childNodes]);
|
|
1523
1546
|
record.paragraph.replaceChildren(output);
|
|
1524
1547
|
style(record.paragraph, {position: 'relative', height: pixels(plan.height), 'min-height': '0'});
|
|
@@ -1529,10 +1552,11 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1529
1552
|
if (!previous) return false;
|
|
1530
1553
|
let found = false;
|
|
1531
1554
|
for (let index = previous.itemIndex; index <= current.itemIndex; index++) {
|
|
1532
|
-
const run = record.runs[index]
|
|
1533
|
-
|
|
1534
|
-
const
|
|
1535
|
-
|
|
1555
|
+
const run = record.runs[index];
|
|
1556
|
+
if (!run) return false;
|
|
1557
|
+
const start = index === previous.itemIndex ? previous.sourceEnd : 0;
|
|
1558
|
+
const end = index === current.itemIndex ? current.sourceStart : run.raw.length;
|
|
1559
|
+
if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end < start || end > run.raw.length) return false;
|
|
1536
1560
|
const gap = run.raw.slice(start, end);
|
|
1537
1561
|
if (gap && !/^[ \t\n\r\f]+$/.test(gap)) return false;
|
|
1538
1562
|
found ||= !!gap;
|
|
@@ -1823,6 +1847,7 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1823
1847
|
|
|
1824
1848
|
function beforePrint() { printing = true; restore(); }
|
|
1825
1849
|
function afterPrint() { printing = false; schedule(); }
|
|
1850
|
+
function fontChanged() { prepared.invalidateFonts(); schedule(); }
|
|
1826
1851
|
|
|
1827
1852
|
const observer = typeof view.ResizeObserver === 'function' ? new view.ResizeObserver(() => {
|
|
1828
1853
|
const width = root.clientWidth;
|
|
@@ -1837,13 +1862,13 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1837
1862
|
view.addEventListener('pageshow', schedule);
|
|
1838
1863
|
view.addEventListener('beforeprint', beforePrint);
|
|
1839
1864
|
view.addEventListener('afterprint', afterPrint);
|
|
1840
|
-
doc.fonts?.addEventListener?.(
|
|
1865
|
+
for (const event of ['loading', 'loadingdone', 'loadingerror']) doc.fonts?.addEventListener?.(event, fontChanged);
|
|
1841
1866
|
Promise.resolve(doc.fonts?.ready).then(schedule, schedule);
|
|
1842
1867
|
schedule();
|
|
1843
|
-
return Object.freeze({reflow, destroy() {
|
|
1868
|
+
return Object.freeze({reflow, destroy({documentChanged = false} = {}) {
|
|
1844
1869
|
stopped = true;
|
|
1845
1870
|
if (scheduled) view.cancelAnimationFrame(scheduled);
|
|
1846
|
-
observer?.disconnect(); restore();
|
|
1871
|
+
observer?.disconnect(); restore(); prepared.retire({documentChanged});
|
|
1847
1872
|
root.removeEventListener('load', schedule, true);
|
|
1848
1873
|
root.removeEventListener('error', schedule, true);
|
|
1849
1874
|
root.removeEventListener('rapier-html-resize', schedule);
|
|
@@ -1852,23 +1877,28 @@ function _rapierProjectArtifactLayout(root, metadata, geometry, pretext) {
|
|
|
1852
1877
|
view.removeEventListener('pageshow', schedule);
|
|
1853
1878
|
view.removeEventListener('beforeprint', beforePrint);
|
|
1854
1879
|
view.removeEventListener('afterprint', afterPrint);
|
|
1855
|
-
doc.fonts?.removeEventListener?.(
|
|
1880
|
+
for (const event of ['loading', 'loadingdone', 'loadingerror']) doc.fonts?.removeEventListener?.(event, fontChanged);
|
|
1856
1881
|
}});
|
|
1857
1882
|
}
|
|
1858
1883
|
|
|
1859
|
-
function _rapierArtifactLayoutScript(root, nonce = '') {
|
|
1884
|
+
function _rapierArtifactLayoutScript(root, nonce = '', {pictures = true} = {}) {
|
|
1860
1885
|
const dependencies = globalThis.RapierArtifactLayoutDependencies;
|
|
1861
1886
|
// Every wrap value earns the reflow script, and an inline turned raster (keyed off data-rapier-image-layout).
|
|
1862
1887
|
const layoutOf = element => globalThis.RapierMarkdownLayout.parseLayoutAttribute(element.getAttribute('data-md-layout'));
|
|
1863
1888
|
const pictureLayoutOf = image => globalThis.RapierMarkdownLayout.parseLayoutAttribute(image.getAttribute('data-rapier-image-layout'));
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1889
|
+
const wrapping = pictures && ([...root.querySelectorAll('p[data-md-layout]')].some(element => ['around', 'box', 'behind', 'front'].includes(layoutOf(element)?.wrap)) ||
|
|
1890
|
+
[...root.querySelectorAll('img[data-rapier-image-layout]')].some(image => pictureLayoutOf(image)?.rotate));
|
|
1891
|
+
const typography = _rapierArtifactTypographyScript(root);
|
|
1892
|
+
if (!wrapping && !typography) return '';
|
|
1893
|
+
if (!dependencies?.factories || typography && !dependencies.factories['layout/typography.mjs'])
|
|
1894
|
+
throw new Error('Offline typography requires the complete retained MIT layout factories');
|
|
1867
1895
|
const modules = _rapierArtifactFactoryModules(dependencies, 'layout');
|
|
1868
|
-
const script = '/* Rapier export layout: SPDX-License-Identifier:
|
|
1869
|
-
'/* Pretext
|
|
1870
|
-
'(() => {\nconst modules =
|
|
1871
|
-
'
|
|
1896
|
+
const script = '/* Rapier export layout: SPDX-License-Identifier: MIT */\n' +
|
|
1897
|
+
'/* Pretext bac1774c0f8b7a92e44b9da43b6d5443d55d0d9b (Unreleased)\n' + dependencies.license + '\n*/\n' +
|
|
1898
|
+
'(() => {\nconst modules = Object.create(null);\n' + modules +
|
|
1899
|
+
(wrapping ? '\n(' + _rapierProjectArtifactLayout.toString() +
|
|
1900
|
+
')(document.querySelector("main.rapier-page"), modules["spec/md-layout.mjs"], modules["layout/model.mjs"], modules["layout/preparation.mjs"]);\n' : '') +
|
|
1901
|
+
typography + '\n})();';
|
|
1872
1902
|
|
|
1873
1903
|
return '<script' + (nonce ? ' nonce="' + nonce + '"' : '') + '>\n' + script.replace(/<\/script/gi, '<\\/script') + '\n</script>';
|
|
1874
1904
|
}
|
|
@@ -1883,21 +1913,27 @@ function _rapierArtifactInkScript(root, nonce = '') {
|
|
|
1883
1913
|
return '<script' + (nonce ? ' nonce="' + nonce + '"' : '') + '>\n' + script.replace(/<\/script/gi, '<\\/script') + '\n</script>';
|
|
1884
1914
|
}
|
|
1885
1915
|
|
|
1886
|
-
function _rapierArtifactTypographyScript(root
|
|
1916
|
+
function _rapierArtifactTypographyScript(root) {
|
|
1887
1917
|
const pictures = root.querySelector('img[data-rapier-print-lines]');
|
|
1888
1918
|
if (!pictures && !root.querySelector('p[data-md-hyphens="auto"],p[data-md-lines],p[data-md-caps="small"],p[data-md-hanging="on"]')) return '';
|
|
1889
|
-
|
|
1919
|
+
return '(() => {\nconst typography = modules["layout/typography.mjs"].createTypography();\n' +
|
|
1890
1920
|
(pictures ? 'const capitals = (' + createPrintCapitals.toString() + ')((' + createLineMeasurer.toString() + ')(),(' + linesHeight.toString() + '),(' + followingPrintProse.toString() + '));\n' : '') +
|
|
1891
|
-
'const root = document.querySelector("main.rapier-page"); let frame = 0, width = -1, printing =
|
|
1892
|
-
|
|
1921
|
+
'const root = document.querySelector("main.rapier-page"); const printMedia = matchMedia("print"); let frame = 0, width = -1, ready = null, printing = printMedia.matches;\n' +
|
|
1922
|
+
// beforeprint can still expose the screen viewport, not the selected paper's content width.
|
|
1923
|
+
// Only an authored paper width supplies a fixed target; automatic paper keeps native wrapping.
|
|
1924
|
+
'const apply = () => { cancelAnimationFrame(frame); frame = 0; ' + (pictures ? 'if (printing) capitals.apply(root); else capitals.clear(root); ' : '') + 'if (printing && !(parseFloat(getComputedStyle(root).getPropertyValue("--rapier-print-width")) > 0)) { typography.clear(root); ready?.(); ready = null; return; } if (typography.apply(root, {all:printing}).pending) schedule(); else { ready?.(); ready = null; } };\n' +
|
|
1893
1925
|
'const schedule = () => { if (!frame) frame = requestAnimationFrame(apply); };\n' +
|
|
1894
|
-
'globalThis.__RAPIER_EXPORT_READY__ = Promise.all([document.fonts?.ready, ...Array.from(root.querySelectorAll("img"), image => image.decode?.().catch(() => {}))]).then(() => new Promise(resolve =>
|
|
1926
|
+
'globalThis.__RAPIER_EXPORT_READY__ = Promise.all([document.fonts?.ready, ...Array.from(root.querySelectorAll("img"), image => image.decode?.().catch(() => {}))]).then(() => new Promise(resolve => { ready = resolve; schedule(); }));\n' +
|
|
1927
|
+
'printMedia.addEventListener("change", () => { printing = printMedia.matches; schedule(); });\n' +
|
|
1895
1928
|
'addEventListener("resize", schedule); addEventListener("beforeprint", () => { printing = true; apply(); });\n' +
|
|
1896
|
-
'addEventListener("
|
|
1929
|
+
'addEventListener("scroll", schedule, {passive:true, capture:true});\n' +
|
|
1930
|
+
'for (const event of ["copy","cut"]) addEventListener(event, () => { if (typography.clearSelection(root)) schedule(); }, true);\n' +
|
|
1931
|
+
'addEventListener("afterprint", () => { printing = printMedia.matches; ' + (pictures ? 'capitals.clear(root); ' : '') + 'schedule(); });\n' +
|
|
1897
1932
|
'root.addEventListener("load", schedule, true);\n' +
|
|
1933
|
+
'for (const event of ["loading","loadingdone","loadingerror"]) document.fonts?.addEventListener(event, () => { typography.invalidateFonts(document); schedule(); });\n' +
|
|
1934
|
+
'addEventListener("pagehide", () => { cancelAnimationFrame(frame); frame = 0; typography.retire(root); }); addEventListener("pageshow", schedule);\n' +
|
|
1898
1935
|
'if (typeof ResizeObserver === "function") new ResizeObserver(() => { const next = root.getBoundingClientRect().width; if (next !== width) { width = next; schedule(); } }).observe(root);\n' +
|
|
1899
1936
|
'})();';
|
|
1900
|
-
return '<script' + (nonce ? ' nonce="' + nonce + '"' : '') + '>\n' + script.replace(/<\/script/gi, '<\\/script') + '\n</script>';
|
|
1901
1937
|
}
|
|
1902
1938
|
|
|
1903
1939
|
return {_rapierDataImageDestinationsAsync, _rapierSharedSourceFormsAsync, _rapierPrefixPortableAnchorsSteps, _rapierProjectStyledRootAsync, _rapierBuildArtifact, _rapierBuildPublishedPage, _rapierProjectStyledRoot, _rapierDocxRenderStyles, _rapierPaintCode, _rapierPrefixPortableAnchors, _rapierDocumentSettingsOf, _rapierDocumentSettingsCss, _rapierExportedPageCsp, _rapierArtifactNonce, _rapierAnnotateExportBoxPolygons, _rapierSharedSourceHash, _rapierSharedSourceForms, _rapierSharedSourceCarrier, _rapierSharedResolve, _rapierReadSharedDocument, _rapierSharedPageFallbackCss, _rapierShareWrapKind, _rapierSharePictureParagraph, _rapierSharedPageLayout, _rapierShareCapLongCodeBlocks, _rapierRequireOfflinePageImages, _rapierCssValueUrls, _rapierBuildSharedPage, _rapierImageSources, _rapierImageDestinations, _rapierScanMarkdownImages, _rapierParseMarkdownImageDestination, _rapierImageOpenRunsList, _rapierArtifactFactoryModules, _rapierProjectArtifactLayout, _rapierArtifactLayoutScript, _rapierArtifactInkScript , render};
|