@wdprlib/render 2.1.0 → 3.0.1
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/dist/index.cjs +2344 -1668
- package/dist/index.d.cts +15 -13
- package/dist/index.d.ts +15 -13
- package/dist/index.js +2375 -1699
- package/package.json +1 -1
- package/src/context/attributes.ts +14 -0
- package/src/context/bibliography.ts +109 -0
- package/src/context/counters.ts +51 -0
- package/src/context/image-urls.ts +31 -0
- package/src/context/index.ts +285 -0
- package/src/context/output.ts +17 -0
- package/src/context/page-urls.ts +81 -0
- package/src/context/style-slots.ts +29 -0
- package/src/context/urls.ts +2 -0
- package/src/elements/bibliography/block.ts +27 -0
- package/src/elements/bibliography/cite.ts +23 -0
- package/src/elements/bibliography/ids.ts +9 -0
- package/src/elements/bibliography/index.ts +9 -0
- package/src/elements/code/contents.ts +18 -0
- package/src/elements/code/index.ts +29 -0
- package/src/elements/collapsible/index.ts +31 -0
- package/src/elements/collapsible/labels.ts +35 -0
- package/src/elements/collapsible/link.ts +11 -0
- package/src/elements/collapsible/sections.ts +39 -0
- package/src/elements/container/attributes.ts +28 -0
- package/src/elements/container/header.ts +27 -0
- package/src/elements/container/index.ts +35 -0
- package/src/elements/container/string-container.ts +40 -0
- package/src/elements/container/string-types.ts +63 -0
- package/src/elements/container/wrappers.ts +32 -0
- package/src/elements/date/format.ts +20 -0
- package/src/elements/{date.ts → date/index.ts} +4 -29
- package/src/elements/date/output.ts +6 -0
- package/src/elements/embed/iframe.ts +8 -0
- package/src/elements/embed/index.ts +28 -0
- package/src/elements/embed/providers.ts +43 -0
- package/src/elements/embed/validation.ts +15 -0
- package/src/elements/embed-block/allowlist.ts +60 -0
- package/src/elements/embed-block/boolean-attributes.ts +38 -0
- package/src/elements/embed-block/iframe.ts +33 -0
- package/src/elements/embed-block/index.ts +31 -0
- package/src/elements/embed-block/sanitize-config.ts +22 -0
- package/src/elements/embed-block/sanitize.ts +44 -0
- package/src/elements/expr/branch.ts +29 -0
- package/src/elements/expr/index.ts +63 -0
- package/src/elements/expr/result.ts +19 -0
- package/src/elements/footnote/body.ts +11 -0
- package/src/elements/footnote/index.ts +35 -0
- package/src/elements/footnote/ref.ts +16 -0
- package/src/elements/html/attributes.ts +24 -0
- package/src/elements/html/index.ts +39 -0
- package/src/elements/html/url.ts +19 -0
- package/src/elements/iframe/attributes.ts +28 -0
- package/src/elements/iframe/index.ts +22 -0
- package/src/elements/iftags/condition.ts +42 -0
- package/src/elements/iftags/index.ts +39 -0
- package/src/elements/iftags/style-slot.ts +23 -0
- package/src/elements/iftags/tokens.ts +36 -0
- package/src/elements/image/alignment.ts +44 -0
- package/src/elements/image/attributes.ts +10 -0
- package/src/elements/image/img-attributes.ts +26 -0
- package/src/elements/image/index.ts +36 -0
- package/src/elements/image/link-href.ts +24 -0
- package/src/elements/image/link.ts +13 -0
- package/src/elements/image/source.ts +16 -0
- package/src/elements/{include.ts → include/index.ts} +5 -13
- package/src/elements/include/missing.ts +15 -0
- package/src/elements/link/anchor-name.ts +6 -0
- package/src/elements/link/anchor.ts +27 -0
- package/src/elements/link/attributes.ts +47 -0
- package/src/elements/link/index.ts +26 -0
- package/src/elements/link/label.ts +23 -0
- package/src/elements/link/target.ts +20 -0
- package/src/elements/list/attributes.ts +19 -0
- package/src/elements/list/definition-list.ts +16 -0
- package/src/elements/list/index.ts +48 -0
- package/src/elements/list/item-rendering.ts +38 -0
- package/src/elements/list/items.ts +61 -0
- package/src/elements/list/no-marker.ts +53 -0
- package/src/elements/list/paragraphs.ts +34 -0
- package/src/elements/list/trim.ts +38 -0
- package/src/elements/math/block.ts +29 -0
- package/src/elements/math/equation-ref.ts +12 -0
- package/src/elements/math/index.ts +14 -0
- package/src/elements/math/inline.ts +19 -0
- package/src/elements/math/latex.ts +27 -0
- package/src/elements/math/source.ts +18 -0
- package/src/elements/module/backlinks.ts +2 -1
- package/src/elements/module/categories.ts +2 -2
- package/src/elements/module/empty-container.ts +10 -0
- package/src/elements/module/index.ts +2 -4
- package/src/elements/module/join-markup.ts +10 -0
- package/src/elements/module/join.ts +2 -7
- package/src/elements/module/listpages.ts +2 -2
- package/src/elements/module/listusers.ts +2 -2
- package/src/elements/module/page-tree.ts +2 -2
- package/src/elements/module/rate-markup.ts +10 -0
- package/src/elements/module/rate.ts +4 -13
- package/src/elements/module/unknown.ts +11 -0
- package/src/elements/tab-view/ids.ts +16 -0
- package/src/elements/tab-view/index.ts +31 -0
- package/src/elements/tab-view/navigation.ts +15 -0
- package/src/elements/tab-view/panels.ts +16 -0
- package/src/elements/table/attributes.ts +23 -0
- package/src/elements/table/cell-attributes.ts +62 -0
- package/src/elements/table/cell.ts +13 -0
- package/src/elements/table/index.ts +27 -0
- package/src/elements/text/email.ts +20 -0
- package/src/elements/text/index.ts +11 -0
- package/src/elements/text/plain.ts +11 -0
- package/src/elements/text/raw.ts +20 -0
- package/src/elements/toc/body.ts +12 -0
- package/src/elements/toc/entries.ts +34 -0
- package/src/elements/toc/frame.ts +27 -0
- package/src/elements/toc/index.ts +17 -0
- package/src/elements/toc/link.ts +26 -0
- package/src/elements/user/index.ts +40 -0
- package/src/elements/user/markup.ts +34 -0
- package/src/elements/user/resolve.ts +6 -0
- package/src/escape/attribute-allowlists.ts +101 -0
- package/src/escape/attributes.ts +62 -0
- package/src/escape/css-color-functions.ts +18 -0
- package/src/escape/css-colors.ts +183 -0
- package/src/escape/css-danger.ts +22 -0
- package/src/escape/css-normalize.ts +54 -0
- package/src/escape/css-style.ts +78 -0
- package/src/escape/css-urls.ts +76 -0
- package/src/escape/css.ts +4 -0
- package/src/escape/email.ts +22 -0
- package/src/escape/html.ts +68 -0
- package/src/escape/index.ts +15 -0
- package/src/escape/url.ts +18 -0
- package/src/libs/highlighter/engine/end-pattern.ts +26 -0
- package/src/libs/highlighter/engine/html.ts +19 -0
- package/src/libs/highlighter/engine/index.ts +3 -0
- package/src/libs/highlighter/engine/keywords.ts +22 -0
- package/src/libs/highlighter/engine/parts.ts +36 -0
- package/src/libs/highlighter/engine/preprocess.ts +10 -0
- package/src/libs/highlighter/engine/render.ts +31 -0
- package/src/libs/highlighter/engine/token.ts +7 -0
- package/src/libs/highlighter/engine/tokenizer.ts +266 -0
- package/src/libs/highlighter/engine/utils.ts +38 -0
- package/src/render/collected-styles.ts +22 -0
- package/src/render/dispatch.ts +181 -0
- package/src/render/index.ts +28 -0
- package/src/render/primitives.ts +17 -0
- package/src/render/style-tag.ts +6 -0
- package/src/render/style.ts +15 -0
- package/src/types.ts +6 -2
- package/src/context.ts +0 -422
- package/src/elements/bibliography.ts +0 -123
- package/src/elements/code.ts +0 -49
- package/src/elements/collapsible.ts +0 -105
- package/src/elements/container.ts +0 -302
- package/src/elements/embed-block.ts +0 -327
- package/src/elements/embed.ts +0 -166
- package/src/elements/expr.ts +0 -102
- package/src/elements/footnote.ts +0 -76
- package/src/elements/html.ts +0 -79
- package/src/elements/iframe.ts +0 -44
- package/src/elements/iftags.ts +0 -118
- package/src/elements/image.ts +0 -154
- package/src/elements/link.ts +0 -201
- package/src/elements/list.ts +0 -241
- package/src/elements/math.ts +0 -177
- package/src/elements/tab-view.ts +0 -75
- package/src/elements/table.ts +0 -101
- package/src/elements/text.ts +0 -57
- package/src/elements/toc.ts +0 -147
- package/src/elements/user.ts +0 -79
- package/src/escape.ts +0 -829
- package/src/libs/highlighter/engine.ts +0 -352
- package/src/render.ts +0 -231
package/dist/index.cjs
CHANGED
|
@@ -44,125 +44,86 @@ var __export = (target, all) => {
|
|
|
44
44
|
var exports_src = {};
|
|
45
45
|
__export(exports_src, {
|
|
46
46
|
renderToHtml: () => renderToHtml,
|
|
47
|
-
createSettings: () =>
|
|
48
|
-
DEFAULT_SETTINGS: () =>
|
|
47
|
+
createSettings: () => import_ast6.createSettings,
|
|
48
|
+
DEFAULT_SETTINGS: () => import_ast6.DEFAULT_SETTINGS,
|
|
49
49
|
DEFAULT_EMBED_ALLOWLIST: () => DEFAULT_EMBED_ALLOWLIST
|
|
50
50
|
});
|
|
51
51
|
module.exports = __toCommonJS(exports_src);
|
|
52
52
|
|
|
53
|
-
// packages/render/src/
|
|
54
|
-
var import_ast4 = require("@wdprlib/ast");
|
|
55
|
-
|
|
56
|
-
// packages/render/src/context.ts
|
|
53
|
+
// packages/render/src/context/index.ts
|
|
57
54
|
var import_ast = require("@wdprlib/ast");
|
|
58
55
|
|
|
59
|
-
// packages/render/src/escape.ts
|
|
56
|
+
// packages/render/src/escape/html.ts
|
|
60
57
|
function escapeHtml(text) {
|
|
61
|
-
|
|
58
|
+
let start = 0;
|
|
59
|
+
let escaped = "";
|
|
60
|
+
for (let i = 0;i < text.length; i++) {
|
|
61
|
+
const char = text[i];
|
|
62
|
+
let replacement = null;
|
|
63
|
+
if (char === "&") {
|
|
64
|
+
replacement = "&";
|
|
65
|
+
} else if (char === "<") {
|
|
66
|
+
replacement = "<";
|
|
67
|
+
} else if (char === ">") {
|
|
68
|
+
replacement = ">";
|
|
69
|
+
}
|
|
70
|
+
if (replacement) {
|
|
71
|
+
if (start < i) {
|
|
72
|
+
escaped += text.slice(start, i);
|
|
73
|
+
}
|
|
74
|
+
escaped += replacement;
|
|
75
|
+
start = i + 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (start === 0) {
|
|
79
|
+
return text;
|
|
80
|
+
}
|
|
81
|
+
return start < text.length ? escaped + text.slice(start) : escaped;
|
|
62
82
|
}
|
|
63
83
|
function escapeAttr(value) {
|
|
84
|
+
if (value.indexOf("&") === -1 && value.indexOf("<") === -1 && value.indexOf(">") === -1 && value.indexOf('"') === -1 && value.indexOf("'") === -1) {
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
64
87
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
65
88
|
}
|
|
66
89
|
function escapeStyleContent(css) {
|
|
90
|
+
if (css.indexOf("</") === -1) {
|
|
91
|
+
return css;
|
|
92
|
+
}
|
|
67
93
|
return css.replace(/<\/style/gi, "<\\/style");
|
|
68
94
|
}
|
|
69
|
-
|
|
70
|
-
"accept",
|
|
71
|
-
"align",
|
|
72
|
-
"alt",
|
|
73
|
-
"autocapitalize",
|
|
74
|
-
"autoplay",
|
|
75
|
-
"background",
|
|
76
|
-
"bgcolor",
|
|
77
|
-
"border",
|
|
78
|
-
"buffered",
|
|
79
|
-
"checked",
|
|
80
|
-
"cite",
|
|
81
|
-
"class",
|
|
82
|
-
"cols",
|
|
83
|
-
"colspan",
|
|
84
|
-
"contenteditable",
|
|
85
|
-
"controls",
|
|
86
|
-
"coords",
|
|
87
|
-
"datetime",
|
|
88
|
-
"decoding",
|
|
89
|
-
"default",
|
|
90
|
-
"dir",
|
|
91
|
-
"dirname",
|
|
92
|
-
"disabled",
|
|
93
|
-
"download",
|
|
94
|
-
"draggable",
|
|
95
|
-
"for",
|
|
96
|
-
"form",
|
|
97
|
-
"headers",
|
|
98
|
-
"height",
|
|
99
|
-
"hidden",
|
|
100
|
-
"high",
|
|
101
|
-
"href",
|
|
102
|
-
"hreflang",
|
|
103
|
-
"id",
|
|
104
|
-
"inputmode",
|
|
105
|
-
"ismap",
|
|
106
|
-
"itemprop",
|
|
107
|
-
"kind",
|
|
108
|
-
"label",
|
|
109
|
-
"lang",
|
|
110
|
-
"list",
|
|
111
|
-
"loop",
|
|
112
|
-
"low",
|
|
113
|
-
"max",
|
|
114
|
-
"maxlength",
|
|
115
|
-
"min",
|
|
116
|
-
"minlength",
|
|
117
|
-
"multiple",
|
|
118
|
-
"muted",
|
|
119
|
-
"name",
|
|
120
|
-
"optimum",
|
|
121
|
-
"pattern",
|
|
122
|
-
"placeholder",
|
|
123
|
-
"poster",
|
|
124
|
-
"preload",
|
|
125
|
-
"readonly",
|
|
126
|
-
"required",
|
|
127
|
-
"reversed",
|
|
128
|
-
"role",
|
|
129
|
-
"rows",
|
|
130
|
-
"rowspan",
|
|
131
|
-
"scope",
|
|
132
|
-
"selected",
|
|
133
|
-
"shape",
|
|
134
|
-
"size",
|
|
135
|
-
"sizes",
|
|
136
|
-
"span",
|
|
137
|
-
"spellcheck",
|
|
138
|
-
"src",
|
|
139
|
-
"srclang",
|
|
140
|
-
"srcset",
|
|
141
|
-
"start",
|
|
142
|
-
"step",
|
|
143
|
-
"style",
|
|
144
|
-
"tabindex",
|
|
145
|
-
"target",
|
|
146
|
-
"title",
|
|
147
|
-
"translate",
|
|
148
|
-
"type",
|
|
149
|
-
"usemap",
|
|
150
|
-
"value",
|
|
151
|
-
"width",
|
|
152
|
-
"wrap"
|
|
153
|
-
]);
|
|
154
|
-
function isSafeAttribute(name) {
|
|
155
|
-
const lower = name.toLowerCase();
|
|
156
|
-
if (lower.startsWith("on"))
|
|
157
|
-
return false;
|
|
158
|
-
if (lower.startsWith("aria-") || lower.startsWith("data-"))
|
|
159
|
-
return true;
|
|
160
|
-
return SAFE_ATTRIBUTES.has(lower);
|
|
161
|
-
}
|
|
95
|
+
// packages/render/src/escape/url.ts
|
|
162
96
|
function isDangerousUrl(value) {
|
|
163
|
-
const normalized = value
|
|
97
|
+
const normalized = stripControlAndWhitespace(value);
|
|
164
98
|
return /^(javascript|data|vbscript):/i.test(normalized);
|
|
165
99
|
}
|
|
100
|
+
var WHITESPACE = /\s/;
|
|
101
|
+
function stripControlAndWhitespace(value) {
|
|
102
|
+
let result = "";
|
|
103
|
+
for (const char of value) {
|
|
104
|
+
const code = char.charCodeAt(0);
|
|
105
|
+
if (WHITESPACE.test(char) || code <= 31 || code >= 127 && code <= 159) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
result += char;
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
// packages/render/src/escape/css-color-functions.ts
|
|
113
|
+
function isValidCssColorFunction(color) {
|
|
114
|
+
const fnMatch = color.match(/^(rgba?|hsla?)\(([^)]*)\)$/);
|
|
115
|
+
if (!fnMatch) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
const fn = fnMatch[1];
|
|
119
|
+
const args = fnMatch[2].split(",").map((s) => s.trim()).join(",");
|
|
120
|
+
if (fn.startsWith("rgb")) {
|
|
121
|
+
return /^\d{1,3},\d{1,3},\d{1,3}(,(0|1|0?\.\d+))?$/.test(args);
|
|
122
|
+
}
|
|
123
|
+
return /^\d{1,3},\d{1,3}%,\d{1,3}%(,(0|1|0?\.\d+))?$/.test(args);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// packages/render/src/escape/css-colors.ts
|
|
166
127
|
var CSS_NAMED_COLORS = new Set([
|
|
167
128
|
"aliceblue",
|
|
168
129
|
"antiquewhite",
|
|
@@ -327,36 +288,58 @@ function isValidCssColor(color) {
|
|
|
327
288
|
if (/^#[0-9a-f]{3}([0-9a-f])?$/.test(trimmed) || /^#[0-9a-f]{6}([0-9a-f]{2})?$/.test(trimmed)) {
|
|
328
289
|
return true;
|
|
329
290
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const fn = fnMatch[1];
|
|
333
|
-
const args = fnMatch[2].split(",").map((s) => s.trim()).join(",");
|
|
334
|
-
if (fn.startsWith("rgb")) {
|
|
335
|
-
if (/^\d{1,3},\d{1,3},\d{1,3}(,(0|1|0?\.\d+))?$/.test(args))
|
|
336
|
-
return true;
|
|
337
|
-
} else {
|
|
338
|
-
if (/^\d{1,3},\d{1,3}%,\d{1,3}%(,(0|1|0?\.\d+))?$/.test(args))
|
|
339
|
-
return true;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
291
|
+
if (isValidCssColorFunction(trimmed))
|
|
292
|
+
return true;
|
|
342
293
|
return false;
|
|
343
294
|
}
|
|
344
295
|
function sanitizeCssColor(color, fallback = "inherit") {
|
|
345
296
|
return isValidCssColor(color) ? color : fallback;
|
|
346
297
|
}
|
|
298
|
+
// packages/render/src/escape/css-normalize.ts
|
|
347
299
|
function normalizeCssValue(value) {
|
|
348
300
|
let result = value;
|
|
349
|
-
result = result
|
|
301
|
+
result = stripCssComments(result);
|
|
350
302
|
result = result.replace(/\\(?:\r\n|[\n\r\f])/g, "");
|
|
351
303
|
result = result.replace(/\\([0-9a-f]{1,6})\s?/gi, (_, hex) => {
|
|
352
304
|
const code = Number.parseInt(hex, 16);
|
|
353
305
|
return code > 0 && code <= 1114111 ? String.fromCodePoint(code) : "";
|
|
354
306
|
});
|
|
355
307
|
result = result.replace(/\\(.)/g, "$1");
|
|
356
|
-
result = result
|
|
308
|
+
result = stripControlAndWhitespace2(result);
|
|
357
309
|
return result.toLowerCase();
|
|
358
310
|
}
|
|
359
|
-
|
|
311
|
+
var WHITESPACE2 = /\s/;
|
|
312
|
+
function stripCssComments(value) {
|
|
313
|
+
let result = "";
|
|
314
|
+
let cursor = 0;
|
|
315
|
+
while (cursor < value.length) {
|
|
316
|
+
const start = value.indexOf("/*", cursor);
|
|
317
|
+
if (start === -1) {
|
|
318
|
+
result += value.slice(cursor);
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
result += value.slice(cursor, start);
|
|
322
|
+
const end = value.indexOf("*/", start + 2);
|
|
323
|
+
if (end === -1) {
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
cursor = end + 2;
|
|
327
|
+
}
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
function stripControlAndWhitespace2(value) {
|
|
331
|
+
let result = "";
|
|
332
|
+
for (const char of value) {
|
|
333
|
+
const code = char.charCodeAt(0);
|
|
334
|
+
if (WHITESPACE2.test(char) || code <= 31 || code >= 127 && code <= 159) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
result += char;
|
|
338
|
+
}
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
// packages/render/src/escape/css-urls.ts
|
|
342
|
+
function isCssUrlAllowed(rawUrl) {
|
|
360
343
|
let url = rawUrl;
|
|
361
344
|
if (url.length >= 2) {
|
|
362
345
|
const first = url[0];
|
|
@@ -387,7 +370,7 @@ function isUrlAllowed(rawUrl) {
|
|
|
387
370
|
}
|
|
388
371
|
return false;
|
|
389
372
|
}
|
|
390
|
-
function*
|
|
373
|
+
function* iterateCssUrls(normalized) {
|
|
391
374
|
let searchPos = 0;
|
|
392
375
|
while (searchPos < normalized.length) {
|
|
393
376
|
const idx = normalized.indexOf("url(", searchPos);
|
|
@@ -418,12 +401,14 @@ function* iterateUrls(normalized) {
|
|
|
418
401
|
searchPos = i;
|
|
419
402
|
}
|
|
420
403
|
}
|
|
404
|
+
|
|
405
|
+
// packages/render/src/escape/css-danger.ts
|
|
421
406
|
function isDangerousCssValue(value) {
|
|
422
407
|
const normalized = normalizeCssValue(value);
|
|
423
|
-
for (const { inner, malformed } of
|
|
408
|
+
for (const { inner, malformed } of iterateCssUrls(normalized)) {
|
|
424
409
|
if (malformed)
|
|
425
410
|
return true;
|
|
426
|
-
if (!
|
|
411
|
+
if (!isCssUrlAllowed(inner))
|
|
427
412
|
return true;
|
|
428
413
|
}
|
|
429
414
|
if (normalized.includes("expression("))
|
|
@@ -436,71 +421,164 @@ function isDangerousCssValue(value) {
|
|
|
436
421
|
return true;
|
|
437
422
|
return false;
|
|
438
423
|
}
|
|
424
|
+
// packages/render/src/escape/css-style.ts
|
|
425
|
+
function sanitizeStyleValue(style) {
|
|
426
|
+
const endsWithSemicolon = style.trimEnd().endsWith(";");
|
|
427
|
+
if (style.indexOf(";") === -1) {
|
|
428
|
+
return sanitizeSingleDeclaration(style.trim());
|
|
429
|
+
}
|
|
430
|
+
const safe = [];
|
|
431
|
+
for (const rawDecl of splitDeclarations(style)) {
|
|
432
|
+
const decl = sanitizeSingleDeclaration(rawDecl.trim());
|
|
433
|
+
if (decl)
|
|
434
|
+
safe.push(decl);
|
|
435
|
+
}
|
|
436
|
+
if (safe.length === 0)
|
|
437
|
+
return "";
|
|
438
|
+
return endsWithSemicolon ? safe.join(";") + ";" : safe.join(";");
|
|
439
|
+
}
|
|
440
|
+
function sanitizeSingleDeclaration(decl) {
|
|
441
|
+
if (decl === "")
|
|
442
|
+
return "";
|
|
443
|
+
const colonIdx = decl.indexOf(":");
|
|
444
|
+
if (colonIdx === -1)
|
|
445
|
+
return "";
|
|
446
|
+
const property = decl.slice(0, colonIdx).trim();
|
|
447
|
+
const value = decl.slice(colonIdx + 1).trim();
|
|
448
|
+
if (isDangerousCssValue(value))
|
|
449
|
+
return "";
|
|
450
|
+
const normalisedProperty = normalizeCssValue(property);
|
|
451
|
+
if (normalisedProperty.startsWith("-moz-binding"))
|
|
452
|
+
return "";
|
|
453
|
+
if (normalisedProperty === "behavior")
|
|
454
|
+
return "";
|
|
455
|
+
return decl;
|
|
456
|
+
}
|
|
439
457
|
function splitDeclarations(style) {
|
|
440
458
|
const out = [];
|
|
441
|
-
let
|
|
459
|
+
let start = 0;
|
|
442
460
|
let parenDepth = 0;
|
|
443
461
|
let quoteChar = null;
|
|
444
|
-
for (
|
|
462
|
+
for (let i = 0;i < style.length; i++) {
|
|
463
|
+
const ch = style[i];
|
|
445
464
|
if (quoteChar !== null) {
|
|
446
|
-
buf += ch;
|
|
447
465
|
if (ch === quoteChar)
|
|
448
466
|
quoteChar = null;
|
|
449
467
|
continue;
|
|
450
468
|
}
|
|
451
469
|
if (ch === '"' || ch === "'") {
|
|
452
470
|
quoteChar = ch;
|
|
453
|
-
buf += ch;
|
|
454
471
|
continue;
|
|
455
472
|
}
|
|
456
473
|
if (ch === "(") {
|
|
457
474
|
parenDepth++;
|
|
458
|
-
buf += ch;
|
|
459
475
|
continue;
|
|
460
476
|
}
|
|
461
477
|
if (ch === ")") {
|
|
462
478
|
if (parenDepth > 0)
|
|
463
479
|
parenDepth--;
|
|
464
|
-
buf += ch;
|
|
465
480
|
continue;
|
|
466
481
|
}
|
|
467
482
|
if (ch === ";" && parenDepth === 0) {
|
|
468
|
-
out.push(
|
|
469
|
-
|
|
483
|
+
out.push(style.slice(start, i));
|
|
484
|
+
start = i + 1;
|
|
470
485
|
continue;
|
|
471
486
|
}
|
|
472
|
-
buf += ch;
|
|
473
487
|
}
|
|
474
|
-
if (
|
|
475
|
-
out.push(
|
|
488
|
+
if (start < style.length)
|
|
489
|
+
out.push(style.slice(start));
|
|
476
490
|
return out;
|
|
477
491
|
}
|
|
478
|
-
|
|
479
|
-
const endsWithSemicolon = style.trimEnd().endsWith(";");
|
|
480
|
-
const declarations = splitDeclarations(style).map((d) => d.trim()).filter(Boolean);
|
|
481
|
-
const safe = [];
|
|
482
|
-
for (const decl of declarations) {
|
|
483
|
-
const colonIdx = decl.indexOf(":");
|
|
484
|
-
if (colonIdx === -1)
|
|
485
|
-
continue;
|
|
486
|
-
const property = decl.slice(0, colonIdx).trim();
|
|
487
|
-
const value = decl.slice(colonIdx + 1).trim();
|
|
488
|
-
if (isDangerousCssValue(value))
|
|
489
|
-
continue;
|
|
490
|
-
const normalisedProperty = normalizeCssValue(property);
|
|
491
|
-
if (normalisedProperty.startsWith("-moz-binding"))
|
|
492
|
-
continue;
|
|
493
|
-
if (normalisedProperty === "behavior")
|
|
494
|
-
continue;
|
|
495
|
-
safe.push(decl);
|
|
496
|
-
}
|
|
497
|
-
if (safe.length === 0)
|
|
498
|
-
return "";
|
|
499
|
-
return endsWithSemicolon ? safe.join(";") + ";" : safe.join(";");
|
|
500
|
-
}
|
|
492
|
+
// packages/render/src/escape/email.ts
|
|
501
493
|
function isValidEmail(email) {
|
|
502
494
|
return /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email);
|
|
503
495
|
}
|
|
496
|
+
// packages/render/src/escape/attribute-allowlists.ts
|
|
497
|
+
var SAFE_ATTRIBUTES = new Set([
|
|
498
|
+
"accept",
|
|
499
|
+
"align",
|
|
500
|
+
"alt",
|
|
501
|
+
"autocapitalize",
|
|
502
|
+
"autoplay",
|
|
503
|
+
"background",
|
|
504
|
+
"bgcolor",
|
|
505
|
+
"border",
|
|
506
|
+
"buffered",
|
|
507
|
+
"checked",
|
|
508
|
+
"cite",
|
|
509
|
+
"class",
|
|
510
|
+
"cols",
|
|
511
|
+
"colspan",
|
|
512
|
+
"contenteditable",
|
|
513
|
+
"controls",
|
|
514
|
+
"coords",
|
|
515
|
+
"datetime",
|
|
516
|
+
"decoding",
|
|
517
|
+
"default",
|
|
518
|
+
"dir",
|
|
519
|
+
"dirname",
|
|
520
|
+
"disabled",
|
|
521
|
+
"download",
|
|
522
|
+
"draggable",
|
|
523
|
+
"for",
|
|
524
|
+
"form",
|
|
525
|
+
"headers",
|
|
526
|
+
"height",
|
|
527
|
+
"hidden",
|
|
528
|
+
"high",
|
|
529
|
+
"href",
|
|
530
|
+
"hreflang",
|
|
531
|
+
"id",
|
|
532
|
+
"inputmode",
|
|
533
|
+
"ismap",
|
|
534
|
+
"itemprop",
|
|
535
|
+
"kind",
|
|
536
|
+
"label",
|
|
537
|
+
"lang",
|
|
538
|
+
"list",
|
|
539
|
+
"loop",
|
|
540
|
+
"low",
|
|
541
|
+
"max",
|
|
542
|
+
"maxlength",
|
|
543
|
+
"min",
|
|
544
|
+
"minlength",
|
|
545
|
+
"multiple",
|
|
546
|
+
"muted",
|
|
547
|
+
"name",
|
|
548
|
+
"optimum",
|
|
549
|
+
"pattern",
|
|
550
|
+
"placeholder",
|
|
551
|
+
"poster",
|
|
552
|
+
"preload",
|
|
553
|
+
"readonly",
|
|
554
|
+
"required",
|
|
555
|
+
"reversed",
|
|
556
|
+
"role",
|
|
557
|
+
"rows",
|
|
558
|
+
"rowspan",
|
|
559
|
+
"scope",
|
|
560
|
+
"selected",
|
|
561
|
+
"shape",
|
|
562
|
+
"size",
|
|
563
|
+
"sizes",
|
|
564
|
+
"span",
|
|
565
|
+
"spellcheck",
|
|
566
|
+
"src",
|
|
567
|
+
"srclang",
|
|
568
|
+
"srcset",
|
|
569
|
+
"start",
|
|
570
|
+
"step",
|
|
571
|
+
"style",
|
|
572
|
+
"tabindex",
|
|
573
|
+
"target",
|
|
574
|
+
"title",
|
|
575
|
+
"translate",
|
|
576
|
+
"type",
|
|
577
|
+
"usemap",
|
|
578
|
+
"value",
|
|
579
|
+
"width",
|
|
580
|
+
"wrap"
|
|
581
|
+
]);
|
|
504
582
|
var URL_ATTRIBUTES = new Set([
|
|
505
583
|
"href",
|
|
506
584
|
"src",
|
|
@@ -510,12 +588,22 @@ var URL_ATTRIBUTES = new Set([
|
|
|
510
588
|
"poster",
|
|
511
589
|
"background"
|
|
512
590
|
]);
|
|
591
|
+
|
|
592
|
+
// packages/render/src/escape/attributes.ts
|
|
593
|
+
function isSafeAttributeLower(lower) {
|
|
594
|
+
if (lower.startsWith("on"))
|
|
595
|
+
return false;
|
|
596
|
+
if (lower.startsWith("aria-") || lower.startsWith("data-"))
|
|
597
|
+
return true;
|
|
598
|
+
return SAFE_ATTRIBUTES.has(lower);
|
|
599
|
+
}
|
|
513
600
|
function sanitizeAttributes(attributes) {
|
|
514
601
|
const result = {};
|
|
515
|
-
for (const
|
|
516
|
-
|
|
517
|
-
continue;
|
|
602
|
+
for (const key in attributes) {
|
|
603
|
+
const value = attributes[key];
|
|
518
604
|
const lower = key.toLowerCase();
|
|
605
|
+
if (!isSafeAttributeLower(lower))
|
|
606
|
+
continue;
|
|
519
607
|
if (URL_ATTRIBUTES.has(lower) && isDangerousUrl(value))
|
|
520
608
|
continue;
|
|
521
609
|
if (lower === "style") {
|
|
@@ -529,1023 +617,734 @@ function sanitizeAttributes(attributes) {
|
|
|
529
617
|
}
|
|
530
618
|
return result;
|
|
531
619
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
620
|
+
// packages/render/src/context/attributes.ts
|
|
621
|
+
function renderAttributeString(attributes) {
|
|
622
|
+
const safe = sanitizeAttributes(attributes);
|
|
623
|
+
let result = "";
|
|
624
|
+
for (const [key, value] of Object.entries(safe)) {
|
|
625
|
+
if (value !== "") {
|
|
626
|
+
result += ` ${key}="${escapeAttr(value)}"`;
|
|
627
|
+
} else {
|
|
628
|
+
result += ` ${key}=""`;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return result;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// packages/render/src/context/bibliography.ts
|
|
635
|
+
class BibliographyIndex {
|
|
636
|
+
elements;
|
|
637
|
+
state = null;
|
|
638
|
+
constructor(elements) {
|
|
639
|
+
this.elements = elements;
|
|
640
|
+
}
|
|
641
|
+
getCitationNumber(label) {
|
|
642
|
+
return this.getState().map.get(label);
|
|
643
|
+
}
|
|
644
|
+
getState() {
|
|
645
|
+
if (this.state === null) {
|
|
646
|
+
this.state = collectBibliographyState(this.elements);
|
|
647
|
+
}
|
|
648
|
+
return this.state;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
function collectBibliographyState(elements) {
|
|
652
|
+
const state = {
|
|
653
|
+
map: new Map
|
|
654
|
+
};
|
|
655
|
+
collectFromElements(elements, state);
|
|
656
|
+
return state;
|
|
657
|
+
}
|
|
658
|
+
function collectFromElements(elements, state) {
|
|
659
|
+
for (const element of elements) {
|
|
660
|
+
if (element.element === "bibliography-block") {
|
|
661
|
+
addBlockEntries(element.data, state);
|
|
662
|
+
}
|
|
663
|
+
collectFromChildren(element, state);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
function addBlockEntries(data, state) {
|
|
667
|
+
for (const entry of data.entries) {
|
|
668
|
+
if (state.map.has(entry.key_string))
|
|
669
|
+
continue;
|
|
670
|
+
state.map.set(entry.key_string, state.map.size + 1);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
function collectFromChildren(element, state) {
|
|
674
|
+
switch (element.element) {
|
|
675
|
+
case "list":
|
|
676
|
+
collectFromList(element.data, state);
|
|
677
|
+
return;
|
|
678
|
+
case "table":
|
|
679
|
+
collectFromTable(element.data, state);
|
|
680
|
+
return;
|
|
681
|
+
case "definition-list":
|
|
682
|
+
for (const item of element.data) {
|
|
683
|
+
collectFromElements(item.key, state);
|
|
684
|
+
collectFromElements(item.value, state);
|
|
685
|
+
}
|
|
686
|
+
return;
|
|
687
|
+
case "tab-view":
|
|
688
|
+
collectFromTabs(element.data, state);
|
|
689
|
+
return;
|
|
690
|
+
default:
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
if (hasElementChildren(element)) {
|
|
694
|
+
collectFromElements(element.data.elements, state);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
function collectFromList(data, state) {
|
|
698
|
+
for (const item of data.items) {
|
|
699
|
+
if (item["item-type"] === "elements") {
|
|
700
|
+
collectFromElements(item.elements, state);
|
|
701
|
+
} else {
|
|
702
|
+
collectFromList(item.data, state);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
function collectFromTable(data, state) {
|
|
707
|
+
for (const row of data.rows) {
|
|
708
|
+
for (const cell of row.cells) {
|
|
709
|
+
collectFromElements(cell.elements, state);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
function collectFromTabs(tabs, state) {
|
|
714
|
+
for (const tab of tabs) {
|
|
715
|
+
collectFromElements(tab.elements, state);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
function hasElementChildren(element) {
|
|
719
|
+
if (!("data" in element))
|
|
720
|
+
return false;
|
|
721
|
+
const data = element.data;
|
|
722
|
+
return data !== null && typeof data === "object" && "elements" in data && Array.isArray(data.elements);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// packages/render/src/context/counters.ts
|
|
726
|
+
class RenderCounters {
|
|
727
|
+
tocIndex = 0;
|
|
728
|
+
footnoteIndex = 0;
|
|
729
|
+
equationIndex = 0;
|
|
730
|
+
htmlBlockIndex = 0;
|
|
731
|
+
bibciteCounter = 0;
|
|
732
|
+
tabViewIndex = 0;
|
|
733
|
+
idSuffix;
|
|
734
|
+
constructor(useTrueIds) {
|
|
735
|
+
this.idSuffix = useTrueIds ? null : Math.random().toString(16).slice(2, 8);
|
|
736
|
+
}
|
|
737
|
+
nextTocIndex() {
|
|
738
|
+
return this.tocIndex++;
|
|
739
|
+
}
|
|
740
|
+
nextFootnoteIndex() {
|
|
741
|
+
return this.footnoteIndex++;
|
|
742
|
+
}
|
|
743
|
+
nextEquationIndex() {
|
|
744
|
+
return this.equationIndex++;
|
|
745
|
+
}
|
|
746
|
+
nextHtmlBlockIndex() {
|
|
747
|
+
return this.htmlBlockIndex++;
|
|
748
|
+
}
|
|
749
|
+
nextBibciteCounter() {
|
|
750
|
+
return ++this.bibciteCounter;
|
|
751
|
+
}
|
|
752
|
+
nextTabViewIndex() {
|
|
753
|
+
return this.tabViewIndex++;
|
|
754
|
+
}
|
|
755
|
+
generateId(prefix, index) {
|
|
756
|
+
if (this.idSuffix === null) {
|
|
757
|
+
return `${prefix}${index}`;
|
|
758
|
+
}
|
|
759
|
+
return `${prefix}${index}-${this.idSuffix}`;
|
|
760
|
+
}
|
|
761
|
+
generateFixedId(name) {
|
|
762
|
+
if (this.idSuffix === null) {
|
|
763
|
+
return name;
|
|
764
|
+
}
|
|
765
|
+
return `${name}-${this.idSuffix}`;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// packages/render/src/context/output.ts
|
|
770
|
+
class RenderOutputBuffer {
|
|
771
|
+
chunks = [];
|
|
772
|
+
push(html) {
|
|
773
|
+
this.chunks.push(html);
|
|
774
|
+
}
|
|
775
|
+
pushEscaped(text) {
|
|
776
|
+
this.chunks.push(escapeHtml(text));
|
|
777
|
+
}
|
|
778
|
+
getOutput() {
|
|
779
|
+
return this.chunks.join("");
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// packages/render/src/context/style-slots.ts
|
|
784
|
+
class StyleSlotState {
|
|
785
|
+
activeSlotId = null;
|
|
786
|
+
contents = new Map;
|
|
787
|
+
enter(slotId) {
|
|
788
|
+
this.activeSlotId = slotId;
|
|
789
|
+
if (!this.contents.has(slotId)) {
|
|
790
|
+
this.contents.set(slotId, []);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
exit() {
|
|
794
|
+
this.activeSlotId = null;
|
|
795
|
+
}
|
|
796
|
+
hasActiveSlot() {
|
|
797
|
+
return this.activeSlotId !== null;
|
|
798
|
+
}
|
|
799
|
+
push(css) {
|
|
800
|
+
if (this.activeSlotId !== null) {
|
|
801
|
+
this.contents.get(this.activeSlotId).push(css);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
getContents(slotId) {
|
|
805
|
+
return this.contents.get(slotId) ?? [];
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// packages/render/src/context/image-urls.ts
|
|
810
|
+
function resolveImageSource(source, settings, page) {
|
|
811
|
+
const pageName = page?.pageName;
|
|
812
|
+
switch (source.type) {
|
|
813
|
+
case "url": {
|
|
814
|
+
const url = source.data;
|
|
815
|
+
if (url.startsWith("/") && !url.startsWith("//")) {
|
|
816
|
+
if (!settings.allowLocalPaths)
|
|
817
|
+
return null;
|
|
818
|
+
return `/local--files${url}`;
|
|
819
|
+
}
|
|
820
|
+
return url;
|
|
821
|
+
}
|
|
822
|
+
case "file1":
|
|
823
|
+
if (!settings.allowLocalPaths)
|
|
824
|
+
return null;
|
|
825
|
+
return pageName ? `/local--files/${pageName}/${source.data.file}` : `/local--files/${source.data.file}`;
|
|
826
|
+
case "file2":
|
|
827
|
+
if (!settings.allowLocalPaths)
|
|
828
|
+
return null;
|
|
829
|
+
return `/local--files/${source.data.page}/${source.data.file}`;
|
|
830
|
+
case "file3":
|
|
831
|
+
if (!settings.allowLocalPaths)
|
|
832
|
+
return null;
|
|
833
|
+
return `/local--files/${source.data.site}/${source.data.page}/${source.data.file}`;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
// packages/render/src/context/page-urls.ts
|
|
837
|
+
function resolvePageLink(location, pageContext) {
|
|
838
|
+
if (typeof location === "string") {
|
|
839
|
+
return location;
|
|
840
|
+
}
|
|
841
|
+
const page = location.page;
|
|
842
|
+
if (page.startsWith("//")) {
|
|
843
|
+
return page.toLowerCase();
|
|
844
|
+
}
|
|
845
|
+
const hashIdx = page.indexOf("#");
|
|
846
|
+
if (hashIdx !== -1) {
|
|
847
|
+
let pagePart = page.slice(0, hashIdx);
|
|
848
|
+
const anchor = page.slice(hashIdx);
|
|
849
|
+
if (pagePart.endsWith("/")) {
|
|
850
|
+
pagePart = pagePart.slice(0, -1);
|
|
851
|
+
}
|
|
852
|
+
return `/${pagePart.toLowerCase()}${anchor.toLowerCase()}`;
|
|
853
|
+
}
|
|
854
|
+
const normalizedPage = normalizePageName(page);
|
|
855
|
+
const safePage = normalizedPage.startsWith("/") ? normalizedPage.slice(1) : normalizedPage;
|
|
856
|
+
if (location.site) {
|
|
857
|
+
const domain = resolveSiteDomain(location.site, pageContext);
|
|
858
|
+
if (domain !== null) {
|
|
859
|
+
return `https://${domain}/${safePage}`;
|
|
860
|
+
}
|
|
861
|
+
return `/${normalizePageName(location.site)}/${safePage}`;
|
|
862
|
+
}
|
|
863
|
+
return `/${safePage}`;
|
|
864
|
+
}
|
|
865
|
+
function resolveSiteDomain(site, pageContext) {
|
|
866
|
+
const configuredDomain = pageContext?.resolveSiteDomain?.(site) ?? pageContext?.siteDomains?.[site] ?? (pageContext?.site === site ? pageContext.domain : undefined);
|
|
867
|
+
if (configuredDomain)
|
|
868
|
+
return normalizeDomain(configuredDomain);
|
|
869
|
+
if (site.includes(".")) {
|
|
870
|
+
return normalizeDomain(site);
|
|
871
|
+
}
|
|
872
|
+
return null;
|
|
873
|
+
}
|
|
874
|
+
function normalizeDomain(domain) {
|
|
875
|
+
let normalized = domain;
|
|
876
|
+
const lower = normalized.toLowerCase();
|
|
877
|
+
if (lower.startsWith("https://")) {
|
|
878
|
+
normalized = normalized.slice("https://".length);
|
|
879
|
+
} else if (lower.startsWith("http://")) {
|
|
880
|
+
normalized = normalized.slice("http://".length);
|
|
881
|
+
}
|
|
882
|
+
let end = normalized.length;
|
|
883
|
+
while (end > 0 && normalized[end - 1] === "/") {
|
|
884
|
+
end--;
|
|
885
|
+
}
|
|
886
|
+
return end === normalized.length ? normalized : normalized.slice(0, end);
|
|
887
|
+
}
|
|
888
|
+
function normalizePageName(page) {
|
|
889
|
+
let normalized = page.toLowerCase();
|
|
890
|
+
if (normalized.indexOf(":") !== -1) {
|
|
891
|
+
normalized = normalized.replace(/:\s+/g, ":");
|
|
892
|
+
}
|
|
893
|
+
if (/\s/.test(normalized)) {
|
|
894
|
+
normalized = normalized.replace(/\s+/g, "-").trim();
|
|
895
|
+
}
|
|
896
|
+
if (!normalized.startsWith("/") && normalized.indexOf("/") !== -1) {
|
|
897
|
+
normalized = normalized.replace(/\//g, "-");
|
|
898
|
+
}
|
|
899
|
+
return normalized;
|
|
900
|
+
}
|
|
901
|
+
// packages/render/src/context/index.ts
|
|
902
|
+
class RenderContext {
|
|
903
|
+
output = new RenderOutputBuffer;
|
|
536
904
|
renderInlineStyles = false;
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
_tocIndex = 0;
|
|
540
|
-
_footnoteIndex = 0;
|
|
541
|
-
_equationIndex = 0;
|
|
542
|
-
_htmlBlockIndex = 0;
|
|
543
|
-
_bibciteCounter = 0;
|
|
544
|
-
_idSuffix;
|
|
905
|
+
_styleSlots = new StyleSlotState;
|
|
906
|
+
counters;
|
|
545
907
|
settings;
|
|
546
908
|
options;
|
|
547
909
|
footnotes;
|
|
548
910
|
styles;
|
|
549
911
|
htmlBlocks;
|
|
550
912
|
tocElements;
|
|
551
|
-
|
|
552
|
-
bibliographyEntries;
|
|
913
|
+
bibliography;
|
|
553
914
|
constructor(tree, options = {}) {
|
|
554
915
|
this.settings = options.settings ?? import_ast.DEFAULT_SETTINGS;
|
|
555
|
-
this.
|
|
916
|
+
this.counters = new RenderCounters(this.settings.useTrueIds);
|
|
556
917
|
this.options = options;
|
|
557
918
|
this.footnotes = options.footnotes ?? tree.footnotes ?? [];
|
|
558
919
|
this.styles = tree.styles ?? [];
|
|
559
920
|
this.htmlBlocks = tree["html-blocks"] ?? [];
|
|
560
921
|
this.tocElements = tree["table-of-contents"] ?? [];
|
|
561
|
-
this.
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
buildBibliographyMap(elements) {
|
|
566
|
-
for (const el of elements) {
|
|
567
|
-
if (el.element === "bibliography-block") {
|
|
568
|
-
const data = el.data;
|
|
569
|
-
for (const entry of data.entries) {
|
|
570
|
-
if (!this.bibliographyMap.has(entry.key_string)) {
|
|
571
|
-
const index = this.bibliographyMap.size + 1;
|
|
572
|
-
this.bibliographyMap.set(entry.key_string, index);
|
|
573
|
-
this.bibliographyEntries.push(entry);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
if ("data" in el && el.data && typeof el.data === "object") {
|
|
578
|
-
const data = el.data;
|
|
579
|
-
if ("elements" in data && Array.isArray(data.elements)) {
|
|
580
|
-
this.buildBibliographyMap(data.elements);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
}
|
|
922
|
+
this.bibliography = new BibliographyIndex(tree.elements);
|
|
923
|
+
}
|
|
924
|
+
getBibliographyCitationNumber(label) {
|
|
925
|
+
return this.bibliography.getCitationNumber(label);
|
|
584
926
|
}
|
|
585
927
|
push(html) {
|
|
586
|
-
this.
|
|
928
|
+
this.output.push(html);
|
|
587
929
|
}
|
|
588
930
|
pushEscaped(text) {
|
|
589
|
-
this.
|
|
931
|
+
this.output.pushEscaped(text);
|
|
590
932
|
}
|
|
591
933
|
getOutput() {
|
|
592
|
-
return this.
|
|
934
|
+
return this.output.getOutput();
|
|
593
935
|
}
|
|
594
936
|
enterStyleSlot(slotId) {
|
|
595
|
-
this.
|
|
596
|
-
if (!this._styleSlotContents.has(slotId)) {
|
|
597
|
-
this._styleSlotContents.set(slotId, []);
|
|
598
|
-
}
|
|
937
|
+
this._styleSlots.enter(slotId);
|
|
599
938
|
}
|
|
600
939
|
exitStyleSlot() {
|
|
601
|
-
this.
|
|
940
|
+
this._styleSlots.exit();
|
|
602
941
|
}
|
|
603
942
|
hasActiveStyleSlot() {
|
|
604
|
-
return this.
|
|
943
|
+
return this._styleSlots.hasActiveSlot();
|
|
605
944
|
}
|
|
606
945
|
pushToStyleSlot(css) {
|
|
607
|
-
|
|
608
|
-
this._styleSlotContents.get(this._styleSlotId).push(css);
|
|
609
|
-
}
|
|
946
|
+
this._styleSlots.push(css);
|
|
610
947
|
}
|
|
611
948
|
getStyleSlotContents(slotId) {
|
|
612
|
-
return this.
|
|
949
|
+
return this._styleSlots.getContents(slotId);
|
|
613
950
|
}
|
|
614
951
|
nextTocIndex() {
|
|
615
|
-
return this.
|
|
952
|
+
return this.counters.nextTocIndex();
|
|
616
953
|
}
|
|
617
954
|
nextFootnoteIndex() {
|
|
618
|
-
return this.
|
|
955
|
+
return this.counters.nextFootnoteIndex();
|
|
619
956
|
}
|
|
620
957
|
nextEquationIndex() {
|
|
621
|
-
return this.
|
|
958
|
+
return this.counters.nextEquationIndex();
|
|
622
959
|
}
|
|
623
960
|
nextHtmlBlockIndex() {
|
|
624
|
-
return this.
|
|
961
|
+
return this.counters.nextHtmlBlockIndex();
|
|
625
962
|
}
|
|
626
963
|
nextBibciteCounter() {
|
|
627
|
-
return
|
|
964
|
+
return this.counters.nextBibciteCounter();
|
|
965
|
+
}
|
|
966
|
+
nextTabViewIndex() {
|
|
967
|
+
return this.counters.nextTabViewIndex();
|
|
628
968
|
}
|
|
629
969
|
generateId(prefix, index) {
|
|
630
|
-
|
|
631
|
-
return `${prefix}${index}`;
|
|
632
|
-
}
|
|
633
|
-
return `${prefix}${index}-${this._idSuffix}`;
|
|
970
|
+
return this.counters.generateId(prefix, index);
|
|
634
971
|
}
|
|
635
972
|
generateFixedId(name) {
|
|
636
|
-
|
|
637
|
-
return name;
|
|
638
|
-
}
|
|
639
|
-
return `${name}-${this._idSuffix}`;
|
|
973
|
+
return this.counters.generateFixedId(name);
|
|
640
974
|
}
|
|
641
975
|
get page() {
|
|
642
976
|
return this.options.page;
|
|
643
977
|
}
|
|
644
978
|
resolveImageSource(source) {
|
|
645
|
-
|
|
646
|
-
switch (source.type) {
|
|
647
|
-
case "url": {
|
|
648
|
-
const url = source.data;
|
|
649
|
-
if (url.startsWith("/") && !url.startsWith("//")) {
|
|
650
|
-
if (!this.settings.allowLocalPaths)
|
|
651
|
-
return null;
|
|
652
|
-
return `/local--files${url}`;
|
|
653
|
-
}
|
|
654
|
-
return url;
|
|
655
|
-
}
|
|
656
|
-
case "file1":
|
|
657
|
-
if (!this.settings.allowLocalPaths)
|
|
658
|
-
return null;
|
|
659
|
-
return pageName ? `/local--files/${pageName}/${source.data.file}` : `/local--files/${source.data.file}`;
|
|
660
|
-
case "file2":
|
|
661
|
-
if (!this.settings.allowLocalPaths)
|
|
662
|
-
return null;
|
|
663
|
-
return `/local--files/${source.data.page}/${source.data.file}`;
|
|
664
|
-
case "file3":
|
|
665
|
-
if (!this.settings.allowLocalPaths)
|
|
666
|
-
return null;
|
|
667
|
-
return `/local--files/${source.data.site}/${source.data.page}/${source.data.file}`;
|
|
668
|
-
}
|
|
979
|
+
return resolveImageSource(source, this.settings, this.page);
|
|
669
980
|
}
|
|
670
981
|
resolvePageLink(location) {
|
|
671
|
-
|
|
672
|
-
return location;
|
|
673
|
-
}
|
|
674
|
-
const page = location.page;
|
|
675
|
-
if (page.startsWith("//")) {
|
|
676
|
-
return page.toLowerCase();
|
|
677
|
-
}
|
|
678
|
-
const hashIdx = page.indexOf("#");
|
|
679
|
-
if (hashIdx !== -1) {
|
|
680
|
-
let pagePart = page.slice(0, hashIdx);
|
|
681
|
-
const anchor = page.slice(hashIdx);
|
|
682
|
-
if (pagePart.endsWith("/")) {
|
|
683
|
-
pagePart = pagePart.slice(0, -1);
|
|
684
|
-
}
|
|
685
|
-
return `/${pagePart.toLowerCase()}${anchor.toLowerCase()}`;
|
|
686
|
-
}
|
|
687
|
-
const normalizedPage = this.normalizePageName(page);
|
|
688
|
-
const safePage = normalizedPage.startsWith("/") ? normalizedPage.slice(1) : normalizedPage;
|
|
689
|
-
if (location.site) {
|
|
690
|
-
return `https://${location.site}.wikidot.com/${safePage}`;
|
|
691
|
-
}
|
|
692
|
-
return `/${safePage}`;
|
|
693
|
-
}
|
|
694
|
-
normalizePageName(page) {
|
|
695
|
-
let normalized = page.toLowerCase();
|
|
696
|
-
normalized = normalized.replace(/:\s+/g, ":");
|
|
697
|
-
normalized = normalized.replace(/\s+/g, "-").trim();
|
|
698
|
-
if (!normalized.startsWith("/")) {
|
|
699
|
-
normalized = normalized.replace(/\//g, "-");
|
|
700
|
-
}
|
|
701
|
-
return normalized;
|
|
982
|
+
return resolvePageLink(location, this.page);
|
|
702
983
|
}
|
|
703
984
|
renderAttributes(attributes) {
|
|
704
|
-
|
|
705
|
-
let result = "";
|
|
706
|
-
for (const [key, value] of Object.entries(safe)) {
|
|
707
|
-
if (value !== "") {
|
|
708
|
-
result += ` ${key}="${escapeAttr(value)}"`;
|
|
709
|
-
} else {
|
|
710
|
-
result += ` ${key}=""`;
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
return result;
|
|
985
|
+
return renderAttributeString(attributes);
|
|
714
986
|
}
|
|
715
987
|
}
|
|
716
988
|
|
|
717
|
-
// packages/render/src/
|
|
989
|
+
// packages/render/src/render/collected-styles.ts
|
|
718
990
|
var import_ast2 = require("@wdprlib/ast");
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
ctx.push("</div>");
|
|
991
|
+
|
|
992
|
+
// packages/render/src/render/style-tag.ts
|
|
993
|
+
function pushStyleTag(ctx, css) {
|
|
994
|
+
ctx.push(`<style>${escapeStyleContent(css)}</style>`);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
// packages/render/src/render/collected-styles.ts
|
|
998
|
+
function renderCollectedStyles(ctx, styles) {
|
|
999
|
+
if (!ctx.settings.allowStyleElements || !styles?.length)
|
|
729
1000
|
return;
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
1001
|
+
for (const style of styles) {
|
|
1002
|
+
if (style.startsWith(import_ast2.STYLE_SLOT_PREFIX)) {
|
|
1003
|
+
renderStyleSlot(ctx, style);
|
|
1004
|
+
} else {
|
|
1005
|
+
pushStyleTag(ctx, style);
|
|
1006
|
+
}
|
|
733
1007
|
}
|
|
734
1008
|
}
|
|
735
|
-
function
|
|
736
|
-
const
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
ctx.push(`<${tag} id="${tocId}"${renderAttrs(attributes)}>`);
|
|
740
|
-
} else {
|
|
741
|
-
ctx.push(`<${tag}${renderAttrs(attributes)}>`);
|
|
1009
|
+
function renderStyleSlot(ctx, marker) {
|
|
1010
|
+
const slotId = parseInt(marker.slice(import_ast2.STYLE_SLOT_PREFIX.length), 10);
|
|
1011
|
+
for (const css of ctx.getStyleSlotContents(slotId)) {
|
|
1012
|
+
pushStyleTag(ctx, css);
|
|
742
1013
|
}
|
|
743
|
-
ctx.push("<span>");
|
|
744
|
-
renderElements(ctx, elements);
|
|
745
|
-
ctx.push("</span>");
|
|
746
|
-
ctx.push(`</${tag}>`);
|
|
747
1014
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
ctx.push(`<strong${renderAttrs(attributes)}>`);
|
|
757
|
-
renderElements(ctx, elements);
|
|
758
|
-
ctx.push("</strong>");
|
|
759
|
-
break;
|
|
760
|
-
case "italics":
|
|
761
|
-
ctx.push(`<em${renderAttrs(attributes)}>`);
|
|
762
|
-
renderElements(ctx, elements);
|
|
763
|
-
ctx.push("</em>");
|
|
764
|
-
break;
|
|
765
|
-
case "underline":
|
|
766
|
-
ctx.push(`<span style="text-decoration: underline;"${renderAttrs(attributes)}>`);
|
|
767
|
-
renderElements(ctx, elements);
|
|
768
|
-
ctx.push("</span>");
|
|
769
|
-
break;
|
|
770
|
-
case "strikethrough":
|
|
771
|
-
ctx.push(`<span style="text-decoration: line-through;"${renderAttrs(attributes)}>`);
|
|
772
|
-
renderElements(ctx, elements);
|
|
773
|
-
ctx.push("</span>");
|
|
774
|
-
break;
|
|
775
|
-
case "superscript":
|
|
776
|
-
ctx.push(`<sup${renderAttrs(attributes)}>`);
|
|
777
|
-
renderElements(ctx, elements);
|
|
778
|
-
ctx.push("</sup>");
|
|
779
|
-
break;
|
|
780
|
-
case "subscript":
|
|
781
|
-
ctx.push(`<sub${renderAttrs(attributes)}>`);
|
|
782
|
-
renderElements(ctx, elements);
|
|
783
|
-
ctx.push("</sub>");
|
|
784
|
-
break;
|
|
785
|
-
case "monospace":
|
|
786
|
-
ctx.push(`<tt${renderAttrs(attributes)}>`);
|
|
787
|
-
renderElements(ctx, elements);
|
|
788
|
-
ctx.push("</tt>");
|
|
789
|
-
break;
|
|
790
|
-
case "span":
|
|
791
|
-
ctx.push(`<span${renderAttrs(attributes)}>`);
|
|
792
|
-
renderElements(ctx, elements);
|
|
793
|
-
ctx.push("</span>");
|
|
794
|
-
break;
|
|
795
|
-
case "div":
|
|
796
|
-
if (elements.length === 0 && Object.keys(attributes).length === 0) {
|
|
797
|
-
break;
|
|
798
|
-
}
|
|
799
|
-
ctx.push(`<div${renderAttrs(attributes)}>`);
|
|
800
|
-
renderElements(ctx, elements);
|
|
801
|
-
ctx.push("</div>");
|
|
802
|
-
break;
|
|
803
|
-
case "blockquote":
|
|
804
|
-
ctx.push(`<blockquote${renderAttrs(attributes)}>`);
|
|
805
|
-
renderElements(ctx, elements);
|
|
806
|
-
ctx.push("</blockquote>");
|
|
807
|
-
break;
|
|
808
|
-
case "mark":
|
|
809
|
-
ctx.push(`<mark${renderAttrs(attributes)}>`);
|
|
810
|
-
renderElements(ctx, elements);
|
|
811
|
-
ctx.push("</mark>");
|
|
812
|
-
break;
|
|
813
|
-
case "insertion":
|
|
814
|
-
ctx.push(`<ins${renderAttrs(attributes)}>`);
|
|
815
|
-
renderElements(ctx, elements);
|
|
816
|
-
ctx.push("</ins>");
|
|
817
|
-
break;
|
|
818
|
-
case "deletion":
|
|
819
|
-
ctx.push(`<del${renderAttrs(attributes)}>`);
|
|
820
|
-
renderElements(ctx, elements);
|
|
821
|
-
ctx.push("</del>");
|
|
822
|
-
break;
|
|
823
|
-
case "size":
|
|
824
|
-
renderSizeContainer(ctx, attributes, elements);
|
|
825
|
-
break;
|
|
826
|
-
case "hidden":
|
|
827
|
-
ctx.push(`<span style="display: none"${renderAttrs(attributes)}>`);
|
|
828
|
-
renderElements(ctx, elements);
|
|
829
|
-
ctx.push("</span>");
|
|
830
|
-
break;
|
|
831
|
-
case "invisible":
|
|
832
|
-
ctx.push(`<span style="visibility: hidden"${renderAttrs(attributes)}>`);
|
|
833
|
-
renderElements(ctx, elements);
|
|
834
|
-
ctx.push("</span>");
|
|
835
|
-
break;
|
|
836
|
-
case "ruby":
|
|
837
|
-
ctx.push(`<ruby${renderAttrs(attributes)}>`);
|
|
838
|
-
renderElements(ctx, elements);
|
|
839
|
-
ctx.push("</ruby>");
|
|
840
|
-
break;
|
|
841
|
-
case "ruby-text":
|
|
842
|
-
ctx.push(`<rt${renderAttrs(attributes)}>`);
|
|
843
|
-
renderElements(ctx, elements);
|
|
844
|
-
ctx.push("</rt>");
|
|
845
|
-
break;
|
|
846
|
-
case "heading":
|
|
847
|
-
renderElements(ctx, elements);
|
|
848
|
-
break;
|
|
849
|
-
case "collapsible":
|
|
850
|
-
renderElements(ctx, elements);
|
|
851
|
-
break;
|
|
852
|
-
case "definition-list":
|
|
853
|
-
ctx.push("<dl>");
|
|
854
|
-
renderElements(ctx, elements);
|
|
855
|
-
ctx.push("</dl>");
|
|
856
|
-
break;
|
|
857
|
-
case "definition-list-item":
|
|
858
|
-
renderElements(ctx, elements);
|
|
859
|
-
break;
|
|
860
|
-
case "definition-list-key":
|
|
861
|
-
ctx.push("<dt>");
|
|
862
|
-
renderElements(ctx, elements);
|
|
863
|
-
ctx.push("</dt>");
|
|
864
|
-
break;
|
|
865
|
-
case "definition-list-value":
|
|
866
|
-
ctx.push("<dd>");
|
|
867
|
-
renderElements(ctx, elements);
|
|
868
|
-
ctx.push("</dd>");
|
|
869
|
-
break;
|
|
870
|
-
case "table-row":
|
|
871
|
-
ctx.push(`<tr${renderAttrs(attributes)}>`);
|
|
872
|
-
renderElements(ctx, elements);
|
|
873
|
-
ctx.push("</tr>");
|
|
874
|
-
break;
|
|
875
|
-
case "table-cell":
|
|
876
|
-
ctx.push(`<td${renderAttrs(attributes)}>`);
|
|
877
|
-
renderElements(ctx, elements);
|
|
878
|
-
ctx.push("</td>");
|
|
879
|
-
break;
|
|
880
|
-
default:
|
|
881
|
-
renderElements(ctx, elements);
|
|
1015
|
+
|
|
1016
|
+
// packages/render/src/elements/bibliography/ids.ts
|
|
1017
|
+
function generateBibliographyIdSuffix(label, counter) {
|
|
1018
|
+
let h = 2166136261;
|
|
1019
|
+
const input = label + counter;
|
|
1020
|
+
for (let i = 0;i < input.length; i++) {
|
|
1021
|
+
h ^= input.charCodeAt(i);
|
|
1022
|
+
h = Math.imul(h, 16777619);
|
|
882
1023
|
}
|
|
1024
|
+
return (h >>> 0).toString(16).slice(0, 6);
|
|
883
1025
|
}
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
ctx.push(
|
|
1026
|
+
|
|
1027
|
+
// packages/render/src/elements/bibliography/cite.ts
|
|
1028
|
+
function renderBibliographyCite(ctx, data) {
|
|
1029
|
+
const number = ctx.getBibliographyCitationNumber(data.label);
|
|
1030
|
+
const counter = ctx.nextBibciteCounter();
|
|
1031
|
+
if (number === undefined) {
|
|
1032
|
+
ctx.push(escapeHtml(data.label));
|
|
1033
|
+
return;
|
|
891
1034
|
}
|
|
892
|
-
|
|
893
|
-
ctx.
|
|
1035
|
+
const idSuffix = generateBibliographyIdSuffix(data.label, counter);
|
|
1036
|
+
const id = ctx.generateId(`bibcite-${number}-`, idSuffix);
|
|
1037
|
+
const bibitemId = ctx.generateId("bibitem-", number);
|
|
1038
|
+
const onclick = `WIKIDOT.page.utils.scrollToReference('${bibitemId}')`;
|
|
1039
|
+
ctx.push(`<a href="javascript:;" class="bibcite" id="${id}" onclick="${escapeAttr(onclick)}">`);
|
|
1040
|
+
ctx.push(String(number));
|
|
1041
|
+
ctx.push("</a>");
|
|
894
1042
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
}
|
|
1043
|
+
// packages/render/src/elements/bibliography/block.ts
|
|
1044
|
+
function renderBibliographyBlock(ctx, data, renderElements) {
|
|
1045
|
+
if (data.hide)
|
|
1046
|
+
return;
|
|
1047
|
+
const title = data.title ?? "Bibliography";
|
|
1048
|
+
ctx.push(`<div class="bibitems">`);
|
|
1049
|
+
ctx.push(`<div class="title">${escapeHtml(title)}</div>`);
|
|
1050
|
+
let index = 1;
|
|
1051
|
+
for (const entry of data.entries) {
|
|
1052
|
+
const itemId = ctx.generateId("bibitem-", index);
|
|
1053
|
+
ctx.push(`<div class="bibitem" id="${itemId}">`);
|
|
1054
|
+
ctx.push(`${index}. `);
|
|
1055
|
+
renderElements(ctx, entry.value);
|
|
1056
|
+
ctx.push("</div>");
|
|
1057
|
+
index++;
|
|
906
1058
|
}
|
|
907
|
-
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
// packages/render/src/elements/text.ts
|
|
911
|
-
function renderText(ctx, data) {
|
|
912
|
-
ctx.pushEscaped(data);
|
|
1059
|
+
ctx.push("</div>");
|
|
913
1060
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
ctx.push(`<span style="white-space: pre-wrap;">`);
|
|
918
|
-
ctx.push(escapeHtml(data).replace(/ /g, " "));
|
|
919
|
-
ctx.push("</span>");
|
|
1061
|
+
// packages/render/src/elements/clear-float.ts
|
|
1062
|
+
function renderClearFloat(ctx, direction) {
|
|
1063
|
+
ctx.push(`<div style="clear:${direction}; height: 0px; font-size: 1px"></div>`);
|
|
920
1064
|
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1065
|
+
|
|
1066
|
+
// packages/render/src/libs/highlighter/engine/html.ts
|
|
1067
|
+
function escapeHighlightHtml(str) {
|
|
1068
|
+
if (str.indexOf("&") === -1 && str.indexOf("<") === -1 && str.indexOf(">") === -1 && str.indexOf('"') === -1) {
|
|
1069
|
+
return str;
|
|
925
1070
|
}
|
|
926
|
-
|
|
1071
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
927
1072
|
}
|
|
928
1073
|
|
|
929
|
-
// packages/render/src/
|
|
930
|
-
function
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
const isSpecialPage = page.startsWith("//") || page.includes("#/");
|
|
943
|
-
if (!isSpecialPage) {
|
|
944
|
-
const hashIdx = page.indexOf("#");
|
|
945
|
-
const pageToCheck = hashIdx !== -1 ? page.slice(0, hashIdx) : page;
|
|
946
|
-
const pageExists = ctx.page?.pageExists;
|
|
947
|
-
const exists = pageExists ? pageExists(pageToCheck) : false;
|
|
948
|
-
if (!exists) {
|
|
949
|
-
attrs.push(`class="newpage"`);
|
|
1074
|
+
// packages/render/src/libs/highlighter/engine/render.ts
|
|
1075
|
+
function renderTokens(tokens) {
|
|
1076
|
+
if (tokens.length === 0)
|
|
1077
|
+
return "";
|
|
1078
|
+
let html = "";
|
|
1079
|
+
let lastClass = "";
|
|
1080
|
+
for (const token of tokens) {
|
|
1081
|
+
if (token.content.length === 0)
|
|
1082
|
+
continue;
|
|
1083
|
+
const escaped = escapeHighlightHtml(token.content);
|
|
1084
|
+
if (token.class !== lastClass) {
|
|
1085
|
+
if (lastClass) {
|
|
1086
|
+
html += "</span>";
|
|
950
1087
|
}
|
|
1088
|
+
html += `<span class="hl-${token.class}">`;
|
|
1089
|
+
lastClass = token.class;
|
|
951
1090
|
}
|
|
1091
|
+
html += escaped;
|
|
952
1092
|
}
|
|
953
|
-
if (
|
|
954
|
-
|
|
955
|
-
"new-tab": "_blank",
|
|
956
|
-
parent: "_parent",
|
|
957
|
-
top: "_top",
|
|
958
|
-
same: "_self"
|
|
959
|
-
};
|
|
960
|
-
const targetValue = targetMap[data.target] ?? "_blank";
|
|
961
|
-
attrs.push(`target="${targetValue}"`);
|
|
962
|
-
if (targetValue === "_blank") {
|
|
963
|
-
attrs.push(`rel="noopener noreferrer"`);
|
|
964
|
-
}
|
|
1093
|
+
if (lastClass) {
|
|
1094
|
+
html += "</span>";
|
|
965
1095
|
}
|
|
966
|
-
|
|
967
|
-
renderLinkLabel(ctx, data);
|
|
968
|
-
ctx.push("</a>");
|
|
1096
|
+
return `<div class="hl-main"><pre>${html}</pre></div>`;
|
|
969
1097
|
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
ctx.pushEscaped(data.link.page);
|
|
976
|
-
}
|
|
977
|
-
return;
|
|
978
|
-
}
|
|
979
|
-
if ("text" in data.label) {
|
|
980
|
-
ctx.pushEscaped(data.label.text);
|
|
981
|
-
return;
|
|
982
|
-
}
|
|
983
|
-
if ("url" in data.label) {
|
|
984
|
-
const href = ctx.resolvePageLink(data.link);
|
|
985
|
-
ctx.pushEscaped(data.label.url ?? href);
|
|
986
|
-
}
|
|
1098
|
+
// packages/render/src/libs/highlighter/engine/utils.ts
|
|
1099
|
+
function findGroupPosition(str, match, groupIndex, matchStart) {
|
|
1100
|
+
const groupStr = match[groupIndex];
|
|
1101
|
+
const idx = str.indexOf(groupStr, matchStart);
|
|
1102
|
+
return idx >= 0 ? idx : matchStart;
|
|
987
1103
|
}
|
|
988
|
-
function
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1104
|
+
function escapeRegex(str) {
|
|
1105
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1106
|
+
}
|
|
1107
|
+
function matchingBrackets(str) {
|
|
1108
|
+
return str.replace(/[()<>[\]{}]/g, (char) => MATCHING_BRACKETS[char] ?? char);
|
|
1109
|
+
}
|
|
1110
|
+
var MATCHING_BRACKETS = {
|
|
1111
|
+
"(": ")",
|
|
1112
|
+
")": "(",
|
|
1113
|
+
"<": ">",
|
|
1114
|
+
">": "<",
|
|
1115
|
+
"[": "]",
|
|
1116
|
+
"]": "[",
|
|
1117
|
+
"{": "}",
|
|
1118
|
+
"}": "{"
|
|
1119
|
+
};
|
|
1120
|
+
|
|
1121
|
+
// packages/render/src/libs/highlighter/engine/end-pattern.ts
|
|
1122
|
+
function buildEndPattern(def, prevState, patternIndex, count, captureIndex, match, endRe) {
|
|
1123
|
+
if (!def.subst[prevState]?.[patternIndex] || !endRe) {
|
|
1124
|
+
return endRe ?? null;
|
|
1125
|
+
}
|
|
1126
|
+
let epSource = endRe.source;
|
|
1127
|
+
for (let k = 0;k <= count; k++) {
|
|
1128
|
+
const subIdx = captureIndex + k;
|
|
1129
|
+
if (subIdx >= match.length || match[subIdx] == null)
|
|
1130
|
+
break;
|
|
1131
|
+
const quoted = escapeRegex(match[subIdx]);
|
|
1132
|
+
epSource = epSource.replace(`%${k}%`, quoted);
|
|
1133
|
+
epSource = epSource.replace(`%b${k}%`, matchingBrackets(quoted));
|
|
993
1134
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1135
|
+
return new RegExp(epSource, endRe.flags);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
// packages/render/src/libs/highlighter/engine/keywords.ts
|
|
1139
|
+
function resolveKeywordClass(def, state, patternIndex, matchStr, fallback) {
|
|
1140
|
+
let kwDef = def.keywords[state]?.[patternIndex];
|
|
1141
|
+
if (!kwDef || kwDef === -1 || typeof kwDef !== "object" || Object.keys(kwDef).length === 0) {
|
|
1142
|
+
kwDef = def.keywords[-1]?.[patternIndex];
|
|
1143
|
+
}
|
|
1144
|
+
if (kwDef && kwDef !== -1 && typeof kwDef === "object") {
|
|
1145
|
+
for (const [group, re] of Object.entries(kwDef)) {
|
|
1146
|
+
if (re.test(matchStr)) {
|
|
1147
|
+
return def.kwmap[group] ?? fallback;
|
|
1148
|
+
}
|
|
1007
1149
|
}
|
|
1008
1150
|
}
|
|
1009
|
-
|
|
1010
|
-
if (key === "href" || key === "target")
|
|
1011
|
-
continue;
|
|
1012
|
-
attrs.push(`${key}="${escapeAttr(value)}"`);
|
|
1013
|
-
}
|
|
1014
|
-
ctx.push(`<a ${attrs.join(" ")}>`);
|
|
1015
|
-
renderElements(ctx, data.elements);
|
|
1016
|
-
ctx.push("</a>");
|
|
1017
|
-
}
|
|
1018
|
-
function renderAnchorName(ctx, name) {
|
|
1019
|
-
ctx.push(`<a name="${escapeAttr(name)}"></a>`);
|
|
1151
|
+
return fallback;
|
|
1020
1152
|
}
|
|
1021
1153
|
|
|
1022
|
-
// packages/render/src/
|
|
1023
|
-
function
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
}
|
|
1030
|
-
const safeAttrs = sanitizeAttributes(data.attributes);
|
|
1031
|
-
const alt = safeAttrs.alt ?? getFilenameFromSource(data.source);
|
|
1032
|
-
const className = safeAttrs.class ?? "image";
|
|
1033
|
-
const imgAttrs = [`src="${escapeAttr(src)}"`];
|
|
1034
|
-
for (const [key, value] of Object.entries(safeAttrs)) {
|
|
1035
|
-
if (key === "alt" || key === "class" || key === "src" || key === "srcset")
|
|
1154
|
+
// packages/render/src/libs/highlighter/engine/parts.ts
|
|
1155
|
+
function buildPartTokens(str, match, partDef, captureIndex, count, groupStart, matchStr, inner) {
|
|
1156
|
+
const parts = [];
|
|
1157
|
+
let partpos = groupStart;
|
|
1158
|
+
for (let j = 1;j <= count; j++) {
|
|
1159
|
+
const subIdx = j + captureIndex;
|
|
1160
|
+
if (subIdx >= match.length || match[subIdx] == null || match[subIdx] === "")
|
|
1036
1161
|
continue;
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
}
|
|
1045
|
-
const imgTag = `<img ${imgAttrs.join(" ")} />`;
|
|
1046
|
-
let output = imgTag;
|
|
1047
|
-
if (data.link) {
|
|
1048
|
-
let href;
|
|
1049
|
-
if (typeof data.link === "string") {
|
|
1050
|
-
if (!data.link.startsWith("/") && !data.link.startsWith("#") && !data.link.startsWith("http://") && !data.link.startsWith("https://")) {
|
|
1051
|
-
href = `/${data.link}`;
|
|
1052
|
-
} else {
|
|
1053
|
-
href = data.link;
|
|
1162
|
+
const subStr = match[subIdx];
|
|
1163
|
+
const subStart = str.indexOf(subStr, partpos);
|
|
1164
|
+
if (subStart < 0)
|
|
1165
|
+
continue;
|
|
1166
|
+
if (partDef[j]) {
|
|
1167
|
+
if (subStart > partpos) {
|
|
1168
|
+
parts.unshift({ class: inner, content: str.substring(partpos, subStart) });
|
|
1054
1169
|
}
|
|
1055
|
-
|
|
1056
|
-
href = `/${data.link.page}`;
|
|
1057
|
-
}
|
|
1058
|
-
if (isDangerousUrl(href)) {
|
|
1059
|
-
href = "#invalid-url";
|
|
1170
|
+
parts.unshift({ class: partDef[j], content: subStr });
|
|
1060
1171
|
}
|
|
1061
|
-
|
|
1172
|
+
partpos = subStart + subStr.length;
|
|
1062
1173
|
}
|
|
1063
|
-
if (
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
} else {
|
|
1069
|
-
ctx.push(output);
|
|
1174
|
+
if (partpos < groupStart + matchStr.length) {
|
|
1175
|
+
parts.unshift({
|
|
1176
|
+
class: inner,
|
|
1177
|
+
content: str.substring(partpos, groupStart + matchStr.length)
|
|
1178
|
+
});
|
|
1070
1179
|
}
|
|
1180
|
+
return parts;
|
|
1071
1181
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1182
|
+
|
|
1183
|
+
// packages/render/src/libs/highlighter/engine/preprocess.ts
|
|
1184
|
+
function preprocessHighlightInput(input) {
|
|
1185
|
+
return input.replace(/\r\n/g, `
|
|
1186
|
+
`).replace(/^$/gm, " ").replace(/\t/g, " ").replace(/\s+$/, "");
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
// packages/render/src/libs/highlighter/engine/tokenizer.ts
|
|
1190
|
+
function tokenize(def, input) {
|
|
1191
|
+
const str = preprocessHighlightInput(input);
|
|
1192
|
+
const len = str.length;
|
|
1193
|
+
if (len === 0)
|
|
1194
|
+
return [];
|
|
1195
|
+
let state = -1;
|
|
1196
|
+
let pos = 0;
|
|
1197
|
+
let lastinner = def.defClass;
|
|
1198
|
+
let lastdelim = def.defClass;
|
|
1199
|
+
let endpattern = null;
|
|
1200
|
+
const stateStack = [];
|
|
1201
|
+
const tokenStack = [];
|
|
1202
|
+
const result = [];
|
|
1203
|
+
function getToken() {
|
|
1204
|
+
if (tokenStack.length > 0) {
|
|
1205
|
+
return tokenStack.pop();
|
|
1206
|
+
}
|
|
1207
|
+
if (pos >= len) {
|
|
1208
|
+
return null;
|
|
1209
|
+
}
|
|
1210
|
+
const endStateMatch = findEndStateMatch(str, pos, state, endpattern);
|
|
1211
|
+
const token2 = matchStateToken({
|
|
1212
|
+
def,
|
|
1213
|
+
str,
|
|
1214
|
+
pos,
|
|
1215
|
+
state,
|
|
1216
|
+
lastinner,
|
|
1217
|
+
lastdelim,
|
|
1218
|
+
endpattern,
|
|
1219
|
+
stateStack,
|
|
1220
|
+
tokenStack,
|
|
1221
|
+
endStateMatch,
|
|
1222
|
+
setPosition: (nextPos) => {
|
|
1223
|
+
pos = nextPos;
|
|
1224
|
+
},
|
|
1225
|
+
setState: (next) => {
|
|
1226
|
+
state = next.state;
|
|
1227
|
+
lastinner = next.lastinner;
|
|
1228
|
+
lastdelim = next.lastdelim;
|
|
1229
|
+
endpattern = next.endpattern;
|
|
1230
|
+
}
|
|
1231
|
+
});
|
|
1232
|
+
if (token2) {
|
|
1233
|
+
return token2;
|
|
1234
|
+
}
|
|
1235
|
+
if (endStateMatch.endpos > -1) {
|
|
1236
|
+
tokenStack.push({ class: lastdelim, content: endStateMatch.endmatch });
|
|
1237
|
+
if (endStateMatch.endpos > pos) {
|
|
1238
|
+
tokenStack.push({ class: lastinner, content: str.substring(pos, endStateMatch.endpos) });
|
|
1239
|
+
}
|
|
1240
|
+
const prev = stateStack.pop();
|
|
1241
|
+
state = prev.state;
|
|
1242
|
+
lastdelim = prev.lastdelim;
|
|
1243
|
+
lastinner = prev.lastinner;
|
|
1244
|
+
endpattern = prev.endpattern;
|
|
1245
|
+
pos = endStateMatch.endpos + endStateMatch.endmatch.length;
|
|
1246
|
+
if (tokenStack.length > 0) {
|
|
1247
|
+
return tokenStack.pop();
|
|
1248
|
+
}
|
|
1249
|
+
return getToken();
|
|
1083
1250
|
}
|
|
1251
|
+
const p = pos;
|
|
1252
|
+
pos = len;
|
|
1253
|
+
return { class: lastinner, content: str.substring(p) };
|
|
1084
1254
|
}
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
case "right":
|
|
1089
|
-
return "alignright";
|
|
1090
|
-
case "center":
|
|
1091
|
-
return "aligncenter";
|
|
1092
|
-
default:
|
|
1093
|
-
return `align${align}`;
|
|
1255
|
+
let token;
|
|
1256
|
+
while ((token = getToken()) !== null) {
|
|
1257
|
+
result.push(token);
|
|
1094
1258
|
}
|
|
1259
|
+
return result;
|
|
1095
1260
|
}
|
|
1096
|
-
function
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
const parts = source.data.split("/");
|
|
1100
|
-
return parts[parts.length - 1] ?? source.data;
|
|
1101
|
-
}
|
|
1102
|
-
case "file1":
|
|
1103
|
-
return source.data.file;
|
|
1104
|
-
case "file2":
|
|
1105
|
-
return source.data.file;
|
|
1106
|
-
case "file3":
|
|
1107
|
-
return source.data.file;
|
|
1261
|
+
function findEndStateMatch(str, pos, state, endpattern) {
|
|
1262
|
+
if (state === -1 || !endpattern) {
|
|
1263
|
+
return { endpos: -1, endmatch: "" };
|
|
1108
1264
|
}
|
|
1265
|
+
endpattern.lastIndex = pos;
|
|
1266
|
+
const match = endpattern.exec(str);
|
|
1267
|
+
return match ? { endpos: match.index, endmatch: match[0] } : { endpos: -1, endmatch: "" };
|
|
1109
1268
|
}
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
while (end > start) {
|
|
1126
|
-
const el = elements[end - 1];
|
|
1127
|
-
if (el.element === "text" && typeof el.data === "string" && el.data.trim() === "") {
|
|
1128
|
-
end--;
|
|
1129
|
-
} else {
|
|
1269
|
+
function matchStateToken(args) {
|
|
1270
|
+
const reg = args.def.regs[args.state];
|
|
1271
|
+
if (!reg)
|
|
1272
|
+
return null;
|
|
1273
|
+
reg.lastIndex = args.pos;
|
|
1274
|
+
const match = reg.exec(args.str);
|
|
1275
|
+
if (!match)
|
|
1276
|
+
return null;
|
|
1277
|
+
const countsArr = args.def.counts[args.state];
|
|
1278
|
+
let captureIndex = 1;
|
|
1279
|
+
for (let patternIndex = 0;patternIndex < countsArr.length; patternIndex++) {
|
|
1280
|
+
const count = countsArr[patternIndex];
|
|
1281
|
+
if (captureIndex >= match.length)
|
|
1130
1282
|
break;
|
|
1283
|
+
if (match[captureIndex] != null && (args.endStateMatch.endpos === -1 || match.index < args.endStateMatch.endpos)) {
|
|
1284
|
+
return emitMatchedPattern(args, match, patternIndex, captureIndex, count);
|
|
1131
1285
|
}
|
|
1286
|
+
captureIndex += count + 1;
|
|
1132
1287
|
}
|
|
1133
|
-
return
|
|
1134
|
-
}
|
|
1135
|
-
function isLiCloseTextParagraph(el) {
|
|
1136
|
-
if (el.element !== "container")
|
|
1137
|
-
return false;
|
|
1138
|
-
const data = el.data;
|
|
1139
|
-
if (data.type !== "paragraph")
|
|
1140
|
-
return false;
|
|
1141
|
-
const texts = data.elements.filter((e) => e.element === "text").map((e) => e.data);
|
|
1142
|
-
const combined = texts.join("").trim();
|
|
1143
|
-
return combined === "[[/li]]";
|
|
1288
|
+
return null;
|
|
1144
1289
|
}
|
|
1145
|
-
function
|
|
1146
|
-
const
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
const
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1290
|
+
function emitMatchedPattern(args, match, patternIndex, captureIndex, count) {
|
|
1291
|
+
const statesArr = args.def.states[args.state];
|
|
1292
|
+
const delimArr = args.def.delim[args.state];
|
|
1293
|
+
const matchStart = match.index;
|
|
1294
|
+
const matchStr = match[captureIndex];
|
|
1295
|
+
const groupStart = findGroupPosition(args.str, match, captureIndex, matchStart);
|
|
1296
|
+
if (statesArr[patternIndex] !== -1) {
|
|
1297
|
+
args.tokenStack.push({ class: delimArr[patternIndex], content: matchStr });
|
|
1298
|
+
} else {
|
|
1299
|
+
pushNonTransitionTokens(args, match, patternIndex, captureIndex, count, groupStart, matchStr);
|
|
1155
1300
|
}
|
|
1156
|
-
if (
|
|
1157
|
-
|
|
1301
|
+
if (groupStart > args.pos) {
|
|
1302
|
+
args.tokenStack.push({
|
|
1303
|
+
class: args.lastinner,
|
|
1304
|
+
content: args.str.substring(args.pos, groupStart)
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
args.setPosition(groupStart + matchStr.length);
|
|
1308
|
+
if (statesArr[patternIndex] !== -1) {
|
|
1309
|
+
enterState(args, match, patternIndex, captureIndex, count);
|
|
1310
|
+
}
|
|
1311
|
+
return args.tokenStack.pop();
|
|
1312
|
+
}
|
|
1313
|
+
function pushNonTransitionTokens(args, match, patternIndex, captureIndex, count, groupStart, matchStr) {
|
|
1314
|
+
let inner = args.def.inner[args.state][patternIndex];
|
|
1315
|
+
const partDef = args.def.parts[args.state]?.[patternIndex];
|
|
1316
|
+
if (partDef) {
|
|
1317
|
+
pushPartTokens(args, match, partDef, captureIndex, count, groupStart, matchStr, inner);
|
|
1158
1318
|
return;
|
|
1159
1319
|
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
for (let i = 0;i < trimmed.length; i++) {
|
|
1163
|
-
const el = trimmed[i];
|
|
1164
|
-
if (el.element === "container" && el.data.type === "paragraph") {
|
|
1165
|
-
const data = el.data;
|
|
1166
|
-
if (i === firstParagraphIdx) {
|
|
1167
|
-
renderElements(ctx, data.elements);
|
|
1168
|
-
} else if (i === lastParagraphIdx && isLiCloseTextParagraph(el)) {
|
|
1169
|
-
renderElements(ctx, data.elements);
|
|
1170
|
-
} else {
|
|
1171
|
-
ctx.push("<p>");
|
|
1172
|
-
renderElements(ctx, data.elements);
|
|
1173
|
-
ctx.push("</p>");
|
|
1174
|
-
}
|
|
1175
|
-
} else {
|
|
1176
|
-
renderElement(ctx, el);
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
function renderList(ctx, data) {
|
|
1181
|
-
const hasContent = data.items.some((item) => {
|
|
1182
|
-
if (item["item-type"] === "sub-list")
|
|
1183
|
-
return true;
|
|
1184
|
-
if (item["item-type"] === "elements") {
|
|
1185
|
-
const trimmed = trimTextElements(item.elements);
|
|
1186
|
-
return trimmed.length > 0;
|
|
1187
|
-
}
|
|
1188
|
-
return false;
|
|
1189
|
-
});
|
|
1190
|
-
if (!hasContent) {
|
|
1191
|
-
return;
|
|
1192
|
-
}
|
|
1193
|
-
const tag = data.type === "numbered" ? "ol" : "ul";
|
|
1194
|
-
ctx.push(`<${tag}${renderListAttrs(data.attributes)}>`);
|
|
1195
|
-
const items = data.items;
|
|
1196
|
-
let i = 0;
|
|
1197
|
-
while (i < items.length) {
|
|
1198
|
-
const item = items[i];
|
|
1199
|
-
if (item["item-type"] === "elements") {
|
|
1200
|
-
const hasNoMarker = item.attributes._noMarker === "true";
|
|
1201
|
-
const styleAttr = hasNoMarker ? ' style="list-style: none"' : "";
|
|
1202
|
-
ctx.push(`<li${renderListAttrs(item.attributes)}${styleAttr}>`);
|
|
1203
|
-
if (hasNoMarker) {
|
|
1204
|
-
renderNoMarkerElements(ctx, item.elements);
|
|
1205
|
-
} else {
|
|
1206
|
-
renderElements(ctx, trimTextElements(item.elements));
|
|
1207
|
-
}
|
|
1208
|
-
while (i + 1 < items.length && items[i + 1]["item-type"] === "sub-list") {
|
|
1209
|
-
i++;
|
|
1210
|
-
const subItem = items[i];
|
|
1211
|
-
renderList(ctx, subItem.data);
|
|
1212
|
-
}
|
|
1213
|
-
ctx.push("</li>");
|
|
1214
|
-
} else {
|
|
1215
|
-
const subItem = item;
|
|
1216
|
-
ctx.push(`<li style="list-style: none; display: inline">`);
|
|
1217
|
-
renderList(ctx, subItem.data);
|
|
1218
|
-
ctx.push("</li>");
|
|
1219
|
-
}
|
|
1220
|
-
i++;
|
|
1221
|
-
}
|
|
1222
|
-
ctx.push(`</${tag}>`);
|
|
1223
|
-
}
|
|
1224
|
-
function renderListAttrs(attributes) {
|
|
1225
|
-
const safe = sanitizeAttributes(attributes);
|
|
1226
|
-
let result = "";
|
|
1227
|
-
for (const [key, value] of Object.entries(safe)) {
|
|
1228
|
-
if (key.startsWith("_"))
|
|
1229
|
-
continue;
|
|
1230
|
-
result += ` ${key}="${escapeAttr(value)}"`;
|
|
1231
|
-
}
|
|
1232
|
-
return result;
|
|
1233
|
-
}
|
|
1234
|
-
function renderDefinitionList(ctx, items) {
|
|
1235
|
-
ctx.push("<dl>");
|
|
1236
|
-
for (const item of items) {
|
|
1237
|
-
ctx.push("<dt>");
|
|
1238
|
-
renderElements(ctx, item.key);
|
|
1239
|
-
ctx.push("</dt>");
|
|
1240
|
-
ctx.push("<dd>");
|
|
1241
|
-
renderElements(ctx, item.value);
|
|
1242
|
-
ctx.push("</dd>");
|
|
1243
|
-
}
|
|
1244
|
-
ctx.push("</dl>");
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
// packages/render/src/elements/table.ts
|
|
1248
|
-
function renderTable(ctx, data) {
|
|
1249
|
-
const isPipeTable = data.attributes._source === "pipe";
|
|
1250
|
-
const classAttr = isPipeTable ? ' class="wiki-content-table"' : "";
|
|
1251
|
-
ctx.push(`<table${classAttr}${renderTableAttrs(data.attributes)}>`);
|
|
1252
|
-
for (const row of data.rows) {
|
|
1253
|
-
ctx.push(`<tr${renderTableAttrs(row.attributes)}>`);
|
|
1254
|
-
for (const cell of row.cells) {
|
|
1255
|
-
const tag = cell.header ? "th" : "td";
|
|
1256
|
-
const attrs = [];
|
|
1257
|
-
const safeCellAttrs = sanitizeAttributes(cell.attributes);
|
|
1258
|
-
if (cell["column-span"] > 1) {
|
|
1259
|
-
attrs.push(`colspan="${cell["column-span"]}"`);
|
|
1260
|
-
}
|
|
1261
|
-
if (safeCellAttrs.rowspan) {
|
|
1262
|
-
const rowspan = parseInt(safeCellAttrs.rowspan, 10);
|
|
1263
|
-
if (rowspan > 1) {
|
|
1264
|
-
attrs.push(`rowspan="${rowspan}"`);
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
if (cell.align) {
|
|
1268
|
-
const existingStyle = safeCellAttrs.style ?? "";
|
|
1269
|
-
const alignStyle = `text-align: ${cell.align};`;
|
|
1270
|
-
if (existingStyle) {
|
|
1271
|
-
attrs.push(`style="${escapeAttr(existingStyle + "; " + alignStyle)}"`);
|
|
1272
|
-
} else {
|
|
1273
|
-
attrs.push(`style="${alignStyle}"`);
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
for (const [key, value] of Object.entries(safeCellAttrs)) {
|
|
1277
|
-
if (key === "style" && cell.align)
|
|
1278
|
-
continue;
|
|
1279
|
-
if (key === "rowspan")
|
|
1280
|
-
continue;
|
|
1281
|
-
attrs.push(`${key}="${escapeAttr(value)}"`);
|
|
1282
|
-
}
|
|
1283
|
-
const attrStr = attrs.length > 0 ? " " + attrs.join(" ") : "";
|
|
1284
|
-
ctx.push(`<${tag}${attrStr}>`);
|
|
1285
|
-
renderElements(ctx, cell.elements);
|
|
1286
|
-
ctx.push(`</${tag}>`);
|
|
1287
|
-
}
|
|
1288
|
-
ctx.push("</tr>");
|
|
1289
|
-
}
|
|
1290
|
-
ctx.push("</table>");
|
|
1291
|
-
}
|
|
1292
|
-
function renderTableAttrs(attributes) {
|
|
1293
|
-
const safe = sanitizeAttributes(attributes);
|
|
1294
|
-
let result = "";
|
|
1295
|
-
for (const [key, value] of Object.entries(safe)) {
|
|
1296
|
-
if (key.startsWith("_"))
|
|
1297
|
-
continue;
|
|
1298
|
-
result += ` ${key}="${escapeAttr(value)}"`;
|
|
1299
|
-
}
|
|
1300
|
-
return result;
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
// packages/render/src/elements/collapsible.ts
|
|
1304
|
-
function renderCollapsible(ctx, data) {
|
|
1305
|
-
const startOpen = data["start-open"];
|
|
1306
|
-
const showTop = data["show-top"];
|
|
1307
|
-
const showBottom = data["show-bottom"];
|
|
1308
|
-
const showLabel = data["show-text"] ? formatLabelText(data["show-text"]) : formatCollapsibleText("+", "show block");
|
|
1309
|
-
const hideLabel = data["hide-text"] ? formatLabelText(data["hide-text"]) : formatCollapsibleText("–", "hide block");
|
|
1310
|
-
ctx.push(`<div class="collapsible-block">`);
|
|
1311
|
-
const foldedStyle = startOpen ? ` style="display:none"` : "";
|
|
1312
|
-
ctx.push(`<div class="collapsible-block-folded"${foldedStyle}>`);
|
|
1313
|
-
ctx.push(`<a class="collapsible-block-link" href="javascript:;">${showLabel}</a>`);
|
|
1314
|
-
ctx.push("</div>");
|
|
1315
|
-
const unfoldedStyle = startOpen ? "" : ` style="display:none"`;
|
|
1316
|
-
ctx.push(`<div class="collapsible-block-unfolded"${unfoldedStyle}>`);
|
|
1317
|
-
if (showTop || !showTop && !showBottom) {
|
|
1318
|
-
ctx.push(`<div class="collapsible-block-unfolded-link">`);
|
|
1319
|
-
ctx.push(`<a class="collapsible-block-link" href="javascript:;">${hideLabel}</a>`);
|
|
1320
|
-
ctx.push("</div>");
|
|
1321
|
-
}
|
|
1322
|
-
ctx.push(`<div class="collapsible-block-content">`);
|
|
1323
|
-
renderElements(ctx, data.elements);
|
|
1324
|
-
ctx.push("</div>");
|
|
1325
|
-
if (showBottom) {
|
|
1326
|
-
ctx.push(`<div class="collapsible-block-unfolded-link">`);
|
|
1327
|
-
ctx.push(`<a class="collapsible-block-link" href="javascript:;">${hideLabel}</a>`);
|
|
1328
|
-
ctx.push("</div>");
|
|
1329
|
-
}
|
|
1330
|
-
ctx.push("</div>");
|
|
1331
|
-
ctx.push("</div>");
|
|
1332
|
-
}
|
|
1333
|
-
function formatCollapsibleText(prefix, text) {
|
|
1334
|
-
const encoded = escapeHtml(text).replace(/ /g, " ");
|
|
1335
|
-
return `${prefix} ${encoded}`;
|
|
1336
|
-
}
|
|
1337
|
-
function formatLabelText(text) {
|
|
1338
|
-
return escapeHtml(text).replace(/ /g, " ");
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
// packages/render/src/libs/highlighter/engine.ts
|
|
1342
|
-
function tokenize(def, input) {
|
|
1343
|
-
let str = input.replace(/\r\n/g, `
|
|
1344
|
-
`);
|
|
1345
|
-
str = str.replace(/^$/gm, " ");
|
|
1346
|
-
str = str.replace(/\t/g, " ");
|
|
1347
|
-
str = str.replace(/\s+$/, "");
|
|
1348
|
-
const len = str.length;
|
|
1349
|
-
if (len === 0)
|
|
1350
|
-
return [];
|
|
1351
|
-
let state = -1;
|
|
1352
|
-
let pos = 0;
|
|
1353
|
-
let lastinner = def.defClass;
|
|
1354
|
-
let lastdelim = def.defClass;
|
|
1355
|
-
let endpattern = null;
|
|
1356
|
-
const stateStack = [];
|
|
1357
|
-
const tokenStack = [];
|
|
1358
|
-
const result = [];
|
|
1359
|
-
function getToken() {
|
|
1360
|
-
if (tokenStack.length > 0) {
|
|
1361
|
-
return tokenStack.pop();
|
|
1362
|
-
}
|
|
1363
|
-
if (pos >= len) {
|
|
1364
|
-
return null;
|
|
1365
|
-
}
|
|
1366
|
-
let endpos = -1;
|
|
1367
|
-
let endmatch = "";
|
|
1368
|
-
if (state !== -1 && endpattern) {
|
|
1369
|
-
endpattern.lastIndex = pos;
|
|
1370
|
-
const em = endpattern.exec(str);
|
|
1371
|
-
if (em) {
|
|
1372
|
-
endpos = em.index;
|
|
1373
|
-
endmatch = em[0];
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
|
-
const reg = def.regs[state];
|
|
1377
|
-
if (reg) {
|
|
1378
|
-
reg.lastIndex = pos;
|
|
1379
|
-
const m = reg.exec(str);
|
|
1380
|
-
if (m) {
|
|
1381
|
-
const countsArr = def.counts[state];
|
|
1382
|
-
const statesArr = def.states[state];
|
|
1383
|
-
const delimArr = def.delim[state];
|
|
1384
|
-
const innerArr = def.inner[state];
|
|
1385
|
-
let n = 1;
|
|
1386
|
-
for (let i = 0;i < countsArr.length; i++) {
|
|
1387
|
-
const count = countsArr[i];
|
|
1388
|
-
if (n >= m.length)
|
|
1389
|
-
break;
|
|
1390
|
-
if (m[n] != null && (endpos === -1 || m.index < endpos)) {
|
|
1391
|
-
const matchStart = m.index;
|
|
1392
|
-
const matchStr = m[n];
|
|
1393
|
-
const groupStart = findGroupPosition(str, m, n, matchStart);
|
|
1394
|
-
if (statesArr[i] !== -1) {
|
|
1395
|
-
tokenStack.push({ class: delimArr[i], content: matchStr });
|
|
1396
|
-
} else {
|
|
1397
|
-
let inner = innerArr[i];
|
|
1398
|
-
const partDef = def.parts[state]?.[i];
|
|
1399
|
-
if (partDef) {
|
|
1400
|
-
const parts = [];
|
|
1401
|
-
let partpos = groupStart;
|
|
1402
|
-
for (let j = 1;j <= count; j++) {
|
|
1403
|
-
const subIdx = j + n;
|
|
1404
|
-
if (subIdx >= m.length || m[subIdx] == null || m[subIdx] === "")
|
|
1405
|
-
continue;
|
|
1406
|
-
const subStr = m[subIdx];
|
|
1407
|
-
const subStart = str.indexOf(subStr, partpos);
|
|
1408
|
-
if (subStart < 0)
|
|
1409
|
-
continue;
|
|
1410
|
-
if (partDef[j]) {
|
|
1411
|
-
if (subStart > partpos) {
|
|
1412
|
-
parts.unshift({ class: inner, content: str.substring(partpos, subStart) });
|
|
1413
|
-
}
|
|
1414
|
-
parts.unshift({ class: partDef[j], content: subStr });
|
|
1415
|
-
}
|
|
1416
|
-
partpos = subStart + subStr.length;
|
|
1417
|
-
}
|
|
1418
|
-
if (partpos < groupStart + matchStr.length) {
|
|
1419
|
-
parts.unshift({
|
|
1420
|
-
class: inner,
|
|
1421
|
-
content: str.substring(partpos, groupStart + matchStr.length)
|
|
1422
|
-
});
|
|
1423
|
-
}
|
|
1424
|
-
tokenStack.push(...parts);
|
|
1425
|
-
} else {
|
|
1426
|
-
let kwDef = def.keywords[state]?.[i];
|
|
1427
|
-
if (!kwDef || kwDef === -1 || typeof kwDef !== "object" || Object.keys(kwDef).length === 0) {
|
|
1428
|
-
kwDef = def.keywords[-1]?.[i];
|
|
1429
|
-
}
|
|
1430
|
-
if (kwDef && kwDef !== -1 && typeof kwDef === "object") {
|
|
1431
|
-
for (const [group, re] of Object.entries(kwDef)) {
|
|
1432
|
-
if (re.test(matchStr)) {
|
|
1433
|
-
inner = def.kwmap[group] ?? inner;
|
|
1434
|
-
break;
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
tokenStack.push({ class: inner, content: matchStr });
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
if (groupStart > pos) {
|
|
1442
|
-
tokenStack.push({ class: lastinner, content: str.substring(pos, groupStart) });
|
|
1443
|
-
}
|
|
1444
|
-
pos = groupStart + matchStr.length;
|
|
1445
|
-
if (statesArr[i] !== -1) {
|
|
1446
|
-
stateStack.push({ state, lastdelim, lastinner, endpattern });
|
|
1447
|
-
lastinner = innerArr[i];
|
|
1448
|
-
lastdelim = delimArr[i];
|
|
1449
|
-
const prevState = state;
|
|
1450
|
-
state = statesArr[i];
|
|
1451
|
-
const endRe = def.end[state];
|
|
1452
|
-
if (def.subst[prevState]?.[i] && endRe) {
|
|
1453
|
-
let epSource = endRe.source;
|
|
1454
|
-
for (let k = 0;k <= count; k++) {
|
|
1455
|
-
const subIdx = n + k;
|
|
1456
|
-
if (subIdx >= m.length || m[subIdx] == null)
|
|
1457
|
-
break;
|
|
1458
|
-
const quoted = escapeRegex(m[subIdx]);
|
|
1459
|
-
epSource = epSource.replace(`%${k}%`, quoted);
|
|
1460
|
-
epSource = epSource.replace(`%b${k}%`, matchingBrackets(quoted));
|
|
1461
|
-
}
|
|
1462
|
-
endpattern = new RegExp(epSource, endRe.flags);
|
|
1463
|
-
} else {
|
|
1464
|
-
endpattern = endRe ?? null;
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
return tokenStack.pop();
|
|
1468
|
-
}
|
|
1469
|
-
n += count + 1;
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
if (endpos > -1) {
|
|
1474
|
-
tokenStack.push({ class: lastdelim, content: endmatch });
|
|
1475
|
-
if (endpos > pos) {
|
|
1476
|
-
tokenStack.push({ class: lastinner, content: str.substring(pos, endpos) });
|
|
1477
|
-
}
|
|
1478
|
-
const prev = stateStack.pop();
|
|
1479
|
-
state = prev.state;
|
|
1480
|
-
lastdelim = prev.lastdelim;
|
|
1481
|
-
lastinner = prev.lastinner;
|
|
1482
|
-
endpattern = prev.endpattern;
|
|
1483
|
-
pos = endpos + endmatch.length;
|
|
1484
|
-
if (tokenStack.length > 0) {
|
|
1485
|
-
return tokenStack.pop();
|
|
1486
|
-
}
|
|
1487
|
-
return getToken();
|
|
1488
|
-
}
|
|
1489
|
-
const p = pos;
|
|
1490
|
-
pos = len;
|
|
1491
|
-
return { class: lastinner, content: str.substring(p) };
|
|
1492
|
-
}
|
|
1493
|
-
let token;
|
|
1494
|
-
while ((token = getToken()) !== null) {
|
|
1495
|
-
result.push(token);
|
|
1496
|
-
}
|
|
1497
|
-
return result;
|
|
1498
|
-
}
|
|
1499
|
-
function findGroupPosition(str, m, n, matchStart) {
|
|
1500
|
-
const groupStr = m[n];
|
|
1501
|
-
const idx = str.indexOf(groupStr, matchStart);
|
|
1502
|
-
return idx >= 0 ? idx : matchStart;
|
|
1503
|
-
}
|
|
1504
|
-
function renderTokens(tokens) {
|
|
1505
|
-
if (tokens.length === 0)
|
|
1506
|
-
return "";
|
|
1507
|
-
let html = "";
|
|
1508
|
-
let lastClass = "";
|
|
1509
|
-
for (const token of tokens) {
|
|
1510
|
-
if (token.content.length === 0)
|
|
1511
|
-
continue;
|
|
1512
|
-
const escaped = escapeHtml2(token.content);
|
|
1513
|
-
if (token.class !== lastClass) {
|
|
1514
|
-
if (lastClass) {
|
|
1515
|
-
html += "</span>";
|
|
1516
|
-
}
|
|
1517
|
-
html += `<span class="hl-${token.class}">`;
|
|
1518
|
-
lastClass = token.class;
|
|
1519
|
-
}
|
|
1520
|
-
html += escaped;
|
|
1521
|
-
}
|
|
1522
|
-
if (lastClass) {
|
|
1523
|
-
html += "</span>";
|
|
1524
|
-
}
|
|
1525
|
-
return `<div class="hl-main"><pre>${html}</pre></div>`;
|
|
1320
|
+
inner = resolveKeywordClass(args.def, args.state, patternIndex, matchStr, inner);
|
|
1321
|
+
args.tokenStack.push({ class: inner, content: matchStr });
|
|
1526
1322
|
}
|
|
1527
|
-
function
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1323
|
+
function pushPartTokens(args, match, partDef, captureIndex, count, groupStart, matchStr, inner) {
|
|
1324
|
+
const parts = [];
|
|
1325
|
+
parts.push(...buildPartTokens(args.str, match, partDef, captureIndex, count, groupStart, matchStr, inner));
|
|
1326
|
+
args.tokenStack.push(...parts);
|
|
1532
1327
|
}
|
|
1533
|
-
function
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1328
|
+
function enterState(args, match, patternIndex, captureIndex, count) {
|
|
1329
|
+
const statesArr = args.def.states[args.state];
|
|
1330
|
+
const delimArr = args.def.delim[args.state];
|
|
1331
|
+
const innerArr = args.def.inner[args.state];
|
|
1332
|
+
args.stateStack.push({
|
|
1333
|
+
state: args.state,
|
|
1334
|
+
lastdelim: args.lastdelim,
|
|
1335
|
+
lastinner: args.lastinner,
|
|
1336
|
+
endpattern: args.endpattern
|
|
1337
|
+
});
|
|
1338
|
+
const prevState = args.state;
|
|
1339
|
+
const nextState = statesArr[patternIndex];
|
|
1340
|
+
const endRe = args.def.end[nextState];
|
|
1341
|
+
args.setState({
|
|
1342
|
+
state: nextState,
|
|
1343
|
+
lastinner: innerArr[patternIndex],
|
|
1344
|
+
lastdelim: delimArr[patternIndex],
|
|
1345
|
+
endpattern: buildEndPattern(args.def, prevState, patternIndex, count, captureIndex, match, endRe ?? undefined)
|
|
1546
1346
|
});
|
|
1547
1347
|
}
|
|
1548
|
-
|
|
1549
1348
|
// packages/render/src/libs/highlighter/languages/css.ts
|
|
1550
1349
|
var cssLang = {
|
|
1551
1350
|
language: "css",
|
|
@@ -4063,112 +3862,1190 @@ function highlight(code, language) {
|
|
|
4063
3862
|
return renderTokens(tokens);
|
|
4064
3863
|
}
|
|
4065
3864
|
|
|
4066
|
-
// packages/render/src/elements/code.ts
|
|
4067
|
-
function
|
|
4068
|
-
ctx.push(`<div class="code">`);
|
|
4069
|
-
if (data.contents === "") {
|
|
4070
|
-
ctx.push("</div>");
|
|
4071
|
-
return;
|
|
4072
|
-
}
|
|
3865
|
+
// packages/render/src/elements/code/contents.ts
|
|
3866
|
+
function renderCodeContents(data) {
|
|
4073
3867
|
if (data.language) {
|
|
4074
3868
|
const highlighted = highlight(data.contents, data.language);
|
|
4075
3869
|
if (highlighted) {
|
|
4076
|
-
|
|
4077
|
-
} else {
|
|
4078
|
-
ctx.push(`<pre><code>${escapeHtml(data.contents)}</code></pre>`);
|
|
3870
|
+
return highlighted;
|
|
4079
3871
|
}
|
|
4080
|
-
}
|
|
4081
|
-
|
|
3872
|
+
}
|
|
3873
|
+
return renderPlainCode(data.contents);
|
|
3874
|
+
}
|
|
3875
|
+
function renderPlainCode(contents) {
|
|
3876
|
+
return `<pre><code>${escapeHtml(contents)}</code></pre>`;
|
|
3877
|
+
}
|
|
3878
|
+
|
|
3879
|
+
// packages/render/src/elements/code/index.ts
|
|
3880
|
+
function renderCode(ctx, data) {
|
|
3881
|
+
ctx.push(`<div class="code">`);
|
|
3882
|
+
if (data.contents !== "") {
|
|
3883
|
+
ctx.push(renderCodeContents(data));
|
|
4082
3884
|
}
|
|
4083
3885
|
ctx.push("</div>");
|
|
4084
3886
|
}
|
|
4085
3887
|
|
|
4086
|
-
// packages/render/src/
|
|
4087
|
-
function
|
|
4088
|
-
return
|
|
3888
|
+
// packages/render/src/elements/collapsible/labels.ts
|
|
3889
|
+
function getCollapsibleLabels(data) {
|
|
3890
|
+
return {
|
|
3891
|
+
show: data["show-text"] ? formatLabelText(data["show-text"]) : formatCollapsibleText("+", "show block"),
|
|
3892
|
+
hide: data["hide-text"] ? formatLabelText(data["hide-text"]) : formatCollapsibleText("–", "hide block")
|
|
3893
|
+
};
|
|
4089
3894
|
}
|
|
4090
|
-
function
|
|
4091
|
-
|
|
3895
|
+
function formatCollapsibleText(prefix, text) {
|
|
3896
|
+
const encoded = escapeHtml(text).replace(/ /g, " ");
|
|
3897
|
+
return `${prefix} ${encoded}`;
|
|
4092
3898
|
}
|
|
4093
|
-
function
|
|
4094
|
-
|
|
4095
|
-
const rounds = Math.ceil(hexLen / 8);
|
|
4096
|
-
for (let round = 0;round < rounds; round++) {
|
|
4097
|
-
let h = 2166136261 ^ round;
|
|
4098
|
-
for (let i = 0;i < input.length; i++) {
|
|
4099
|
-
h ^= input.charCodeAt(i);
|
|
4100
|
-
h = Math.imul(h, 16777619);
|
|
4101
|
-
}
|
|
4102
|
-
result += (h >>> 0).toString(16).padStart(8, "0");
|
|
4103
|
-
}
|
|
4104
|
-
return result.substring(0, hexLen);
|
|
3899
|
+
function formatLabelText(text) {
|
|
3900
|
+
return escapeHtml(text).replace(/ /g, " ");
|
|
4105
3901
|
}
|
|
4106
3902
|
|
|
4107
|
-
// packages/render/src/elements/
|
|
4108
|
-
function
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
ctx.push(`<div
|
|
4113
|
-
ctx
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
}
|
|
4121
|
-
ctx.
|
|
4122
|
-
ctx.push(
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
ctx.push("</div>");
|
|
3903
|
+
// packages/render/src/elements/collapsible/link.ts
|
|
3904
|
+
function renderCollapsibleLink(ctx, label) {
|
|
3905
|
+
ctx.push(`<a class="collapsible-block-link" href="javascript:;">${label}</a>`);
|
|
3906
|
+
}
|
|
3907
|
+
function renderHideLink(ctx, label) {
|
|
3908
|
+
ctx.push(`<div class="collapsible-block-unfolded-link">`);
|
|
3909
|
+
renderCollapsibleLink(ctx, label);
|
|
3910
|
+
ctx.push("</div>");
|
|
3911
|
+
}
|
|
3912
|
+
|
|
3913
|
+
// packages/render/src/elements/collapsible/sections.ts
|
|
3914
|
+
function renderFoldedSection(ctx, startOpen, labels) {
|
|
3915
|
+
const foldedStyle = startOpen ? ` style="display:none"` : "";
|
|
3916
|
+
ctx.push(`<div class="collapsible-block-folded"${foldedStyle}>`);
|
|
3917
|
+
renderCollapsibleLink(ctx, labels.show);
|
|
3918
|
+
ctx.push("</div>");
|
|
3919
|
+
}
|
|
3920
|
+
function renderUnfoldedSection(ctx, data, labels) {
|
|
3921
|
+
const unfoldedStyle = data["start-open"] ? "" : ` style="display:none"`;
|
|
3922
|
+
ctx.push(`<div class="collapsible-block-unfolded"${unfoldedStyle}>`);
|
|
3923
|
+
if (data["show-top"] || !data["show-top"] && !data["show-bottom"]) {
|
|
3924
|
+
renderHideLink(ctx, labels.hide);
|
|
4130
3925
|
}
|
|
3926
|
+
ctx.push(`<div class="collapsible-block-content">`);
|
|
3927
|
+
renderElements(ctx, data.elements);
|
|
4131
3928
|
ctx.push("</div>");
|
|
3929
|
+
if (data["show-bottom"]) {
|
|
3930
|
+
renderHideLink(ctx, labels.hide);
|
|
3931
|
+
}
|
|
4132
3932
|
ctx.push("</div>");
|
|
4133
3933
|
}
|
|
4134
|
-
|
|
4135
|
-
|
|
3934
|
+
|
|
3935
|
+
// packages/render/src/elements/collapsible/index.ts
|
|
3936
|
+
function renderCollapsible(ctx, data) {
|
|
3937
|
+
const startOpen = data["start-open"];
|
|
3938
|
+
const labels = getCollapsibleLabels(data);
|
|
3939
|
+
ctx.push(`<div class="collapsible-block">`);
|
|
3940
|
+
renderFoldedSection(ctx, startOpen, labels);
|
|
3941
|
+
renderUnfoldedSection(ctx, data, labels);
|
|
3942
|
+
ctx.push("</div>");
|
|
4136
3943
|
}
|
|
4137
3944
|
|
|
4138
|
-
// packages/render/src/elements/
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
3945
|
+
// packages/render/src/elements/container/index.ts
|
|
3946
|
+
var import_ast3 = require("@wdprlib/ast");
|
|
3947
|
+
|
|
3948
|
+
// packages/render/src/elements/container/attributes.ts
|
|
3949
|
+
function renderContainerAttrs(attributes) {
|
|
3950
|
+
if (!hasAttributes(attributes)) {
|
|
3951
|
+
return "";
|
|
3952
|
+
}
|
|
3953
|
+
const safe = sanitizeAttributes(attributes);
|
|
3954
|
+
let result = "";
|
|
3955
|
+
for (const key in safe) {
|
|
3956
|
+
if (key.startsWith("_")) {
|
|
3957
|
+
continue;
|
|
3958
|
+
}
|
|
3959
|
+
const value = safe[key];
|
|
3960
|
+
result += value !== "" ? ` ${key}="${escapeAttr(value)}"` : ` ${key}=""`;
|
|
3961
|
+
}
|
|
3962
|
+
return result;
|
|
4144
3963
|
}
|
|
4145
|
-
function
|
|
4146
|
-
|
|
3964
|
+
function hasAttributes(attributes) {
|
|
3965
|
+
for (const _ in attributes) {
|
|
3966
|
+
return true;
|
|
3967
|
+
}
|
|
3968
|
+
return false;
|
|
3969
|
+
}
|
|
3970
|
+
|
|
3971
|
+
// packages/render/src/elements/container/header.ts
|
|
3972
|
+
function renderHeader(ctx, level, hasToc, attributes, elements) {
|
|
3973
|
+
const tag = `h${level}`;
|
|
3974
|
+
if (hasToc) {
|
|
3975
|
+
const tocId = ctx.generateId("toc", ctx.nextTocIndex());
|
|
3976
|
+
ctx.push(`<${tag} id="${tocId}"${renderContainerAttrs(attributes)}>`);
|
|
3977
|
+
} else {
|
|
3978
|
+
ctx.push(`<${tag}${renderContainerAttrs(attributes)}>`);
|
|
3979
|
+
}
|
|
3980
|
+
ctx.push("<span>");
|
|
3981
|
+
renderElements(ctx, elements);
|
|
3982
|
+
ctx.push("</span>");
|
|
3983
|
+
ctx.push(`</${tag}>`);
|
|
3984
|
+
}
|
|
3985
|
+
|
|
3986
|
+
// packages/render/src/elements/container/string-types.ts
|
|
3987
|
+
var WRAPPED_TAGS = {
|
|
3988
|
+
paragraph: "p",
|
|
3989
|
+
bold: "strong",
|
|
3990
|
+
italics: "em",
|
|
3991
|
+
superscript: "sup",
|
|
3992
|
+
subscript: "sub",
|
|
3993
|
+
monospace: "tt",
|
|
3994
|
+
span: "span",
|
|
3995
|
+
div: "div",
|
|
3996
|
+
blockquote: "blockquote",
|
|
3997
|
+
mark: "mark",
|
|
3998
|
+
insertion: "ins",
|
|
3999
|
+
deletion: "del",
|
|
4000
|
+
size: "span",
|
|
4001
|
+
ruby: "ruby",
|
|
4002
|
+
"ruby-text": "rt",
|
|
4003
|
+
"table-row": "tr",
|
|
4004
|
+
"table-cell": "td"
|
|
4005
|
+
};
|
|
4006
|
+
var STYLED_SPANS = {
|
|
4007
|
+
underline: "text-decoration: underline;",
|
|
4008
|
+
strikethrough: "text-decoration: line-through;",
|
|
4009
|
+
hidden: "display: none",
|
|
4010
|
+
invisible: "visibility: hidden"
|
|
4011
|
+
};
|
|
4012
|
+
var PLAIN_WRAPPED_TAGS = {
|
|
4013
|
+
"definition-list": "dl",
|
|
4014
|
+
"definition-list-key": "dt",
|
|
4015
|
+
"definition-list-value": "dd"
|
|
4016
|
+
};
|
|
4017
|
+
var CONTENTS_ONLY_TYPES = new Set(["heading", "collapsible", "definition-list-item"]);
|
|
4018
|
+
function getStringContainerRendering(type) {
|
|
4019
|
+
const wrappedTag = WRAPPED_TAGS[type];
|
|
4020
|
+
if (wrappedTag) {
|
|
4021
|
+
return { kind: "wrapped", tag: wrappedTag };
|
|
4022
|
+
}
|
|
4023
|
+
const style = STYLED_SPANS[type];
|
|
4024
|
+
if (style) {
|
|
4025
|
+
return { kind: "styled-span", style };
|
|
4026
|
+
}
|
|
4027
|
+
const plainTag = PLAIN_WRAPPED_TAGS[type];
|
|
4028
|
+
if (plainTag) {
|
|
4029
|
+
return { kind: "plain-wrapped", tag: plainTag };
|
|
4030
|
+
}
|
|
4031
|
+
return { kind: "contents" };
|
|
4032
|
+
}
|
|
4033
|
+
function isContentsOnlyStringContainer(type) {
|
|
4034
|
+
return CONTENTS_ONLY_TYPES.has(type);
|
|
4035
|
+
}
|
|
4036
|
+
|
|
4037
|
+
// packages/render/src/elements/container/wrappers.ts
|
|
4038
|
+
function renderWrapped(ctx, tag, attributes, elements) {
|
|
4039
|
+
ctx.push(`<${tag}${renderContainerAttrs(attributes)}>`);
|
|
4040
|
+
renderElements(ctx, elements);
|
|
4041
|
+
ctx.push(`</${tag}>`);
|
|
4042
|
+
}
|
|
4043
|
+
function renderPlainWrapped(ctx, tag, elements) {
|
|
4044
|
+
ctx.push(`<${tag}>`);
|
|
4045
|
+
renderElements(ctx, elements);
|
|
4046
|
+
ctx.push(`</${tag}>`);
|
|
4047
|
+
}
|
|
4048
|
+
function renderStyledSpan(ctx, style, attributes, elements) {
|
|
4049
|
+
ctx.push(`<span style="${style}"${renderContainerAttrs(attributes)}>`);
|
|
4050
|
+
renderElements(ctx, elements);
|
|
4051
|
+
ctx.push("</span>");
|
|
4052
|
+
}
|
|
4053
|
+
|
|
4054
|
+
// packages/render/src/elements/container/string-container.ts
|
|
4055
|
+
function renderStringContainer(ctx, type, attributes, elements) {
|
|
4056
|
+
if (type === "div" && elements.length === 0 && !hasAttributes(attributes)) {
|
|
4147
4057
|
return;
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4058
|
+
}
|
|
4059
|
+
if (isContentsOnlyStringContainer(type)) {
|
|
4060
|
+
renderElements(ctx, elements);
|
|
4061
|
+
return;
|
|
4062
|
+
}
|
|
4063
|
+
const rendering = getStringContainerRendering(type);
|
|
4064
|
+
switch (rendering.kind) {
|
|
4065
|
+
case "wrapped":
|
|
4066
|
+
renderWrapped(ctx, rendering.tag, attributes, elements);
|
|
4067
|
+
return;
|
|
4068
|
+
case "styled-span":
|
|
4069
|
+
renderStyledSpan(ctx, rendering.style, attributes, elements);
|
|
4070
|
+
return;
|
|
4071
|
+
case "plain-wrapped":
|
|
4072
|
+
renderPlainWrapped(ctx, rendering.tag, elements);
|
|
4073
|
+
return;
|
|
4074
|
+
case "contents":
|
|
4075
|
+
renderElements(ctx, elements);
|
|
4076
|
+
}
|
|
4077
|
+
}
|
|
4078
|
+
|
|
4079
|
+
// packages/render/src/elements/container/index.ts
|
|
4080
|
+
function renderContainer(ctx, data) {
|
|
4081
|
+
const { type, attributes, elements } = data;
|
|
4082
|
+
if (import_ast3.isHeaderType(type)) {
|
|
4083
|
+
renderHeader(ctx, type.header.level, type.header["has-toc"], attributes, elements);
|
|
4084
|
+
return;
|
|
4085
|
+
}
|
|
4086
|
+
if (import_ast3.isAlignType(type)) {
|
|
4087
|
+
ctx.push(`<div style="text-align: ${type.align};">`);
|
|
4157
4088
|
renderElements(ctx, elements);
|
|
4158
4089
|
ctx.push("</div>");
|
|
4090
|
+
return;
|
|
4159
4091
|
}
|
|
4092
|
+
if (import_ast3.isStringContainerType(type)) {
|
|
4093
|
+
renderStringContainer(ctx, type, attributes, elements);
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
|
|
4097
|
+
// packages/render/src/elements/color.ts
|
|
4098
|
+
function renderColor(ctx, data) {
|
|
4099
|
+
const safeColor = sanitizeCssColor(data.color, "inherit");
|
|
4100
|
+
ctx.push(`<span style="color: ${escapeAttr(safeColor)}">`);
|
|
4101
|
+
renderElements(ctx, data.elements);
|
|
4102
|
+
ctx.push("</span>");
|
|
4103
|
+
}
|
|
4104
|
+
|
|
4105
|
+
// packages/render/src/elements/date/format.ts
|
|
4106
|
+
function formatDate(date, format) {
|
|
4107
|
+
return format.replace(/%Y/g, String(date.getFullYear())).replace(/%m/g, String(date.getMonth() + 1).padStart(2, "0")).replace(/%d/g, String(date.getDate()).padStart(2, "0")).replace(/%H/g, String(date.getHours()).padStart(2, "0")).replace(/%M/g, String(date.getMinutes()).padStart(2, "0")).replace(/%S/g, String(date.getSeconds()).padStart(2, "0"));
|
|
4108
|
+
}
|
|
4109
|
+
|
|
4110
|
+
// packages/render/src/elements/date/output.ts
|
|
4111
|
+
function renderDateOutput(formatted, hover) {
|
|
4112
|
+
const escaped = escapeHtml(formatted);
|
|
4113
|
+
return hover ? `<span class="odate">${escaped}</span>` : escaped;
|
|
4114
|
+
}
|
|
4115
|
+
|
|
4116
|
+
// packages/render/src/elements/date/index.ts
|
|
4117
|
+
function renderDate(ctx, data) {
|
|
4118
|
+
const date = new Date(data.value.timestamp * 1000);
|
|
4119
|
+
const formatted = data.format ? formatDate(date, data.format) : date.toLocaleString();
|
|
4120
|
+
ctx.push(renderDateOutput(formatted, data.hover));
|
|
4121
|
+
}
|
|
4122
|
+
|
|
4123
|
+
// packages/render/src/elements/embed/iframe.ts
|
|
4124
|
+
function renderEmbedIframe(ctx, className, src) {
|
|
4125
|
+
ctx.push(`<div class="${className}">`);
|
|
4126
|
+
ctx.push(`<iframe src="${escapeAttr(src)}" frameborder="0" allowfullscreen></iframe>`);
|
|
4160
4127
|
ctx.push("</div>");
|
|
4161
4128
|
}
|
|
4162
4129
|
|
|
4163
|
-
// packages/render/src/elements/
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4130
|
+
// packages/render/src/elements/embed/validation.ts
|
|
4131
|
+
function isValidVideoId(id) {
|
|
4132
|
+
return /^[a-zA-Z0-9_-]+$/.test(id);
|
|
4133
|
+
}
|
|
4134
|
+
function isValidGithubUsername(username) {
|
|
4135
|
+
return /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,37}[a-zA-Z0-9])?$/.test(username);
|
|
4136
|
+
}
|
|
4137
|
+
function isValidGistHash(hash) {
|
|
4138
|
+
return /^[a-f0-9]+$/.test(hash);
|
|
4139
|
+
}
|
|
4140
|
+
function isValidGitlabSnippetId(id) {
|
|
4141
|
+
return /^[0-9]+$/.test(id);
|
|
4142
|
+
}
|
|
4143
|
+
|
|
4144
|
+
// packages/render/src/elements/embed/providers.ts
|
|
4145
|
+
function renderYoutube(ctx, videoId) {
|
|
4146
|
+
if (!isValidVideoId(videoId)) {
|
|
4147
|
+
ctx.push(`<!-- Invalid YouTube video ID -->`);
|
|
4148
|
+
return;
|
|
4149
|
+
}
|
|
4150
|
+
renderEmbedIframe(ctx, "embed-youtube", `https://www.youtube.com/embed/${videoId}`);
|
|
4151
|
+
}
|
|
4152
|
+
function renderVimeo(ctx, videoId) {
|
|
4153
|
+
if (!isValidVideoId(videoId)) {
|
|
4154
|
+
ctx.push(`<!-- Invalid Vimeo video ID -->`);
|
|
4155
|
+
return;
|
|
4156
|
+
}
|
|
4157
|
+
renderEmbedIframe(ctx, "embed-vimeo", `https://player.vimeo.com/video/${videoId}`);
|
|
4158
|
+
}
|
|
4159
|
+
function renderGithubGist(ctx, username, hash) {
|
|
4160
|
+
if (!isValidGithubUsername(username) || !isValidGistHash(hash)) {
|
|
4161
|
+
ctx.push(`<!-- Invalid GitHub Gist parameters -->`);
|
|
4162
|
+
return;
|
|
4163
|
+
}
|
|
4164
|
+
ctx.push(`<script src="https://gist.github.com/${escapeAttr(username)}/${escapeAttr(hash)}.js"></script>`);
|
|
4165
|
+
}
|
|
4166
|
+
function renderGitlabSnippet(ctx, snippetId) {
|
|
4167
|
+
if (!isValidGitlabSnippetId(snippetId)) {
|
|
4168
|
+
ctx.push(`<!-- Invalid GitLab snippet ID -->`);
|
|
4169
|
+
return;
|
|
4170
|
+
}
|
|
4171
|
+
ctx.push(`<script src="https://gitlab.com/snippets/${escapeAttr(snippetId)}.js"></script>`);
|
|
4172
|
+
}
|
|
4173
|
+
|
|
4174
|
+
// packages/render/src/elements/embed/index.ts
|
|
4175
|
+
function renderEmbed(ctx, data) {
|
|
4176
|
+
switch (data.embed) {
|
|
4177
|
+
case "youtube":
|
|
4178
|
+
renderYoutube(ctx, data.data["video-id"]);
|
|
4179
|
+
break;
|
|
4180
|
+
case "vimeo":
|
|
4181
|
+
renderVimeo(ctx, data.data["video-id"]);
|
|
4182
|
+
break;
|
|
4183
|
+
case "github-gist":
|
|
4184
|
+
renderGithubGist(ctx, data.data.username, data.data.hash);
|
|
4185
|
+
break;
|
|
4186
|
+
case "gitlab-snippet":
|
|
4187
|
+
renderGitlabSnippet(ctx, data.data["snippet-id"]);
|
|
4188
|
+
break;
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
|
|
4192
|
+
// packages/render/src/elements/embed-block/allowlist.ts
|
|
4193
|
+
var DEFAULT_EMBED_ALLOWLIST = [
|
|
4194
|
+
{ host: "*.youtube.com", pathPrefix: "/embed/" },
|
|
4195
|
+
{ host: "*.youtube-nocookie.com", pathPrefix: "/embed/" },
|
|
4196
|
+
{ host: "player.vimeo.com", pathPrefix: "/video/" },
|
|
4197
|
+
{ host: "*.google.com", pathPrefix: "/maps/embed" },
|
|
4198
|
+
{ host: "calendar.google.com", pathPrefix: "/calendar/embed" },
|
|
4199
|
+
{ host: "open.spotify.com", pathPrefix: "/embed/" },
|
|
4200
|
+
{ host: "w.soundcloud.com", pathPrefix: "/player/" },
|
|
4201
|
+
{ host: "codepen.io" }
|
|
4202
|
+
];
|
|
4203
|
+
function matchesAllowlistEntry(url, entry) {
|
|
4204
|
+
if (!matchesHostPattern(url.hostname, entry.host)) {
|
|
4167
4205
|
return false;
|
|
4168
4206
|
}
|
|
4169
|
-
|
|
4170
|
-
|
|
4207
|
+
if (entry.pathPrefix) {
|
|
4208
|
+
const pathLower = url.pathname.toLowerCase();
|
|
4209
|
+
const prefixLower = entry.pathPrefix.toLowerCase();
|
|
4210
|
+
if (!pathLower.startsWith(prefixLower)) {
|
|
4211
|
+
return false;
|
|
4212
|
+
}
|
|
4213
|
+
if (!prefixLower.endsWith("/")) {
|
|
4214
|
+
const remainder = pathLower.slice(prefixLower.length);
|
|
4215
|
+
if (remainder && !/^[/?#]/.test(remainder)) {
|
|
4216
|
+
return false;
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
}
|
|
4220
|
+
return true;
|
|
4221
|
+
}
|
|
4222
|
+
function matchesHostPattern(hostname, pattern) {
|
|
4223
|
+
const lowerHostname = hostname.toLowerCase();
|
|
4224
|
+
const lowerPattern = pattern.toLowerCase();
|
|
4225
|
+
if (lowerPattern.startsWith("*.")) {
|
|
4226
|
+
const base = lowerPattern.slice(2);
|
|
4227
|
+
return lowerHostname === base || lowerHostname.endsWith("." + base);
|
|
4228
|
+
}
|
|
4229
|
+
return lowerHostname === lowerPattern;
|
|
4230
|
+
}
|
|
4231
|
+
|
|
4232
|
+
// packages/render/src/elements/embed-block/sanitize.ts
|
|
4233
|
+
var import_sanitize_html = __toESM(require("sanitize-html"));
|
|
4234
|
+
|
|
4235
|
+
// packages/render/src/elements/embed-block/iframe.ts
|
|
4236
|
+
var import_htmlparser2 = require("htmlparser2");
|
|
4237
|
+
function findIframes(html) {
|
|
4238
|
+
const doc = import_htmlparser2.parseDocument(html);
|
|
4239
|
+
const iframes = [];
|
|
4240
|
+
function walk(nodes) {
|
|
4241
|
+
for (const node of nodes) {
|
|
4242
|
+
if (node.type !== "tag") {
|
|
4243
|
+
continue;
|
|
4244
|
+
}
|
|
4245
|
+
if (node.name === "iframe") {
|
|
4246
|
+
iframes.push(node);
|
|
4247
|
+
}
|
|
4248
|
+
if (node.children) {
|
|
4249
|
+
walk(node.children);
|
|
4250
|
+
}
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
walk(doc.children);
|
|
4254
|
+
return iframes;
|
|
4255
|
+
}
|
|
4256
|
+
function parseIframeUrl(src, baseUrl) {
|
|
4257
|
+
try {
|
|
4258
|
+
if (src.startsWith("//")) {
|
|
4259
|
+
return new URL(src, baseUrl ?? "https://localhost");
|
|
4260
|
+
}
|
|
4261
|
+
return new URL(src);
|
|
4262
|
+
} catch {
|
|
4263
|
+
return null;
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
|
|
4267
|
+
// packages/render/src/elements/embed-block/sanitize-config.ts
|
|
4268
|
+
var SANITIZE_CONFIG = {
|
|
4269
|
+
allowedTags: ["iframe"],
|
|
4270
|
+
allowedAttributes: {
|
|
4271
|
+
iframe: [
|
|
4272
|
+
"class",
|
|
4273
|
+
"src",
|
|
4274
|
+
"style",
|
|
4275
|
+
"allow",
|
|
4276
|
+
"allowfullscreen",
|
|
4277
|
+
"frameborder",
|
|
4278
|
+
"height",
|
|
4279
|
+
"loading",
|
|
4280
|
+
"referrerpolicy",
|
|
4281
|
+
"sandbox",
|
|
4282
|
+
"title",
|
|
4283
|
+
"width"
|
|
4284
|
+
]
|
|
4285
|
+
},
|
|
4286
|
+
allowedSchemes: ["https", "http"]
|
|
4287
|
+
};
|
|
4288
|
+
|
|
4289
|
+
// packages/render/src/elements/embed-block/boolean-attributes.ts
|
|
4290
|
+
var BOOLEAN_ATTRIBUTES = [
|
|
4291
|
+
"allowfullscreen",
|
|
4292
|
+
"async",
|
|
4293
|
+
"autofocus",
|
|
4294
|
+
"autoplay",
|
|
4295
|
+
"checked",
|
|
4296
|
+
"controls",
|
|
4297
|
+
"default",
|
|
4298
|
+
"defer",
|
|
4299
|
+
"disabled",
|
|
4300
|
+
"formnovalidate",
|
|
4301
|
+
"hidden",
|
|
4302
|
+
"ismap",
|
|
4303
|
+
"loop",
|
|
4304
|
+
"multiple",
|
|
4305
|
+
"muted",
|
|
4306
|
+
"novalidate",
|
|
4307
|
+
"open",
|
|
4308
|
+
"readonly",
|
|
4309
|
+
"required",
|
|
4310
|
+
"reversed",
|
|
4311
|
+
"selected"
|
|
4312
|
+
];
|
|
4313
|
+
function normalizeBooleanAttributes(html) {
|
|
4314
|
+
let result = html;
|
|
4315
|
+
for (const attr of BOOLEAN_ATTRIBUTES) {
|
|
4316
|
+
const standalonePattern = new RegExp(`\\s${attr}(?=\\s|>|/>)`, "gi");
|
|
4317
|
+
result = result.replace(standalonePattern, ` ${attr}="${attr}"`);
|
|
4318
|
+
const emptyValuePattern = new RegExp(`\\s${attr}=""`, "gi");
|
|
4319
|
+
result = result.replace(emptyValuePattern, ` ${attr}="${attr}"`);
|
|
4320
|
+
}
|
|
4321
|
+
return result;
|
|
4322
|
+
}
|
|
4323
|
+
|
|
4324
|
+
// packages/render/src/elements/embed-block/sanitize.ts
|
|
4325
|
+
function validateAndSanitizeEmbed(content, allowlist, baseUrl) {
|
|
4326
|
+
const sanitized = import_sanitize_html.default(content.trim(), SANITIZE_CONFIG);
|
|
4327
|
+
if (!sanitized.trim()) {
|
|
4328
|
+
return null;
|
|
4329
|
+
}
|
|
4330
|
+
const iframes = findIframes(sanitized);
|
|
4331
|
+
if (iframes.length !== 1) {
|
|
4332
|
+
return null;
|
|
4333
|
+
}
|
|
4334
|
+
const src = iframes[0].attribs.src?.trim();
|
|
4335
|
+
if (!src) {
|
|
4336
|
+
return null;
|
|
4337
|
+
}
|
|
4338
|
+
const url = parseIframeUrl(src, baseUrl);
|
|
4339
|
+
if (url === null) {
|
|
4340
|
+
return null;
|
|
4341
|
+
}
|
|
4342
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
4343
|
+
return null;
|
|
4344
|
+
}
|
|
4345
|
+
if (allowlist !== null && !allowlist.some((entry) => matchesAllowlistEntry(url, entry))) {
|
|
4346
|
+
return null;
|
|
4347
|
+
}
|
|
4348
|
+
return sanitized;
|
|
4349
|
+
}
|
|
4350
|
+
|
|
4351
|
+
// packages/render/src/elements/embed-block/index.ts
|
|
4352
|
+
function renderEmbedBlock(ctx, data) {
|
|
4353
|
+
const allowlist = ctx.options.embedAllowlist !== undefined ? ctx.options.embedAllowlist : DEFAULT_EMBED_ALLOWLIST;
|
|
4354
|
+
const sanitized = validateAndSanitizeEmbed(data.contents, allowlist, ctx.options.baseUrl);
|
|
4355
|
+
if (sanitized === null) {
|
|
4356
|
+
ctx.push('<div class="error-block">Sorry, no match for the embedded content.</div>');
|
|
4357
|
+
return;
|
|
4358
|
+
}
|
|
4359
|
+
ctx.push(normalizeBooleanAttributes(sanitized));
|
|
4360
|
+
}
|
|
4361
|
+
|
|
4362
|
+
// packages/render/src/elements/expr/index.ts
|
|
4363
|
+
var import_ast5 = require("@wdprlib/ast");
|
|
4364
|
+
|
|
4365
|
+
// packages/render/src/elements/expr/branch.ts
|
|
4366
|
+
function renderBranchElements(ctx, elements) {
|
|
4367
|
+
renderElements(ctx, elements.slice(0, findBranchRenderLength(elements)));
|
|
4368
|
+
}
|
|
4369
|
+
function findBranchRenderLength(elements) {
|
|
4370
|
+
let lastIdx = elements.length - 1;
|
|
4371
|
+
while (lastIdx >= 0 && isWhitespaceText(elements[lastIdx])) {
|
|
4372
|
+
lastIdx--;
|
|
4373
|
+
}
|
|
4374
|
+
return lastIdx + 1;
|
|
4375
|
+
}
|
|
4376
|
+
function isWhitespaceText(element) {
|
|
4377
|
+
return element.element === "text" && typeof element.data === "string" && element.data.trim() === "";
|
|
4378
|
+
}
|
|
4379
|
+
|
|
4380
|
+
// packages/render/src/elements/expr/result.ts
|
|
4381
|
+
var import_ast4 = require("@wdprlib/ast");
|
|
4382
|
+
function evaluateExpressionOutput(expression) {
|
|
4383
|
+
const result = import_ast4.evaluateExpression(expression);
|
|
4384
|
+
if (result.success) {
|
|
4385
|
+
return import_ast4.formatExprValue(result.value);
|
|
4386
|
+
}
|
|
4387
|
+
if (result.error === "empty expression") {
|
|
4388
|
+
return null;
|
|
4389
|
+
}
|
|
4390
|
+
return `run-time error: ${result.error}`;
|
|
4391
|
+
}
|
|
4392
|
+
function evaluateIfExpressionValue(expression) {
|
|
4393
|
+
const result = import_ast4.evaluateExpression(expression);
|
|
4394
|
+
return result.success ? result.value : `run-time error: ${result.error}`;
|
|
4395
|
+
}
|
|
4396
|
+
|
|
4397
|
+
// packages/render/src/elements/expr/index.ts
|
|
4398
|
+
function renderExpr(ctx, data) {
|
|
4399
|
+
const output = evaluateExpressionOutput(data.expression);
|
|
4400
|
+
if (output !== null) {
|
|
4401
|
+
ctx.pushEscaped(output);
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
function renderIf(ctx, data) {
|
|
4405
|
+
renderBranchElements(ctx, import_ast5.isTruthy(data.condition) ? data.then : data.else);
|
|
4406
|
+
}
|
|
4407
|
+
function renderIfExpr(ctx, data) {
|
|
4408
|
+
const value = evaluateIfExpressionValue(data.expression);
|
|
4409
|
+
if (typeof value === "string") {
|
|
4410
|
+
ctx.pushEscaped(value);
|
|
4411
|
+
return;
|
|
4412
|
+
}
|
|
4413
|
+
renderBranchElements(ctx, value !== 0 ? data.then : data.else);
|
|
4414
|
+
}
|
|
4415
|
+
|
|
4416
|
+
// packages/render/src/elements/footnote/body.ts
|
|
4417
|
+
function renderFootnoteBody(ctx, index, elements) {
|
|
4418
|
+
const fnId = ctx.generateId("footnote-", index);
|
|
4419
|
+
ctx.push(`<div class="footnote-footer" id="${fnId}">`);
|
|
4420
|
+
ctx.push(`<a href="javascript:;">${index}</a>. `);
|
|
4421
|
+
renderElements(ctx, elements);
|
|
4422
|
+
ctx.push("</div>");
|
|
4423
|
+
}
|
|
4424
|
+
|
|
4425
|
+
// packages/render/src/elements/footnote/ref.ts
|
|
4426
|
+
function renderFootnoteRef(ctx, index) {
|
|
4427
|
+
const id = ctx.generateId("footnoteref-", index);
|
|
4428
|
+
ctx.push(`<sup class="footnoteref">`);
|
|
4429
|
+
ctx.push(`<a id="${id}" href="javascript:;" class="footnoteref">${index}</a>`);
|
|
4430
|
+
ctx.push("</sup>");
|
|
4431
|
+
}
|
|
4432
|
+
|
|
4433
|
+
// packages/render/src/elements/footnote/index.ts
|
|
4434
|
+
function renderFootnoteBlock(ctx, data) {
|
|
4435
|
+
if (ctx.footnotes.length === 0)
|
|
4436
|
+
return;
|
|
4437
|
+
const title = data.title ?? "Footnotes";
|
|
4438
|
+
ctx.push(`<div class="footnotes-footer">`);
|
|
4439
|
+
ctx.push(`<div class="title">${escapeHtml(title)}</div>`);
|
|
4440
|
+
for (let i = 0;i < ctx.footnotes.length; i++) {
|
|
4441
|
+
renderFootnoteBody(ctx, i + 1, ctx.footnotes[i] ?? []);
|
|
4442
|
+
}
|
|
4443
|
+
ctx.push("</div>");
|
|
4444
|
+
}
|
|
4445
|
+
|
|
4446
|
+
// packages/render/src/elements/html/attributes.ts
|
|
4447
|
+
function getHtmlBlockAttributes(ctx, data) {
|
|
4448
|
+
return {
|
|
4449
|
+
sandbox: getSandboxAttribute(ctx),
|
|
4450
|
+
style: getStyleAttribute(data)
|
|
4451
|
+
};
|
|
4452
|
+
}
|
|
4453
|
+
function getSandboxAttribute(ctx) {
|
|
4454
|
+
const sandbox = ctx.options.htmlBlockSandbox;
|
|
4455
|
+
return sandbox ? ` sandbox="${escapeAttr(sandbox)}"` : "";
|
|
4456
|
+
}
|
|
4457
|
+
function getStyleAttribute(data) {
|
|
4458
|
+
return data.style ? ` style="${escapeAttr(sanitizeStyleValue(data.style))}"` : "";
|
|
4459
|
+
}
|
|
4460
|
+
|
|
4461
|
+
// packages/render/src/hash.ts
|
|
4462
|
+
function syncHashSha1(input) {
|
|
4463
|
+
return fnv1aHash(input, 40);
|
|
4464
|
+
}
|
|
4465
|
+
function syncHashMd5(input) {
|
|
4466
|
+
return fnv1aHash(input, 32);
|
|
4467
|
+
}
|
|
4468
|
+
function fnv1aHash(input, hexLen) {
|
|
4469
|
+
let result = "";
|
|
4470
|
+
const rounds = Math.ceil(hexLen / 8);
|
|
4471
|
+
for (let round = 0;round < rounds; round++) {
|
|
4472
|
+
let h = 2166136261 ^ round;
|
|
4473
|
+
for (let i = 0;i < input.length; i++) {
|
|
4474
|
+
h ^= input.charCodeAt(i);
|
|
4475
|
+
h = Math.imul(h, 16777619);
|
|
4476
|
+
}
|
|
4477
|
+
result += (h >>> 0).toString(16).padStart(8, "0");
|
|
4478
|
+
}
|
|
4479
|
+
return result.substring(0, hexLen);
|
|
4480
|
+
}
|
|
4481
|
+
|
|
4482
|
+
// packages/render/src/elements/html/url.ts
|
|
4483
|
+
function resolveHtmlBlockUrl(ctx, contents, index) {
|
|
4484
|
+
const callbackUrl = ctx.options.resolvers?.htmlBlockUrl?.(index);
|
|
4485
|
+
return callbackUrl || generateDefaultHtmlBlockUrl(ctx.page?.pageName ?? "", contents);
|
|
4486
|
+
}
|
|
4487
|
+
function generateDefaultHtmlBlockUrl(pageName, contents) {
|
|
4488
|
+
const hash = syncHashSha1(contents);
|
|
4489
|
+
const nonce = BigInt(contents.length) * 1000000000n + BigInt(hash.charCodeAt(0)) * 100000000n;
|
|
4490
|
+
return pageName ? `/${pageName}/html/${hash}-${nonce}` : `/html/${hash}-${nonce}`;
|
|
4491
|
+
}
|
|
4492
|
+
|
|
4493
|
+
// packages/render/src/elements/html/index.ts
|
|
4494
|
+
function renderHtmlBlock(ctx, data) {
|
|
4495
|
+
if (ctx.settings.allowHtmlBlocks === false) {
|
|
4496
|
+
return;
|
|
4497
|
+
}
|
|
4498
|
+
const index = ctx.nextHtmlBlockIndex();
|
|
4499
|
+
const src = resolveHtmlBlockUrl(ctx, data.contents, index);
|
|
4500
|
+
const attrs = getHtmlBlockAttributes(ctx, data);
|
|
4501
|
+
ctx.push(`<p><iframe src="${escapeAttr(src)}"${attrs.sandbox} allowtransparency="true" frameborder="0" class="html-block-iframe"${attrs.style}></iframe></p>`);
|
|
4502
|
+
}
|
|
4503
|
+
|
|
4504
|
+
// packages/render/src/elements/iframe/attributes.ts
|
|
4505
|
+
var IFRAME_ATTRIBUTES = [
|
|
4506
|
+
"align",
|
|
4507
|
+
"frameborder",
|
|
4508
|
+
"height",
|
|
4509
|
+
"scrolling",
|
|
4510
|
+
"width",
|
|
4511
|
+
"class",
|
|
4512
|
+
"style"
|
|
4513
|
+
];
|
|
4514
|
+
function getIframeAttributes(data) {
|
|
4515
|
+
const url = isDangerousUrl(data.url) ? "#invalid-url" : data.url;
|
|
4516
|
+
const attrs = [`src="${escapeAttr(url)}"`];
|
|
4517
|
+
for (const attr of IFRAME_ATTRIBUTES) {
|
|
4518
|
+
attrs.push(`${attr}="${escapeAttr(getIframeAttributeValue(data, attr))}"`);
|
|
4519
|
+
}
|
|
4520
|
+
return attrs;
|
|
4521
|
+
}
|
|
4522
|
+
function getIframeAttributeValue(data, attr) {
|
|
4523
|
+
const value = data.attributes[attr] ?? "";
|
|
4524
|
+
return attr === "style" ? sanitizeStyleValue(value) : value;
|
|
4525
|
+
}
|
|
4526
|
+
|
|
4527
|
+
// packages/render/src/elements/iframe/index.ts
|
|
4528
|
+
function renderIframe(ctx, data) {
|
|
4529
|
+
ctx.push(`<p><iframe ${getIframeAttributes(data).join(" ")}></iframe></p>`);
|
|
4530
|
+
}
|
|
4531
|
+
|
|
4532
|
+
// packages/render/src/elements/iftags/tokens.ts
|
|
4533
|
+
function parseIfTagsConditionTokens(condition) {
|
|
4534
|
+
const tokens = {
|
|
4535
|
+
required: [],
|
|
4536
|
+
excluded: [],
|
|
4537
|
+
optional: []
|
|
4538
|
+
};
|
|
4539
|
+
for (const token of condition.split(/\s+/).filter(Boolean)) {
|
|
4540
|
+
if (token.startsWith("+")) {
|
|
4541
|
+
addTag(tokens.required, token.slice(1));
|
|
4542
|
+
} else if (token.startsWith("-")) {
|
|
4543
|
+
addTag(tokens.excluded, token.slice(1));
|
|
4544
|
+
} else {
|
|
4545
|
+
addTag(tokens.optional, token);
|
|
4546
|
+
}
|
|
4547
|
+
}
|
|
4548
|
+
return tokens;
|
|
4549
|
+
}
|
|
4550
|
+
function hasAnyIfTagsConditionToken(tokens) {
|
|
4551
|
+
return tokens.required.length > 0 || tokens.excluded.length > 0 || tokens.optional.length > 0;
|
|
4552
|
+
}
|
|
4553
|
+
function addTag(tags, rawTag) {
|
|
4554
|
+
const tag = rawTag.toLowerCase();
|
|
4555
|
+
if (tag) {
|
|
4556
|
+
tags.push(tag);
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
|
|
4560
|
+
// packages/render/src/elements/iftags/condition.ts
|
|
4561
|
+
function evaluateIfTagsCondition(condition, pageTags) {
|
|
4562
|
+
const pageTagSet = new Set(pageTags.map((tag) => tag.toLowerCase()));
|
|
4563
|
+
const tokens = parseIfTagsConditionTokens(condition);
|
|
4564
|
+
if (!hasAnyIfTagsConditionToken(tokens)) {
|
|
4565
|
+
return false;
|
|
4566
|
+
}
|
|
4567
|
+
return hasRequiredTags(tokens.required, pageTagSet) && hasNoExcludedTags(tokens.excluded, pageTagSet) && hasOptionalTagIfNeeded(tokens.optional, pageTagSet);
|
|
4568
|
+
}
|
|
4569
|
+
function hasRequiredTags(required, pageTags) {
|
|
4570
|
+
return required.every((tag) => pageTags.has(tag));
|
|
4571
|
+
}
|
|
4572
|
+
function hasNoExcludedTags(excluded, pageTags) {
|
|
4573
|
+
return excluded.every((tag) => !pageTags.has(tag));
|
|
4574
|
+
}
|
|
4575
|
+
function hasOptionalTagIfNeeded(optional, pageTags) {
|
|
4576
|
+
return optional.length === 0 || optional.some((tag) => pageTags.has(tag));
|
|
4577
|
+
}
|
|
4578
|
+
|
|
4579
|
+
// packages/render/src/elements/iftags/style-slot.ts
|
|
4580
|
+
function withIfTagsStyleSlot(ctx, data, render) {
|
|
4581
|
+
const slotId = data._styleSlot;
|
|
4582
|
+
if (slotId !== undefined) {
|
|
4583
|
+
ctx.enterStyleSlot(slotId);
|
|
4584
|
+
}
|
|
4585
|
+
render();
|
|
4586
|
+
if (slotId !== undefined) {
|
|
4587
|
+
ctx.exitStyleSlot();
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
|
|
4591
|
+
// packages/render/src/elements/iftags/index.ts
|
|
4592
|
+
function renderIfTags(ctx, data) {
|
|
4593
|
+
const pageTags = ctx.page?.tags ?? [];
|
|
4594
|
+
if (!evaluateIfTagsCondition(data.condition, pageTags)) {
|
|
4595
|
+
return;
|
|
4596
|
+
}
|
|
4597
|
+
const prev = ctx.renderInlineStyles;
|
|
4598
|
+
ctx.renderInlineStyles = true;
|
|
4599
|
+
withIfTagsStyleSlot(ctx, data, () => {
|
|
4600
|
+
renderElements(ctx, data.elements);
|
|
4601
|
+
});
|
|
4602
|
+
ctx.renderInlineStyles = prev;
|
|
4603
|
+
}
|
|
4604
|
+
|
|
4605
|
+
// packages/render/src/elements/image/source.ts
|
|
4606
|
+
function getFilenameFromSource(source) {
|
|
4607
|
+
switch (source.type) {
|
|
4608
|
+
case "url": {
|
|
4609
|
+
const slashIndex = source.data.lastIndexOf("/");
|
|
4610
|
+
return slashIndex === -1 ? source.data : source.data.slice(slashIndex + 1);
|
|
4611
|
+
}
|
|
4612
|
+
case "file1":
|
|
4613
|
+
return source.data.file;
|
|
4614
|
+
case "file2":
|
|
4615
|
+
return source.data.file;
|
|
4616
|
+
case "file3":
|
|
4617
|
+
return source.data.file;
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4620
|
+
|
|
4621
|
+
// packages/render/src/elements/image/img-attributes.ts
|
|
4622
|
+
function getImageAttributes(src, source, attributes) {
|
|
4623
|
+
const safeAttrs = sanitizeAttributes(attributes);
|
|
4624
|
+
const imgAttrs = [`src="${escapeAttr(src)}"`];
|
|
4625
|
+
appendPassedImageAttributes(imgAttrs, safeAttrs);
|
|
4626
|
+
imgAttrs.push(`alt="${escapeAttr(safeAttrs.alt ?? getFilenameFromSource(source))}"`);
|
|
4627
|
+
imgAttrs.push(`class="${escapeAttr(safeAttrs.class ?? "image")}"`);
|
|
4628
|
+
return imgAttrs;
|
|
4629
|
+
}
|
|
4630
|
+
function appendPassedImageAttributes(imgAttrs, safeAttrs) {
|
|
4631
|
+
for (const key in safeAttrs) {
|
|
4632
|
+
if (key === "alt" || key === "class" || key === "src" || key === "srcset")
|
|
4633
|
+
continue;
|
|
4634
|
+
const value = safeAttrs[key];
|
|
4635
|
+
imgAttrs.push(`${key}="${escapeAttr(value)}"`);
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
|
|
4639
|
+
// packages/render/src/elements/image/attributes.ts
|
|
4640
|
+
function buildImageTag(src, source, attributes) {
|
|
4641
|
+
return `<img ${getImageAttributes(src, source, attributes).join(" ")} />`;
|
|
4642
|
+
}
|
|
4643
|
+
|
|
4644
|
+
// packages/render/src/elements/image/alignment.ts
|
|
4645
|
+
function pushAlignedImage(ctx, output, alignment) {
|
|
4646
|
+
if (!alignment) {
|
|
4647
|
+
ctx.push(output);
|
|
4648
|
+
return;
|
|
4649
|
+
}
|
|
4650
|
+
const alignClass = getAlignmentClass(alignment.align, alignment.float);
|
|
4651
|
+
ctx.push(`<div class="image-container ${alignClass}">`);
|
|
4652
|
+
ctx.push(output);
|
|
4653
|
+
ctx.push("</div>");
|
|
4654
|
+
}
|
|
4655
|
+
function getAlignmentClass(align, isFloat) {
|
|
4656
|
+
if (isFloat) {
|
|
4657
|
+
switch (align) {
|
|
4658
|
+
case "left":
|
|
4659
|
+
return "floatleft";
|
|
4660
|
+
case "right":
|
|
4661
|
+
return "floatright";
|
|
4662
|
+
case "center":
|
|
4663
|
+
return "floatcenter";
|
|
4664
|
+
default:
|
|
4665
|
+
return `float${align}`;
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4668
|
+
switch (align) {
|
|
4669
|
+
case "left":
|
|
4670
|
+
return "alignleft";
|
|
4671
|
+
case "right":
|
|
4672
|
+
return "alignright";
|
|
4673
|
+
case "center":
|
|
4674
|
+
return "aligncenter";
|
|
4675
|
+
default:
|
|
4676
|
+
return `align${align}`;
|
|
4677
|
+
}
|
|
4678
|
+
}
|
|
4679
|
+
|
|
4680
|
+
// packages/render/src/elements/image/link-href.ts
|
|
4681
|
+
function resolveSafeImageLinkHref(link) {
|
|
4682
|
+
const href = resolveImageLinkHref(link);
|
|
4683
|
+
return isDangerousUrl(href) ? "#invalid-url" : href;
|
|
4684
|
+
}
|
|
4685
|
+
function resolveImageLinkHref(link) {
|
|
4686
|
+
if (typeof link !== "string") {
|
|
4687
|
+
return `/${link.page}`;
|
|
4688
|
+
}
|
|
4689
|
+
if (!link.startsWith("/") && !link.startsWith("#") && !link.startsWith("http://") && !link.startsWith("https://")) {
|
|
4690
|
+
return `/${link}`;
|
|
4691
|
+
}
|
|
4692
|
+
return link;
|
|
4693
|
+
}
|
|
4694
|
+
|
|
4695
|
+
// packages/render/src/elements/image/link.ts
|
|
4696
|
+
function wrapImageLink(imageTag, link) {
|
|
4697
|
+
if (!link) {
|
|
4698
|
+
return imageTag;
|
|
4699
|
+
}
|
|
4700
|
+
const href = resolveSafeImageLinkHref(link);
|
|
4701
|
+
return `<a href="${escapeAttr(href)}">${imageTag}</a>`;
|
|
4702
|
+
}
|
|
4703
|
+
|
|
4704
|
+
// packages/render/src/elements/image/index.ts
|
|
4705
|
+
function renderImage(ctx, data) {
|
|
4706
|
+
let src = ctx.resolveImageSource(data.source);
|
|
4707
|
+
if (src === null)
|
|
4708
|
+
return;
|
|
4709
|
+
if (isDangerousUrl(src)) {
|
|
4710
|
+
src = "#invalid-url";
|
|
4711
|
+
}
|
|
4712
|
+
const imageTag = buildImageTag(src, data.source, data.attributes);
|
|
4713
|
+
const output = wrapImageLink(imageTag, data.link);
|
|
4714
|
+
pushAlignedImage(ctx, output, data.alignment);
|
|
4715
|
+
}
|
|
4716
|
+
|
|
4717
|
+
// packages/render/src/elements/include/missing.ts
|
|
4718
|
+
function renderMissingInclude(ctx, page) {
|
|
4719
|
+
const pageName = page.toLowerCase();
|
|
4720
|
+
const safePath = encodeIncludeEditPath(pageName);
|
|
4721
|
+
ctx.push(`<div class="error-block"><p>Included page "${escapeHtml(pageName)}" does not exist (<a href="/${escapeAttr(safePath)}/edit/true">create it now</a>)</p></div>`);
|
|
4722
|
+
}
|
|
4723
|
+
function encodeIncludeEditPath(pageName) {
|
|
4724
|
+
const encodedPageName = pageName.replace(/[^a-z0-9\-_:/]/g, (c) => encodeURIComponent(c));
|
|
4725
|
+
return encodedPageName.startsWith("/") ? encodedPageName.slice(1) : encodedPageName;
|
|
4726
|
+
}
|
|
4727
|
+
|
|
4728
|
+
// packages/render/src/elements/include/index.ts
|
|
4729
|
+
function renderInclude(ctx, data) {
|
|
4730
|
+
if (data.elements.length === 0) {
|
|
4731
|
+
renderMissingInclude(ctx, data.location.page);
|
|
4732
|
+
return;
|
|
4733
|
+
}
|
|
4734
|
+
renderElements(ctx, data.elements);
|
|
4735
|
+
}
|
|
4736
|
+
|
|
4737
|
+
// packages/render/src/elements/line-break.ts
|
|
4738
|
+
function renderLineBreaks(ctx, count) {
|
|
4739
|
+
for (let i = 0;i < count; i++) {
|
|
4740
|
+
ctx.push("<br />");
|
|
4741
|
+
}
|
|
4742
|
+
}
|
|
4743
|
+
|
|
4744
|
+
// packages/render/src/elements/link/target.ts
|
|
4745
|
+
var TARGET_VALUES = {
|
|
4746
|
+
"new-tab": "_blank",
|
|
4747
|
+
parent: "_parent",
|
|
4748
|
+
top: "_top",
|
|
4749
|
+
same: "_self"
|
|
4750
|
+
};
|
|
4751
|
+
function renderTargetAttributes(attrs, target) {
|
|
4752
|
+
if (!target) {
|
|
4753
|
+
return;
|
|
4754
|
+
}
|
|
4755
|
+
const targetValue = TARGET_VALUES[target] ?? "_blank";
|
|
4756
|
+
attrs.push(`target="${targetValue}"`);
|
|
4757
|
+
if (targetValue === "_blank") {
|
|
4758
|
+
attrs.push(`rel="noopener noreferrer"`);
|
|
4759
|
+
}
|
|
4760
|
+
}
|
|
4761
|
+
|
|
4762
|
+
// packages/render/src/elements/link/anchor.ts
|
|
4763
|
+
function renderAnchor(ctx, data) {
|
|
4764
|
+
const safe = sanitizeAttributes(data.attributes);
|
|
4765
|
+
const attrs = [];
|
|
4766
|
+
if (safe.href && isDangerousUrl(safe.href)) {
|
|
4767
|
+
safe.href = "#invalid-url";
|
|
4768
|
+
}
|
|
4769
|
+
const href = safe.href ?? "";
|
|
4770
|
+
attrs.push(`href="${escapeAttr(href)}"`);
|
|
4771
|
+
renderTargetAttributes(attrs, data.target);
|
|
4772
|
+
for (const [key, value] of Object.entries(safe)) {
|
|
4773
|
+
if (key === "href" || key === "target")
|
|
4774
|
+
continue;
|
|
4775
|
+
attrs.push(`${key}="${escapeAttr(value)}"`);
|
|
4776
|
+
}
|
|
4777
|
+
ctx.push(`<a ${attrs.join(" ")}>`);
|
|
4778
|
+
renderElements(ctx, data.elements);
|
|
4779
|
+
ctx.push("</a>");
|
|
4780
|
+
}
|
|
4781
|
+
|
|
4782
|
+
// packages/render/src/elements/link/anchor-name.ts
|
|
4783
|
+
function renderAnchorName(ctx, name) {
|
|
4784
|
+
ctx.push(`<a name="${escapeAttr(name)}"></a>`);
|
|
4785
|
+
}
|
|
4786
|
+
|
|
4787
|
+
// packages/render/src/elements/link/attributes.ts
|
|
4788
|
+
function getLinkAttributes(ctx, data) {
|
|
4789
|
+
const attrs = [`href="${escapeAttr(resolveSafeHref(ctx, data))}"`];
|
|
4790
|
+
if (shouldAddNewPageClass(ctx, data)) {
|
|
4791
|
+
attrs.push(`class="newpage"`);
|
|
4792
|
+
}
|
|
4793
|
+
renderTargetAttributes(attrs, data.target);
|
|
4794
|
+
return attrs;
|
|
4795
|
+
}
|
|
4796
|
+
function resolveSafeHref(ctx, data) {
|
|
4797
|
+
let href = ctx.resolvePageLink(data.link);
|
|
4798
|
+
if (data.extra) {
|
|
4799
|
+
href += data.extra;
|
|
4800
|
+
}
|
|
4801
|
+
const isAnchorJsVoid = data.type === "anchor" && href === "javascript:;";
|
|
4802
|
+
if (!isAnchorJsVoid && isDangerousUrl(href)) {
|
|
4803
|
+
return "#invalid-url";
|
|
4804
|
+
}
|
|
4805
|
+
return href;
|
|
4806
|
+
}
|
|
4807
|
+
function shouldAddNewPageClass(ctx, data) {
|
|
4808
|
+
if (data.type !== "page" || typeof data.link !== "object") {
|
|
4809
|
+
return false;
|
|
4810
|
+
}
|
|
4811
|
+
const page = data.link.page;
|
|
4812
|
+
const isSpecialPage = page.startsWith("//") || page.includes("#/");
|
|
4813
|
+
if (isSpecialPage) {
|
|
4814
|
+
return false;
|
|
4815
|
+
}
|
|
4816
|
+
const hashIdx = page.indexOf("#");
|
|
4817
|
+
const pageToCheck = hashIdx !== -1 ? page.slice(0, hashIdx) : page;
|
|
4818
|
+
const pageExists = ctx.page?.pageExists;
|
|
4819
|
+
return pageExists ? !pageExists(pageToCheck) : true;
|
|
4820
|
+
}
|
|
4821
|
+
|
|
4822
|
+
// packages/render/src/elements/link/label.ts
|
|
4823
|
+
function renderLinkLabel(ctx, data) {
|
|
4824
|
+
if (data.label === "page") {
|
|
4825
|
+
if (typeof data.link === "string") {
|
|
4826
|
+
ctx.pushEscaped(data.link);
|
|
4827
|
+
} else {
|
|
4828
|
+
ctx.pushEscaped(data.link.page);
|
|
4829
|
+
}
|
|
4830
|
+
return;
|
|
4831
|
+
}
|
|
4832
|
+
if ("text" in data.label) {
|
|
4833
|
+
ctx.pushEscaped(data.label.text);
|
|
4834
|
+
return;
|
|
4835
|
+
}
|
|
4836
|
+
if ("url" in data.label) {
|
|
4837
|
+
const href = ctx.resolvePageLink(data.link);
|
|
4838
|
+
ctx.pushEscaped(data.label.url ?? href);
|
|
4839
|
+
}
|
|
4840
|
+
}
|
|
4841
|
+
|
|
4842
|
+
// packages/render/src/elements/link/index.ts
|
|
4843
|
+
function renderLink(ctx, data) {
|
|
4844
|
+
const attrs = getLinkAttributes(ctx, data);
|
|
4845
|
+
ctx.push(`<a ${attrs.join(" ")}>`);
|
|
4846
|
+
renderLinkLabel(ctx, data);
|
|
4847
|
+
ctx.push("</a>");
|
|
4848
|
+
}
|
|
4849
|
+
|
|
4850
|
+
// packages/render/src/elements/list/definition-list.ts
|
|
4851
|
+
function renderDefinitionList(ctx, items) {
|
|
4852
|
+
ctx.push("<dl>");
|
|
4853
|
+
for (const item of items) {
|
|
4854
|
+
ctx.push("<dt>");
|
|
4855
|
+
renderElements(ctx, item.key);
|
|
4856
|
+
ctx.push("</dt>");
|
|
4857
|
+
ctx.push("<dd>");
|
|
4858
|
+
renderElements(ctx, item.value);
|
|
4859
|
+
ctx.push("</dd>");
|
|
4860
|
+
}
|
|
4861
|
+
ctx.push("</dl>");
|
|
4862
|
+
}
|
|
4863
|
+
|
|
4864
|
+
// packages/render/src/elements/list/attributes.ts
|
|
4865
|
+
function renderListAttrs(attributes) {
|
|
4866
|
+
let hasAttributes2 = false;
|
|
4867
|
+
for (const _ in attributes) {
|
|
4868
|
+
hasAttributes2 = true;
|
|
4869
|
+
break;
|
|
4870
|
+
}
|
|
4871
|
+
if (!hasAttributes2)
|
|
4872
|
+
return "";
|
|
4873
|
+
const safe = sanitizeAttributes(attributes);
|
|
4874
|
+
let result = "";
|
|
4875
|
+
for (const key in safe) {
|
|
4876
|
+
if (key.startsWith("_"))
|
|
4877
|
+
continue;
|
|
4878
|
+
const value = safe[key];
|
|
4879
|
+
result += ` ${key}="${escapeAttr(value)}"`;
|
|
4880
|
+
}
|
|
4881
|
+
return result;
|
|
4882
|
+
}
|
|
4883
|
+
|
|
4884
|
+
// packages/render/src/elements/list/item-rendering.ts
|
|
4885
|
+
function getListItemOpenTag(attributes) {
|
|
4886
|
+
const styleAttr = hasNoMarker(attributes) ? ' style="list-style: none"' : "";
|
|
4887
|
+
return `<li${renderListAttrs(attributes)}${styleAttr}>`;
|
|
4888
|
+
}
|
|
4889
|
+
function hasNoMarker(attributes) {
|
|
4890
|
+
return attributes._noMarker === "true";
|
|
4891
|
+
}
|
|
4892
|
+
function renderFollowingSubLists(items, index, renderNestedList) {
|
|
4893
|
+
let nextIndex = index;
|
|
4894
|
+
while (nextIndex + 1 < items.length) {
|
|
4895
|
+
const nextItem = getSubListItem(items[nextIndex + 1]);
|
|
4896
|
+
if (!nextItem) {
|
|
4897
|
+
break;
|
|
4898
|
+
}
|
|
4899
|
+
nextIndex++;
|
|
4900
|
+
renderNestedList(nextItem.data);
|
|
4901
|
+
}
|
|
4902
|
+
return nextIndex;
|
|
4903
|
+
}
|
|
4904
|
+
function getSubListItem(item) {
|
|
4905
|
+
return item?.["item-type"] === "sub-list" ? item : null;
|
|
4906
|
+
}
|
|
4907
|
+
|
|
4908
|
+
// packages/render/src/elements/list/paragraphs.ts
|
|
4909
|
+
function getParagraphIndices(elements) {
|
|
4910
|
+
const indices = [];
|
|
4911
|
+
for (let i = 0;i < elements.length; i++) {
|
|
4912
|
+
if (isParagraphElement(elements[i])) {
|
|
4913
|
+
indices.push(i);
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
return indices;
|
|
4917
|
+
}
|
|
4918
|
+
function isParagraphElement(element) {
|
|
4919
|
+
return element?.element === "container" && element.data.type === "paragraph";
|
|
4920
|
+
}
|
|
4921
|
+
function isLiCloseTextParagraph(element) {
|
|
4922
|
+
let combined = "";
|
|
4923
|
+
for (const child of element.data.elements) {
|
|
4924
|
+
if (child.element === "text") {
|
|
4925
|
+
combined += child.data;
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
return combined.trim() === "[[/li]]";
|
|
4929
|
+
}
|
|
4930
|
+
|
|
4931
|
+
// packages/render/src/elements/list/trim.ts
|
|
4932
|
+
function trimTextElements(elements) {
|
|
4933
|
+
if (elements.length === 0)
|
|
4934
|
+
return elements;
|
|
4935
|
+
let start = 0;
|
|
4936
|
+
let end = elements.length;
|
|
4937
|
+
while (start < end) {
|
|
4938
|
+
const el = elements[start];
|
|
4939
|
+
if (el.element === "text" && typeof el.data === "string" && el.data.trim() === "") {
|
|
4940
|
+
start++;
|
|
4941
|
+
} else {
|
|
4942
|
+
break;
|
|
4943
|
+
}
|
|
4944
|
+
}
|
|
4945
|
+
while (end > start) {
|
|
4946
|
+
const el = elements[end - 1];
|
|
4947
|
+
if (el.element === "text" && typeof el.data === "string" && el.data.trim() === "") {
|
|
4948
|
+
end--;
|
|
4949
|
+
} else {
|
|
4950
|
+
break;
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4953
|
+
if (start === 0 && end === elements.length)
|
|
4954
|
+
return elements;
|
|
4955
|
+
return elements.slice(start, end);
|
|
4956
|
+
}
|
|
4957
|
+
function hasNonWhitespaceElement(elements) {
|
|
4958
|
+
for (const el of elements) {
|
|
4959
|
+
if (el.element !== "text" || typeof el.data !== "string" || el.data.trim() !== "") {
|
|
4960
|
+
return true;
|
|
4961
|
+
}
|
|
4962
|
+
}
|
|
4963
|
+
return false;
|
|
4964
|
+
}
|
|
4965
|
+
|
|
4966
|
+
// packages/render/src/elements/list/no-marker.ts
|
|
4967
|
+
function renderNoMarkerElements(ctx, elements) {
|
|
4968
|
+
const trimmed = trimTextElements(elements);
|
|
4969
|
+
if (trimmed.length === 0)
|
|
4970
|
+
return;
|
|
4971
|
+
const paragraphIndices = getParagraphIndices(trimmed);
|
|
4972
|
+
if (paragraphIndices.length === 0) {
|
|
4973
|
+
renderElements(ctx, trimmed);
|
|
4974
|
+
return;
|
|
4975
|
+
}
|
|
4976
|
+
const firstParagraphIdx = paragraphIndices[0];
|
|
4977
|
+
const lastParagraphIdx = paragraphIndices[paragraphIndices.length - 1];
|
|
4978
|
+
for (let i = 0;i < trimmed.length; i++) {
|
|
4979
|
+
const el = trimmed[i];
|
|
4980
|
+
if (isParagraphElement(el)) {
|
|
4981
|
+
renderNoMarkerParagraph(ctx, el, i, firstParagraphIdx, lastParagraphIdx);
|
|
4982
|
+
} else {
|
|
4983
|
+
renderElement(ctx, el);
|
|
4984
|
+
}
|
|
4985
|
+
}
|
|
4986
|
+
}
|
|
4987
|
+
function renderNoMarkerParagraph(ctx, element, index, firstParagraphIdx, lastParagraphIdx) {
|
|
4988
|
+
if (index === firstParagraphIdx || index === lastParagraphIdx && isLiCloseTextParagraph(element)) {
|
|
4989
|
+
renderElements(ctx, element.data.elements);
|
|
4990
|
+
return;
|
|
4991
|
+
}
|
|
4992
|
+
ctx.push("<p>");
|
|
4993
|
+
renderElements(ctx, element.data.elements);
|
|
4994
|
+
ctx.push("</p>");
|
|
4995
|
+
}
|
|
4996
|
+
|
|
4997
|
+
// packages/render/src/elements/list/items.ts
|
|
4998
|
+
function renderListItems(ctx, items, renderNestedList) {
|
|
4999
|
+
let i = 0;
|
|
5000
|
+
while (i < items.length) {
|
|
5001
|
+
const item = items[i];
|
|
5002
|
+
if (item["item-type"] === "elements") {
|
|
5003
|
+
i = renderElementsListItem(ctx, item, items, i, renderNestedList);
|
|
5004
|
+
} else {
|
|
5005
|
+
renderOrphanSubListItem(ctx, item, renderNestedList);
|
|
5006
|
+
}
|
|
5007
|
+
i++;
|
|
5008
|
+
}
|
|
5009
|
+
}
|
|
5010
|
+
function renderElementsListItem(ctx, item, items, index, renderNestedList) {
|
|
5011
|
+
const noMarker = hasNoMarker(item.attributes);
|
|
5012
|
+
ctx.push(getListItemOpenTag(item.attributes));
|
|
5013
|
+
if (noMarker) {
|
|
5014
|
+
renderNoMarkerElements(ctx, item.elements);
|
|
5015
|
+
} else {
|
|
5016
|
+
renderElements(ctx, trimTextElements(item.elements));
|
|
5017
|
+
}
|
|
5018
|
+
const nextIndex = renderFollowingSubLists(items, index, (data) => renderNestedList(ctx, data));
|
|
5019
|
+
ctx.push("</li>");
|
|
5020
|
+
return nextIndex;
|
|
5021
|
+
}
|
|
5022
|
+
function renderOrphanSubListItem(ctx, item, renderNestedList) {
|
|
5023
|
+
ctx.push(`<li style="list-style: none; display: inline">`);
|
|
5024
|
+
renderNestedList(ctx, item.data);
|
|
5025
|
+
ctx.push("</li>");
|
|
5026
|
+
}
|
|
5027
|
+
|
|
5028
|
+
// packages/render/src/elements/list/index.ts
|
|
5029
|
+
function renderList(ctx, data) {
|
|
5030
|
+
const hasContent = data.items.some((item) => {
|
|
5031
|
+
if (item["item-type"] === "sub-list")
|
|
5032
|
+
return true;
|
|
5033
|
+
if (item["item-type"] === "elements") {
|
|
5034
|
+
return hasNonWhitespaceElement(item.elements);
|
|
5035
|
+
}
|
|
5036
|
+
return false;
|
|
5037
|
+
});
|
|
5038
|
+
if (!hasContent) {
|
|
5039
|
+
return;
|
|
5040
|
+
}
|
|
5041
|
+
const tag = data.type === "numbered" ? "ol" : "ul";
|
|
5042
|
+
ctx.push(`<${tag}${renderListAttrs(data.attributes)}>`);
|
|
5043
|
+
renderListItems(ctx, data.items, renderList);
|
|
5044
|
+
ctx.push(`</${tag}>`);
|
|
4171
5045
|
}
|
|
5046
|
+
|
|
5047
|
+
// packages/render/src/elements/math/latex.ts
|
|
5048
|
+
var import_temml = __toESM(require("temml"));
|
|
4172
5049
|
function renderLatexToMathML(latex, displayMode) {
|
|
4173
5050
|
try {
|
|
4174
5051
|
let processedLatex = latex;
|
|
@@ -4186,6 +5063,31 @@ ${latex}
|
|
|
4186
5063
|
return "";
|
|
4187
5064
|
}
|
|
4188
5065
|
}
|
|
5066
|
+
function needsAlignedWrapper(latex) {
|
|
5067
|
+
if (/\\begin\s*\{/.test(latex)) {
|
|
5068
|
+
return false;
|
|
5069
|
+
}
|
|
5070
|
+
const withoutEscaped = latex.replace(/\\&/g, "");
|
|
5071
|
+
return withoutEscaped.includes("&");
|
|
5072
|
+
}
|
|
5073
|
+
|
|
5074
|
+
// packages/render/src/elements/math/source.ts
|
|
5075
|
+
function pushHiddenLatexSource(ctx, latex) {
|
|
5076
|
+
ctx.push(`<code class="math-source" hidden aria-hidden="true">`);
|
|
5077
|
+
ctx.push(escapeHtml(latex));
|
|
5078
|
+
ctx.push(`</code>`);
|
|
5079
|
+
}
|
|
5080
|
+
function pushMathRender(ctx, mathml, fallback) {
|
|
5081
|
+
ctx.push(`<span class="math-render">`);
|
|
5082
|
+
if (mathml) {
|
|
5083
|
+
ctx.push(mathml);
|
|
5084
|
+
} else {
|
|
5085
|
+
fallback();
|
|
5086
|
+
}
|
|
5087
|
+
ctx.push(`</span>`);
|
|
5088
|
+
}
|
|
5089
|
+
|
|
5090
|
+
// packages/render/src/elements/math/block.ts
|
|
4189
5091
|
function renderMath(ctx, data) {
|
|
4190
5092
|
const index = ctx.nextEquationIndex() + 1;
|
|
4191
5093
|
const latex = data["latex-source"];
|
|
@@ -4196,38 +5098,28 @@ function renderMath(ctx, data) {
|
|
|
4196
5098
|
if (data.name) {
|
|
4197
5099
|
ctx.push(`<span class="equation-number">(${index})</span>`);
|
|
4198
5100
|
}
|
|
4199
|
-
ctx
|
|
4200
|
-
ctx
|
|
4201
|
-
ctx.push(`</code>`);
|
|
4202
|
-
ctx.push(`<span class="math-render">`);
|
|
4203
|
-
if (mathml) {
|
|
4204
|
-
ctx.push(mathml);
|
|
4205
|
-
} else {
|
|
5101
|
+
pushHiddenLatexSource(ctx, latex);
|
|
5102
|
+
pushMathRender(ctx, mathml, () => {
|
|
4206
5103
|
ctx.push(`<span class="math-error">`);
|
|
4207
5104
|
ctx.push(escapeHtml(latex));
|
|
4208
5105
|
ctx.push(`</span>`);
|
|
4209
|
-
}
|
|
4210
|
-
ctx.push(`</span>`);
|
|
5106
|
+
});
|
|
4211
5107
|
ctx.push("</div>");
|
|
4212
5108
|
}
|
|
5109
|
+
// packages/render/src/elements/math/inline.ts
|
|
4213
5110
|
function renderMathInline(ctx, data) {
|
|
4214
5111
|
const latex = data["latex-source"];
|
|
4215
5112
|
const mathml = renderLatexToMathML(latex, false);
|
|
4216
5113
|
ctx.push(`<span class="math-inline">`);
|
|
4217
|
-
ctx
|
|
4218
|
-
ctx
|
|
4219
|
-
ctx.push(`</code>`);
|
|
4220
|
-
ctx.push(`<span class="math-render">`);
|
|
4221
|
-
if (mathml) {
|
|
4222
|
-
ctx.push(mathml);
|
|
4223
|
-
} else {
|
|
5114
|
+
pushHiddenLatexSource(ctx, latex);
|
|
5115
|
+
pushMathRender(ctx, mathml, () => {
|
|
4224
5116
|
ctx.push(`<span class="math-error">$`);
|
|
4225
5117
|
ctx.push(escapeHtml(latex));
|
|
4226
5118
|
ctx.push(`$</span>`);
|
|
4227
|
-
}
|
|
4228
|
-
ctx.push(`</span>`);
|
|
5119
|
+
});
|
|
4229
5120
|
ctx.push("</span>");
|
|
4230
5121
|
}
|
|
5122
|
+
// packages/render/src/elements/math/equation-ref.ts
|
|
4231
5123
|
function renderEquationRef(ctx, name) {
|
|
4232
5124
|
const id = ctx.generateId("equation-", name);
|
|
4233
5125
|
ctx.push(`<span class="eref" data-target="${escapeAttr(id)}">`);
|
|
@@ -4237,62 +5129,82 @@ function renderEquationRef(ctx, name) {
|
|
|
4237
5129
|
ctx.push(`<span class="eref-tooltip" aria-hidden="true"></span>`);
|
|
4238
5130
|
ctx.push("</span>");
|
|
4239
5131
|
}
|
|
5132
|
+
// packages/render/src/elements/module/empty-container.ts
|
|
5133
|
+
function renderEmptyModuleContainer(ctx, className) {
|
|
5134
|
+
ctx.push(`<div class="${className}">`);
|
|
5135
|
+
ctx.push("</div>");
|
|
5136
|
+
}
|
|
5137
|
+
function renderIndentedEmptyModuleContainer(ctx, className) {
|
|
5138
|
+
ctx.push(`<div class="${className}">
|
|
5139
|
+
</div>`);
|
|
5140
|
+
}
|
|
4240
5141
|
|
|
4241
5142
|
// packages/render/src/elements/module/backlinks.ts
|
|
4242
5143
|
function renderBacklinks(ctx, _data) {
|
|
4243
|
-
ctx
|
|
4244
|
-
</div>`);
|
|
5144
|
+
renderIndentedEmptyModuleContainer(ctx, "backlinks-module-box");
|
|
4245
5145
|
}
|
|
4246
5146
|
|
|
4247
5147
|
// packages/render/src/elements/module/categories.ts
|
|
4248
5148
|
function renderCategories(ctx, _data) {
|
|
4249
|
-
ctx
|
|
4250
|
-
|
|
5149
|
+
renderEmptyModuleContainer(ctx, "categories-module-box");
|
|
5150
|
+
}
|
|
5151
|
+
|
|
5152
|
+
// packages/render/src/elements/module/join-markup.ts
|
|
5153
|
+
function renderJoinMarkup(data) {
|
|
5154
|
+
const buttonText = data["button-text"] ?? "Join";
|
|
5155
|
+
const className = data.attributes?.class ?? "join-box";
|
|
5156
|
+
return `<div class="${escapeAttr(className)}"><a href="javascript:;">${escapeHtml(buttonText)}</a></div>`;
|
|
4251
5157
|
}
|
|
4252
5158
|
|
|
4253
5159
|
// packages/render/src/elements/module/join.ts
|
|
4254
5160
|
function renderJoin(ctx, data) {
|
|
4255
|
-
|
|
4256
|
-
const attrs = data.attributes ?? {};
|
|
4257
|
-
const className = attrs.class ?? "join-box";
|
|
4258
|
-
ctx.push(`<div class="${escapeAttr(className)}">`);
|
|
4259
|
-
ctx.push(`<a href="javascript:;">${escapeHtml(buttonText)}</a>`);
|
|
4260
|
-
ctx.push("</div>");
|
|
5161
|
+
ctx.push(renderJoinMarkup(data));
|
|
4261
5162
|
}
|
|
4262
5163
|
|
|
4263
5164
|
// packages/render/src/elements/module/page-tree.ts
|
|
4264
5165
|
function renderPageTree(ctx, _data) {
|
|
4265
|
-
ctx
|
|
4266
|
-
|
|
5166
|
+
renderEmptyModuleContainer(ctx, "page-tree-module-box");
|
|
5167
|
+
}
|
|
5168
|
+
|
|
5169
|
+
// packages/render/src/elements/module/rate-markup.ts
|
|
5170
|
+
function getRateWidgetParts() {
|
|
5171
|
+
return [
|
|
5172
|
+
`<div class="page-rate-widget-box">`,
|
|
5173
|
+
`<span class="rate-points">rating: <span class="number prw54353">0</span></span>`,
|
|
5174
|
+
`<span class="rateup btn btn-default"><a title="I like it" href="javascript:;">+</a></span>`,
|
|
5175
|
+
`<span class="ratedown btn btn-default"><a title="I don't like it" href="javascript:;">–</a></span>`,
|
|
5176
|
+
`<span class="cancel btn btn-default"><a title="Cancel my vote" href="javascript:;">x</a></span>`,
|
|
5177
|
+
"</div>"
|
|
5178
|
+
];
|
|
4267
5179
|
}
|
|
4268
5180
|
|
|
4269
5181
|
// packages/render/src/elements/module/rate.ts
|
|
4270
5182
|
function renderRate(ctx) {
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
ctx.push(`<span class="ratedown btn btn-default"><a title="I don't like it" href="javascript:;">–</a></span>`);
|
|
4275
|
-
ctx.push(`<span class="cancel btn btn-default"><a title="Cancel my vote" href="javascript:;">x</a></span>`);
|
|
4276
|
-
ctx.push("</div>");
|
|
5183
|
+
for (const part of getRateWidgetParts()) {
|
|
5184
|
+
ctx.push(part);
|
|
5185
|
+
}
|
|
4277
5186
|
}
|
|
4278
5187
|
|
|
4279
5188
|
// packages/render/src/elements/module/listusers.ts
|
|
4280
5189
|
function renderListUsers(ctx, _data) {
|
|
4281
|
-
ctx
|
|
4282
|
-
ctx.push("</div>");
|
|
5190
|
+
renderEmptyModuleContainer(ctx, "list-users-module-box");
|
|
4283
5191
|
}
|
|
4284
5192
|
|
|
4285
5193
|
// packages/render/src/elements/module/listpages.ts
|
|
4286
5194
|
function renderListPages(ctx, _data) {
|
|
4287
|
-
ctx
|
|
4288
|
-
|
|
5195
|
+
renderEmptyModuleContainer(ctx, "list-pages-box");
|
|
5196
|
+
}
|
|
5197
|
+
|
|
5198
|
+
// packages/render/src/elements/module/unknown.ts
|
|
5199
|
+
function renderUnknownModule(ctx, data) {
|
|
5200
|
+
ctx.push(`<div class="error-block">[[module <em>${data.name}</em>]] No such module, please <a href="https://www.wikidot.com/doc:modules" target="_blank" rel="noopener noreferrer">check available modules</a> and fix this page.</div>`);
|
|
4289
5201
|
}
|
|
4290
5202
|
|
|
4291
5203
|
// packages/render/src/elements/module/index.ts
|
|
4292
5204
|
function renderModule(ctx, data) {
|
|
4293
5205
|
switch (data.module) {
|
|
4294
5206
|
case "unknown":
|
|
4295
|
-
ctx
|
|
5207
|
+
renderUnknownModule(ctx, data);
|
|
4296
5208
|
break;
|
|
4297
5209
|
case "backlinks":
|
|
4298
5210
|
renderBacklinks(ctx, data);
|
|
@@ -4318,306 +5230,166 @@ function renderModule(ctx, data) {
|
|
|
4318
5230
|
}
|
|
4319
5231
|
}
|
|
4320
5232
|
|
|
4321
|
-
// packages/render/src/elements/
|
|
4322
|
-
function
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
return
|
|
4330
|
-
}
|
|
4331
|
-
function isValidGitlabSnippetId(id) {
|
|
4332
|
-
return /^[0-9]+$/.test(id);
|
|
4333
|
-
}
|
|
4334
|
-
function renderEmbed(ctx, data) {
|
|
4335
|
-
switch (data.embed) {
|
|
4336
|
-
case "youtube":
|
|
4337
|
-
renderYoutube(ctx, data.data["video-id"]);
|
|
4338
|
-
break;
|
|
4339
|
-
case "vimeo":
|
|
4340
|
-
renderVimeo(ctx, data.data["video-id"]);
|
|
4341
|
-
break;
|
|
4342
|
-
case "github-gist":
|
|
4343
|
-
renderGithubGist(ctx, data.data.username, data.data.hash);
|
|
4344
|
-
break;
|
|
4345
|
-
case "gitlab-snippet":
|
|
4346
|
-
renderGitlabSnippet(ctx, data.data["snippet-id"]);
|
|
4347
|
-
break;
|
|
4348
|
-
}
|
|
4349
|
-
}
|
|
4350
|
-
function renderYoutube(ctx, videoId) {
|
|
4351
|
-
if (!isValidVideoId(videoId)) {
|
|
4352
|
-
ctx.push(`<!-- Invalid YouTube video ID -->`);
|
|
4353
|
-
return;
|
|
4354
|
-
}
|
|
4355
|
-
ctx.push(`<div class="embed-youtube">`);
|
|
4356
|
-
ctx.push(`<iframe src="https://www.youtube.com/embed/${escapeAttr(videoId)}" ` + `frameborder="0" allowfullscreen></iframe>`);
|
|
4357
|
-
ctx.push("</div>");
|
|
4358
|
-
}
|
|
4359
|
-
function renderVimeo(ctx, videoId) {
|
|
4360
|
-
if (!isValidVideoId(videoId)) {
|
|
4361
|
-
ctx.push(`<!-- Invalid Vimeo video ID -->`);
|
|
4362
|
-
return;
|
|
4363
|
-
}
|
|
4364
|
-
ctx.push(`<div class="embed-vimeo">`);
|
|
4365
|
-
ctx.push(`<iframe src="https://player.vimeo.com/video/${escapeAttr(videoId)}" ` + `frameborder="0" allowfullscreen></iframe>`);
|
|
4366
|
-
ctx.push("</div>");
|
|
4367
|
-
}
|
|
4368
|
-
function renderGithubGist(ctx, username, hash) {
|
|
4369
|
-
if (!isValidGithubUsername(username) || !isValidGistHash(hash)) {
|
|
4370
|
-
ctx.push(`<!-- Invalid GitHub Gist parameters -->`);
|
|
4371
|
-
return;
|
|
4372
|
-
}
|
|
4373
|
-
ctx.push(`<script src="https://gist.github.com/${escapeAttr(username)}/${escapeAttr(hash)}.js"></script>`);
|
|
4374
|
-
}
|
|
4375
|
-
function renderGitlabSnippet(ctx, snippetId) {
|
|
4376
|
-
if (!isValidGitlabSnippetId(snippetId)) {
|
|
4377
|
-
ctx.push(`<!-- Invalid GitLab snippet ID -->`);
|
|
4378
|
-
return;
|
|
4379
|
-
}
|
|
4380
|
-
ctx.push(`<script src="https://gitlab.com/snippets/${escapeAttr(snippetId)}.js"></script>`);
|
|
4381
|
-
}
|
|
4382
|
-
|
|
4383
|
-
// packages/render/src/elements/embed-block.ts
|
|
4384
|
-
var import_htmlparser2 = require("htmlparser2");
|
|
4385
|
-
var import_sanitize_html = __toESM(require("sanitize-html"));
|
|
4386
|
-
var BOOLEAN_ATTRIBUTES = [
|
|
4387
|
-
"allowfullscreen",
|
|
4388
|
-
"async",
|
|
4389
|
-
"autofocus",
|
|
4390
|
-
"autoplay",
|
|
4391
|
-
"checked",
|
|
4392
|
-
"controls",
|
|
4393
|
-
"default",
|
|
4394
|
-
"defer",
|
|
4395
|
-
"disabled",
|
|
4396
|
-
"formnovalidate",
|
|
4397
|
-
"hidden",
|
|
4398
|
-
"ismap",
|
|
4399
|
-
"loop",
|
|
4400
|
-
"multiple",
|
|
4401
|
-
"muted",
|
|
4402
|
-
"novalidate",
|
|
4403
|
-
"open",
|
|
4404
|
-
"readonly",
|
|
4405
|
-
"required",
|
|
4406
|
-
"reversed",
|
|
4407
|
-
"selected"
|
|
4408
|
-
];
|
|
4409
|
-
var DEFAULT_EMBED_ALLOWLIST = [
|
|
4410
|
-
{ host: "*.youtube.com", pathPrefix: "/embed/" },
|
|
4411
|
-
{ host: "*.youtube-nocookie.com", pathPrefix: "/embed/" },
|
|
4412
|
-
{ host: "player.vimeo.com", pathPrefix: "/video/" },
|
|
4413
|
-
{ host: "*.google.com", pathPrefix: "/maps/embed" },
|
|
4414
|
-
{ host: "calendar.google.com", pathPrefix: "/calendar/embed" },
|
|
4415
|
-
{ host: "open.spotify.com", pathPrefix: "/embed/" },
|
|
4416
|
-
{ host: "w.soundcloud.com", pathPrefix: "/player/" },
|
|
4417
|
-
{ host: "codepen.io" }
|
|
4418
|
-
];
|
|
4419
|
-
var SANITIZE_CONFIG = {
|
|
4420
|
-
allowedTags: ["iframe"],
|
|
4421
|
-
allowedAttributes: {
|
|
4422
|
-
iframe: [
|
|
4423
|
-
"class",
|
|
4424
|
-
"src",
|
|
4425
|
-
"style",
|
|
4426
|
-
"allow",
|
|
4427
|
-
"allowfullscreen",
|
|
4428
|
-
"frameborder",
|
|
4429
|
-
"height",
|
|
4430
|
-
"loading",
|
|
4431
|
-
"referrerpolicy",
|
|
4432
|
-
"sandbox",
|
|
4433
|
-
"title",
|
|
4434
|
-
"width"
|
|
4435
|
-
]
|
|
4436
|
-
},
|
|
4437
|
-
allowedSchemes: ["https", "http"]
|
|
4438
|
-
};
|
|
4439
|
-
function findIframes(html) {
|
|
4440
|
-
const doc = import_htmlparser2.parseDocument(html);
|
|
4441
|
-
const iframes = [];
|
|
4442
|
-
function walk(nodes) {
|
|
4443
|
-
for (const node of nodes) {
|
|
4444
|
-
if (node.type === "tag") {
|
|
4445
|
-
if (node.name === "iframe") {
|
|
4446
|
-
iframes.push(node);
|
|
4447
|
-
}
|
|
4448
|
-
if (node.children) {
|
|
4449
|
-
walk(node.children);
|
|
4450
|
-
}
|
|
4451
|
-
}
|
|
4452
|
-
}
|
|
4453
|
-
}
|
|
4454
|
-
walk(doc.children);
|
|
4455
|
-
return iframes;
|
|
4456
|
-
}
|
|
4457
|
-
function matchesHostPattern(hostname, pattern) {
|
|
4458
|
-
const lowerHostname = hostname.toLowerCase();
|
|
4459
|
-
const lowerPattern = pattern.toLowerCase();
|
|
4460
|
-
if (lowerPattern.startsWith("*.")) {
|
|
4461
|
-
const base = lowerPattern.slice(2);
|
|
4462
|
-
return lowerHostname === base || lowerHostname.endsWith("." + base);
|
|
4463
|
-
}
|
|
4464
|
-
return lowerHostname === lowerPattern;
|
|
4465
|
-
}
|
|
4466
|
-
function matchesAllowlistEntry(url, entry) {
|
|
4467
|
-
if (!matchesHostPattern(url.hostname, entry.host)) {
|
|
4468
|
-
return false;
|
|
4469
|
-
}
|
|
4470
|
-
if (entry.pathPrefix) {
|
|
4471
|
-
const pathLower = url.pathname.toLowerCase();
|
|
4472
|
-
const prefixLower = entry.pathPrefix.toLowerCase();
|
|
4473
|
-
if (!pathLower.startsWith(prefixLower)) {
|
|
4474
|
-
return false;
|
|
4475
|
-
}
|
|
4476
|
-
if (!prefixLower.endsWith("/")) {
|
|
4477
|
-
const remainder = pathLower.slice(prefixLower.length);
|
|
4478
|
-
if (remainder && !/^[/?#]/.test(remainder)) {
|
|
4479
|
-
return false;
|
|
4480
|
-
}
|
|
4481
|
-
}
|
|
4482
|
-
}
|
|
4483
|
-
return true;
|
|
4484
|
-
}
|
|
4485
|
-
function validateAndSanitizeEmbed(content, allowlist, baseUrl) {
|
|
4486
|
-
const sanitized = import_sanitize_html.default(content.trim(), SANITIZE_CONFIG);
|
|
4487
|
-
if (!sanitized.trim()) {
|
|
4488
|
-
return null;
|
|
4489
|
-
}
|
|
4490
|
-
const iframes = findIframes(sanitized);
|
|
4491
|
-
if (iframes.length !== 1) {
|
|
4492
|
-
return null;
|
|
4493
|
-
}
|
|
4494
|
-
const iframe = iframes[0];
|
|
4495
|
-
const src = iframe.attribs.src?.trim();
|
|
4496
|
-
if (!src) {
|
|
4497
|
-
return null;
|
|
4498
|
-
}
|
|
4499
|
-
let url;
|
|
4500
|
-
try {
|
|
4501
|
-
if (src.startsWith("//")) {
|
|
4502
|
-
const base = baseUrl ?? "https://localhost";
|
|
4503
|
-
url = new URL(src, base);
|
|
4504
|
-
} else {
|
|
4505
|
-
url = new URL(src);
|
|
4506
|
-
}
|
|
4507
|
-
} catch {
|
|
4508
|
-
return null;
|
|
4509
|
-
}
|
|
4510
|
-
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
4511
|
-
return null;
|
|
4512
|
-
}
|
|
4513
|
-
if (allowlist !== null) {
|
|
4514
|
-
const matched = allowlist.some((entry) => matchesAllowlistEntry(url, entry));
|
|
4515
|
-
if (!matched) {
|
|
4516
|
-
return null;
|
|
4517
|
-
}
|
|
4518
|
-
}
|
|
4519
|
-
return sanitized;
|
|
4520
|
-
}
|
|
4521
|
-
function normalizeBooleanAttributes(html) {
|
|
4522
|
-
let result = html;
|
|
4523
|
-
for (const attr of BOOLEAN_ATTRIBUTES) {
|
|
4524
|
-
const standalonePattern = new RegExp(`\\s${attr}(?=\\s|>|/>)`, "gi");
|
|
4525
|
-
result = result.replace(standalonePattern, ` ${attr}="${attr}"`);
|
|
4526
|
-
const emptyValuePattern = new RegExp(`\\s${attr}=""`, "gi");
|
|
4527
|
-
result = result.replace(emptyValuePattern, ` ${attr}="${attr}"`);
|
|
4528
|
-
}
|
|
4529
|
-
return result;
|
|
5233
|
+
// packages/render/src/elements/table/cell-attributes.ts
|
|
5234
|
+
function renderTableCellAttrs(cell) {
|
|
5235
|
+
const attrs = [];
|
|
5236
|
+
const safeCellAttrs = sanitizeAttributes(cell.attributes);
|
|
5237
|
+
appendColumnSpan(attrs, cell);
|
|
5238
|
+
appendRowSpan(attrs, safeCellAttrs);
|
|
5239
|
+
appendAlignmentStyle(attrs, cell, safeCellAttrs);
|
|
5240
|
+
appendRemainingCellAttributes(attrs, cell, safeCellAttrs);
|
|
5241
|
+
return attrs.length > 0 ? " " + attrs.join(" ") : "";
|
|
4530
5242
|
}
|
|
4531
|
-
function
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
if (sanitized === null) {
|
|
4535
|
-
ctx.push('<div class="error-block">Sorry, no match for the embedded content.</div>');
|
|
4536
|
-
return;
|
|
5243
|
+
function appendColumnSpan(attrs, cell) {
|
|
5244
|
+
if (cell["column-span"] > 1) {
|
|
5245
|
+
attrs.push(`colspan="${cell["column-span"]}"`);
|
|
4537
5246
|
}
|
|
4538
|
-
const normalized = normalizeBooleanAttributes(sanitized);
|
|
4539
|
-
ctx.push(normalized);
|
|
4540
5247
|
}
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
function renderUser(ctx, data) {
|
|
4544
|
-
const normalized = data.name.toLowerCase().trim();
|
|
4545
|
-
if (normalized === "anonymous") {
|
|
4546
|
-
ctx.push("Anonymous");
|
|
5248
|
+
function appendRowSpan(attrs, safeCellAttrs) {
|
|
5249
|
+
if (!safeCellAttrs.rowspan) {
|
|
4547
5250
|
return;
|
|
4548
5251
|
}
|
|
4549
|
-
const
|
|
4550
|
-
if (
|
|
4551
|
-
|
|
5252
|
+
const rowspan = parseInt(safeCellAttrs.rowspan, 10);
|
|
5253
|
+
if (rowspan > 1) {
|
|
5254
|
+
attrs.push(`rowspan="${rowspan}"`);
|
|
5255
|
+
}
|
|
5256
|
+
}
|
|
5257
|
+
function appendAlignmentStyle(attrs, cell, safeCellAttrs) {
|
|
5258
|
+
if (!cell.align) {
|
|
4552
5259
|
return;
|
|
4553
5260
|
}
|
|
4554
|
-
const
|
|
4555
|
-
const
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
const styleAttr = resolved.karmaUrl ? ` style="background-image:url(${escapeAttr(resolved.karmaUrl)})"` : "";
|
|
4559
|
-
ctx.push(`<span class="printuser avatarhover">`);
|
|
4560
|
-
ctx.push(`<a${hrefAttr}>`);
|
|
4561
|
-
ctx.push(`<img class="small" src="${escapeAttr(resolved.avatarUrl)}" alt="${escapeAttr(displayName)}"${styleAttr} />`);
|
|
4562
|
-
ctx.push("</a>");
|
|
4563
|
-
ctx.push(`<a${hrefAttr}>`);
|
|
4564
|
-
ctx.push(escapeHtml(displayName));
|
|
4565
|
-
ctx.push("</a>");
|
|
4566
|
-
ctx.push("</span>");
|
|
5261
|
+
const existingStyle = safeCellAttrs.style ?? "";
|
|
5262
|
+
const alignStyle = `text-align: ${cell.align};`;
|
|
5263
|
+
if (existingStyle) {
|
|
5264
|
+
attrs.push(`style="${escapeAttr(existingStyle + "; " + alignStyle)}"`);
|
|
4567
5265
|
} else {
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
5266
|
+
attrs.push(`style="${alignStyle}"`);
|
|
5267
|
+
}
|
|
5268
|
+
}
|
|
5269
|
+
function appendRemainingCellAttributes(attrs, cell, safeCellAttrs) {
|
|
5270
|
+
for (const key in safeCellAttrs) {
|
|
5271
|
+
if (key === "style" && cell.align)
|
|
5272
|
+
continue;
|
|
5273
|
+
if (key === "rowspan")
|
|
5274
|
+
continue;
|
|
5275
|
+
const value = safeCellAttrs[key];
|
|
5276
|
+
attrs.push(`${key}="${escapeAttr(value)}"`);
|
|
4573
5277
|
}
|
|
4574
5278
|
}
|
|
4575
5279
|
|
|
4576
|
-
// packages/render/src/elements/
|
|
4577
|
-
function
|
|
4578
|
-
let
|
|
4579
|
-
const
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
5280
|
+
// packages/render/src/elements/table/attributes.ts
|
|
5281
|
+
function renderTableAttrs(attributes) {
|
|
5282
|
+
let hasRenderableAttributes = false;
|
|
5283
|
+
for (const key in attributes) {
|
|
5284
|
+
if (!key.startsWith("_")) {
|
|
5285
|
+
hasRenderableAttributes = true;
|
|
5286
|
+
break;
|
|
5287
|
+
}
|
|
4583
5288
|
}
|
|
4584
|
-
|
|
5289
|
+
if (!hasRenderableAttributes)
|
|
5290
|
+
return "";
|
|
5291
|
+
const safe = sanitizeAttributes(attributes);
|
|
5292
|
+
let result = "";
|
|
5293
|
+
for (const key in safe) {
|
|
5294
|
+
if (key.startsWith("_"))
|
|
5295
|
+
continue;
|
|
5296
|
+
const value = safe[key];
|
|
5297
|
+
result += ` ${key}="${escapeAttr(value)}"`;
|
|
5298
|
+
}
|
|
5299
|
+
return result;
|
|
4585
5300
|
}
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
5301
|
+
|
|
5302
|
+
// packages/render/src/elements/table/cell.ts
|
|
5303
|
+
function renderTableCell(ctx, cell) {
|
|
5304
|
+
const tag = cell.header ? "th" : "td";
|
|
5305
|
+
const attrStr = renderTableCellAttrs(cell);
|
|
5306
|
+
ctx.push(`<${tag}${attrStr}>`);
|
|
5307
|
+
renderElements(ctx, cell.elements);
|
|
5308
|
+
ctx.push(`</${tag}>`);
|
|
5309
|
+
}
|
|
5310
|
+
|
|
5311
|
+
// packages/render/src/elements/table/index.ts
|
|
5312
|
+
function renderTable(ctx, data) {
|
|
5313
|
+
const isPipeTable = data.attributes._source === "pipe";
|
|
5314
|
+
const classAttr = isPipeTable ? ' class="wiki-content-table"' : "";
|
|
5315
|
+
ctx.push(`<table${classAttr}${renderTableAttrs(data.attributes)}>`);
|
|
5316
|
+
for (const row of data.rows) {
|
|
5317
|
+
ctx.push(`<tr${renderTableAttrs(row.attributes)}>`);
|
|
5318
|
+
for (const cell of row.cells) {
|
|
5319
|
+
renderTableCell(ctx, cell);
|
|
5320
|
+
}
|
|
5321
|
+
ctx.push("</tr>");
|
|
4592
5322
|
}
|
|
4593
|
-
|
|
4594
|
-
const id = ctx.generateId(`bibcite-${number}-`, idSuffix);
|
|
4595
|
-
const bibitemId = ctx.generateId("bibitem-", number);
|
|
4596
|
-
const onclick = `WIKIDOT.page.utils.scrollToReference('${bibitemId}')`;
|
|
4597
|
-
ctx.push(`<a href="javascript:;" class="bibcite" id="${id}" onclick="${escapeAttr(onclick)}">`);
|
|
4598
|
-
ctx.push(String(number));
|
|
4599
|
-
ctx.push("</a>");
|
|
5323
|
+
ctx.push("</table>");
|
|
4600
5324
|
}
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
const
|
|
4605
|
-
ctx.
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
5325
|
+
|
|
5326
|
+
// packages/render/src/elements/tab-view/ids.ts
|
|
5327
|
+
function getTabViewWidgetId(ctx, tabs, tabViewIndex) {
|
|
5328
|
+
const labelString = tabs.map((tab) => tab.label).join("");
|
|
5329
|
+
return ctx.generateFixedId(`wiki-tabview-${tabViewIndex}-${syncHashMd5(labelString)}`);
|
|
5330
|
+
}
|
|
5331
|
+
function getTabPanelId(ctx, tabViewIndex, tabIndex) {
|
|
5332
|
+
return ctx.generateId(`wiki-tab-${tabViewIndex}-`, tabIndex);
|
|
5333
|
+
}
|
|
5334
|
+
|
|
5335
|
+
// packages/render/src/elements/tab-view/panels.ts
|
|
5336
|
+
function renderTabPanels(ctx, tabs, tabViewIndex) {
|
|
5337
|
+
ctx.push(`<div class="yui-content">`);
|
|
5338
|
+
for (let i = 0;i < tabs.length; i++) {
|
|
5339
|
+
const tab = tabs[i];
|
|
5340
|
+
const displayStyle = i === 0 ? "" : ` style="display:none"`;
|
|
5341
|
+
ctx.push(`<div id="${getTabPanelId(ctx, tabViewIndex, i)}"${displayStyle}>`);
|
|
5342
|
+
renderElements(ctx, tab.elements);
|
|
4613
5343
|
ctx.push("</div>");
|
|
4614
|
-
index++;
|
|
4615
5344
|
}
|
|
4616
5345
|
ctx.push("</div>");
|
|
4617
5346
|
}
|
|
4618
5347
|
|
|
4619
|
-
// packages/render/src/elements/
|
|
4620
|
-
function
|
|
5348
|
+
// packages/render/src/elements/tab-view/navigation.ts
|
|
5349
|
+
function renderTabNavigation(ctx, tabs) {
|
|
5350
|
+
ctx.push(`<ul class="yui-nav">`);
|
|
5351
|
+
for (let i = 0;i < tabs.length; i++) {
|
|
5352
|
+
const tab = tabs[i];
|
|
5353
|
+
const selectedClass = i === 0 ? ` class="selected"` : "";
|
|
5354
|
+
ctx.push(`<li${selectedClass}>`);
|
|
5355
|
+
ctx.push(`<a href="javascript:;"><em>${escapeHtml(tab.label)}</em></a>`);
|
|
5356
|
+
ctx.push("</li>");
|
|
5357
|
+
}
|
|
5358
|
+
ctx.push("</ul>");
|
|
5359
|
+
}
|
|
5360
|
+
|
|
5361
|
+
// packages/render/src/elements/tab-view/index.ts
|
|
5362
|
+
function renderTabView(ctx, tabs) {
|
|
5363
|
+
const tabViewIndex = ctx.nextTabViewIndex();
|
|
5364
|
+
const widgetId = getTabViewWidgetId(ctx, tabs, tabViewIndex);
|
|
5365
|
+
ctx.push(`<div id="${widgetId}" class="yui-navset">`);
|
|
5366
|
+
renderTabNavigation(ctx, tabs);
|
|
5367
|
+
renderTabPanels(ctx, tabs, tabViewIndex);
|
|
5368
|
+
ctx.push("</div>");
|
|
5369
|
+
}
|
|
5370
|
+
|
|
5371
|
+
// packages/render/src/elements/text/email.ts
|
|
5372
|
+
function renderEmail(ctx, email) {
|
|
5373
|
+
if (!isValidEmail(email)) {
|
|
5374
|
+
ctx.pushEscaped(email);
|
|
5375
|
+
return;
|
|
5376
|
+
}
|
|
5377
|
+
ctx.push(`<a href="mailto:${escapeAttr(email)}">${escapeHtml(email)}</a>`);
|
|
5378
|
+
}
|
|
5379
|
+
// packages/render/src/elements/text/raw.ts
|
|
5380
|
+
function renderRaw(ctx, data) {
|
|
5381
|
+
if (data === "")
|
|
5382
|
+
return;
|
|
5383
|
+
ctx.push(`<span style="white-space: pre-wrap;">`);
|
|
5384
|
+
ctx.push(escapeHtml(data).replace(/ /g, " "));
|
|
5385
|
+
ctx.push("</span>");
|
|
5386
|
+
}
|
|
5387
|
+
// packages/render/src/elements/text/plain.ts
|
|
5388
|
+
function renderText(ctx, data) {
|
|
5389
|
+
ctx.pushEscaped(data);
|
|
5390
|
+
}
|
|
5391
|
+
// packages/render/src/elements/toc/link.ts
|
|
5392
|
+
function extractTocLink(element) {
|
|
4621
5393
|
if (element.element !== "link")
|
|
4622
5394
|
return null;
|
|
4623
5395
|
const label = element.data.label;
|
|
@@ -4628,6 +5400,14 @@ function extractLinkText(element) {
|
|
|
4628
5400
|
const href = typeof element.data.link === "string" ? element.data.link : "";
|
|
4629
5401
|
return { href, text };
|
|
4630
5402
|
}
|
|
5403
|
+
function rewriteTocAnchor(ctx, href) {
|
|
5404
|
+
const match = /^#toc(\d+)$/.exec(href);
|
|
5405
|
+
if (!match)
|
|
5406
|
+
return href;
|
|
5407
|
+
return `#${ctx.generateId("toc", Number(match[1]))}`;
|
|
5408
|
+
}
|
|
5409
|
+
|
|
5410
|
+
// packages/render/src/elements/toc/entries.ts
|
|
4631
5411
|
function renderTocEntries(ctx, elements) {
|
|
4632
5412
|
for (const element of elements) {
|
|
4633
5413
|
if (element.element === "list") {
|
|
@@ -4640,16 +5420,10 @@ function renderTocList(ctx, listData, depth) {
|
|
|
4640
5420
|
renderTocItem(ctx, item, depth);
|
|
4641
5421
|
}
|
|
4642
5422
|
}
|
|
4643
|
-
function rewriteTocAnchor(ctx, href) {
|
|
4644
|
-
const match = /^#toc(\d+)$/.exec(href);
|
|
4645
|
-
if (!match)
|
|
4646
|
-
return href;
|
|
4647
|
-
return `#${ctx.generateId("toc", Number(match[1]))}`;
|
|
4648
|
-
}
|
|
4649
5423
|
function renderTocItem(ctx, item, depth) {
|
|
4650
5424
|
if (item["item-type"] === "elements") {
|
|
4651
5425
|
for (const el of item.elements) {
|
|
4652
|
-
const link =
|
|
5426
|
+
const link = extractTocLink(el);
|
|
4653
5427
|
if (link) {
|
|
4654
5428
|
const href = rewriteTocAnchor(ctx, link.href);
|
|
4655
5429
|
ctx.push(`<div style="margin-left: ${depth}em;"><a href="${escapeAttr(href)}">${escapeHtml(link.text)}</a></div>`);
|
|
@@ -4659,222 +5433,122 @@ function renderTocItem(ctx, item, depth) {
|
|
|
4659
5433
|
renderTocList(ctx, item.data, depth + 1);
|
|
4660
5434
|
}
|
|
4661
5435
|
}
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
5436
|
+
|
|
5437
|
+
// packages/render/src/elements/toc/body.ts
|
|
5438
|
+
function renderTocBody(ctx) {
|
|
5439
|
+
ctx.push(`<div id="toc-action-bar"><a href="javascript:;">Fold</a><a style="display: none" href="javascript:;">Unfold</a></div>`);
|
|
5440
|
+
ctx.push(`<div class="title">Table of Contents</div>`);
|
|
5441
|
+
ctx.push(`<div id="toc-list">`);
|
|
5442
|
+
renderTocEntries(ctx, ctx.tocElements);
|
|
5443
|
+
ctx.push("</div>");
|
|
5444
|
+
}
|
|
5445
|
+
|
|
5446
|
+
// packages/render/src/elements/toc/frame.ts
|
|
5447
|
+
function isFloatingToc(align) {
|
|
5448
|
+
return align === "left" || align === "right";
|
|
5449
|
+
}
|
|
5450
|
+
function openTocFrame(ctx, align) {
|
|
5451
|
+
if (!isFloatingToc(align)) {
|
|
4665
5452
|
ctx.push(`<table style="margin:0; padding:0"><tr><td style="margin:0; padding:0">`);
|
|
4666
5453
|
}
|
|
4667
|
-
if (
|
|
4668
|
-
const floatClass =
|
|
5454
|
+
if (isFloatingToc(align)) {
|
|
5455
|
+
const floatClass = align === "left" ? "floatleft" : "floatright";
|
|
4669
5456
|
ctx.push(`<div id="toc" class="${floatClass}">`);
|
|
4670
5457
|
} else {
|
|
4671
5458
|
ctx.push(`<div id="toc">`);
|
|
4672
5459
|
}
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
ctx.push(`<div id="toc-list">`);
|
|
4676
|
-
renderTocEntries(ctx, ctx.tocElements);
|
|
4677
|
-
ctx.push("</div>");
|
|
5460
|
+
}
|
|
5461
|
+
function closeTocFrame(ctx, align) {
|
|
4678
5462
|
ctx.push("</div>");
|
|
4679
|
-
if (!
|
|
5463
|
+
if (!isFloatingToc(align)) {
|
|
4680
5464
|
ctx.push(`</td></tr></table>`);
|
|
4681
5465
|
}
|
|
4682
5466
|
}
|
|
4683
5467
|
|
|
4684
|
-
// packages/render/src/elements/
|
|
4685
|
-
function
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
}
|
|
4690
|
-
|
|
4691
|
-
// packages/render/src/elements/clear-float.ts
|
|
4692
|
-
function renderClearFloat(ctx, direction) {
|
|
4693
|
-
ctx.push(`<div style="clear:${direction}; height: 0px; font-size: 1px"></div>`);
|
|
5468
|
+
// packages/render/src/elements/toc/index.ts
|
|
5469
|
+
function renderTableOfContents(ctx, data) {
|
|
5470
|
+
openTocFrame(ctx, data.align);
|
|
5471
|
+
renderTocBody(ctx);
|
|
5472
|
+
closeTocFrame(ctx, data.align);
|
|
4694
5473
|
}
|
|
4695
5474
|
|
|
4696
|
-
// packages/render/src/elements/
|
|
4697
|
-
function
|
|
4698
|
-
|
|
4699
|
-
const attrs = [`src="${escapeAttr(url)}"`];
|
|
4700
|
-
const iframeAttrs = ["align", "frameborder", "height", "scrolling", "width", "class", "style"];
|
|
4701
|
-
for (const attr of iframeAttrs) {
|
|
4702
|
-
let value = data.attributes[attr] ?? "";
|
|
4703
|
-
if (attr === "style") {
|
|
4704
|
-
value = sanitizeStyleValue(value);
|
|
4705
|
-
}
|
|
4706
|
-
attrs.push(`${attr}="${escapeAttr(value)}"`);
|
|
4707
|
-
}
|
|
4708
|
-
ctx.push(`<p><iframe ${attrs.join(" ")}></iframe></p>`);
|
|
5475
|
+
// packages/render/src/elements/user/resolve.ts
|
|
5476
|
+
function getResolvedUser(ctx, username) {
|
|
5477
|
+
return ctx.options.resolvers?.user?.(username) ?? null;
|
|
4709
5478
|
}
|
|
4710
5479
|
|
|
4711
|
-
// packages/render/src/elements/
|
|
4712
|
-
function
|
|
4713
|
-
const
|
|
4714
|
-
const
|
|
4715
|
-
|
|
4716
|
-
|
|
5480
|
+
// packages/render/src/elements/user/markup.ts
|
|
5481
|
+
function renderLinkedUser(ctx, username, user) {
|
|
5482
|
+
const displayName = user.name ?? username;
|
|
5483
|
+
const hrefAttr = user.url ? ` href="${escapeAttr(user.url)}"` : "";
|
|
5484
|
+
ctx.push(`<span class="printuser">`);
|
|
5485
|
+
ctx.push(`<a${hrefAttr}>`);
|
|
5486
|
+
ctx.push(escapeHtml(displayName));
|
|
5487
|
+
ctx.push("</a>");
|
|
5488
|
+
ctx.push("</span>");
|
|
4717
5489
|
}
|
|
4718
|
-
function
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
const
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
ctx.push(
|
|
5490
|
+
function renderAvatarUser(ctx, username, user) {
|
|
5491
|
+
const displayName = user.name ?? username;
|
|
5492
|
+
const hrefAttr = user.url ? ` href="${escapeAttr(user.url)}"` : "";
|
|
5493
|
+
const avatarUrl = user.avatarUrl ?? "";
|
|
5494
|
+
const styleAttr = user.karmaUrl ? ` style="background-image:url(${escapeAttr(user.karmaUrl)})"` : "";
|
|
5495
|
+
ctx.push(`<span class="printuser avatarhover">`);
|
|
5496
|
+
ctx.push(`<a${hrefAttr}>`);
|
|
5497
|
+
ctx.push(`<img class="small" src="${escapeAttr(avatarUrl)}" alt="${escapeAttr(displayName)}"${styleAttr} />`);
|
|
5498
|
+
ctx.push("</a>");
|
|
5499
|
+
ctx.push(`<a${hrefAttr}>`);
|
|
5500
|
+
ctx.push(escapeHtml(displayName));
|
|
5501
|
+
ctx.push("</a>");
|
|
5502
|
+
ctx.push("</span>");
|
|
4730
5503
|
}
|
|
4731
5504
|
|
|
4732
|
-
// packages/render/src/elements/
|
|
4733
|
-
function
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
const safePath = encodedPageName.startsWith("/") ? encodedPageName.slice(1) : encodedPageName;
|
|
4738
|
-
ctx.push(`<div class="error-block"><p>Included page "${escapeHtml(pageName)}" does not exist (<a href="/${escapeAttr(safePath)}/edit/true">create it now</a>)</p></div>`);
|
|
5505
|
+
// packages/render/src/elements/user/index.ts
|
|
5506
|
+
function renderUser(ctx, data) {
|
|
5507
|
+
const normalized = data.name.toLowerCase().trim();
|
|
5508
|
+
if (normalized === "anonymous") {
|
|
5509
|
+
ctx.push("Anonymous");
|
|
4739
5510
|
return;
|
|
4740
5511
|
}
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
function evaluateIfTagsCondition(condition, pageTags) {
|
|
4746
|
-
const pageTagSet = new Set(pageTags.map((t) => t.toLowerCase()));
|
|
4747
|
-
const tokens = condition.split(/\s+/).filter(Boolean);
|
|
4748
|
-
if (tokens.length === 0) {
|
|
4749
|
-
return false;
|
|
4750
|
-
}
|
|
4751
|
-
const required = [];
|
|
4752
|
-
const excluded = [];
|
|
4753
|
-
const optional = [];
|
|
4754
|
-
for (const token of tokens) {
|
|
4755
|
-
if (token.startsWith("+")) {
|
|
4756
|
-
const tag = token.slice(1).toLowerCase();
|
|
4757
|
-
if (tag)
|
|
4758
|
-
required.push(tag);
|
|
4759
|
-
} else if (token.startsWith("-")) {
|
|
4760
|
-
const tag = token.slice(1).toLowerCase();
|
|
4761
|
-
if (tag)
|
|
4762
|
-
excluded.push(tag);
|
|
4763
|
-
} else {
|
|
4764
|
-
optional.push(token.toLowerCase());
|
|
4765
|
-
}
|
|
4766
|
-
}
|
|
4767
|
-
if (required.length === 0 && excluded.length === 0 && optional.length === 0) {
|
|
4768
|
-
return false;
|
|
4769
|
-
}
|
|
4770
|
-
for (const tag of required) {
|
|
4771
|
-
if (!pageTagSet.has(tag))
|
|
4772
|
-
return false;
|
|
4773
|
-
}
|
|
4774
|
-
for (const tag of excluded) {
|
|
4775
|
-
if (pageTagSet.has(tag))
|
|
4776
|
-
return false;
|
|
4777
|
-
}
|
|
4778
|
-
if (optional.length > 0) {
|
|
4779
|
-
const hasAnyOptional = optional.some((tag) => pageTagSet.has(tag));
|
|
4780
|
-
if (!hasAnyOptional)
|
|
4781
|
-
return false;
|
|
5512
|
+
const resolved = getResolvedUser(ctx, data.name);
|
|
5513
|
+
if (resolved === null) {
|
|
5514
|
+
ctx.push(escapeHtml(data.name));
|
|
5515
|
+
return;
|
|
4782
5516
|
}
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
const prev = ctx.renderInlineStyles;
|
|
4789
|
-
ctx.renderInlineStyles = true;
|
|
4790
|
-
const slotId = data._styleSlot;
|
|
4791
|
-
if (slotId !== undefined) {
|
|
4792
|
-
ctx.enterStyleSlot(slotId);
|
|
4793
|
-
}
|
|
4794
|
-
renderElements(ctx, data.elements);
|
|
4795
|
-
if (slotId !== undefined) {
|
|
4796
|
-
ctx.exitStyleSlot();
|
|
4797
|
-
}
|
|
4798
|
-
ctx.renderInlineStyles = prev;
|
|
5517
|
+
const showAvatar = data["show-avatar"] && resolved.url && resolved.avatarUrl;
|
|
5518
|
+
if (showAvatar) {
|
|
5519
|
+
renderAvatarUser(ctx, data.name, resolved);
|
|
5520
|
+
} else {
|
|
5521
|
+
renderLinkedUser(ctx, data.name, resolved);
|
|
4799
5522
|
}
|
|
4800
5523
|
}
|
|
4801
5524
|
|
|
4802
|
-
// packages/render/src/
|
|
4803
|
-
function
|
|
4804
|
-
|
|
4805
|
-
ctx.push(`<span style="color: ${escapeAttr(safeColor)}">`);
|
|
4806
|
-
renderElements(ctx, data.elements);
|
|
4807
|
-
ctx.push("</span>");
|
|
4808
|
-
}
|
|
4809
|
-
|
|
4810
|
-
// packages/render/src/elements/date.ts
|
|
4811
|
-
function renderDate(ctx, data) {
|
|
4812
|
-
const date = new Date(data.value.timestamp * 1000);
|
|
4813
|
-
const formatted = data.format ? formatDate(date, data.format) : date.toLocaleString();
|
|
4814
|
-
if (data.hover) {
|
|
4815
|
-
ctx.push(`<span class="odate">${escapeHtml(formatted)}</span>`);
|
|
4816
|
-
} else {
|
|
4817
|
-
ctx.push(escapeHtml(formatted));
|
|
4818
|
-
}
|
|
5525
|
+
// packages/render/src/render/primitives.ts
|
|
5526
|
+
function renderTextNode(ctx, text) {
|
|
5527
|
+
ctx.pushEscaped(text);
|
|
4819
5528
|
}
|
|
4820
|
-
function
|
|
4821
|
-
|
|
5529
|
+
function renderLineBreak(ctx) {
|
|
5530
|
+
ctx.push("<br />");
|
|
4822
5531
|
}
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
var import_ast3 = require("@wdprlib/ast");
|
|
4826
|
-
function renderExpr(ctx, data) {
|
|
4827
|
-
const result = import_ast3.evaluateExpression(data.expression);
|
|
4828
|
-
if (result.success) {
|
|
4829
|
-
ctx.pushEscaped(import_ast3.formatExprValue(result.value));
|
|
4830
|
-
} else if (result.error !== "empty expression") {
|
|
4831
|
-
ctx.pushEscaped(`run-time error: ${result.error}`);
|
|
4832
|
-
}
|
|
5532
|
+
function renderHorizontalRule(ctx) {
|
|
5533
|
+
ctx.push("<hr />");
|
|
4833
5534
|
}
|
|
4834
|
-
function
|
|
4835
|
-
|
|
4836
|
-
renderBranchElements(ctx, elements);
|
|
5535
|
+
function renderContentSeparator(ctx) {
|
|
5536
|
+
ctx.push(`<div class="content-separator" style="display: none:"></div>`);
|
|
4837
5537
|
}
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
5538
|
+
|
|
5539
|
+
// packages/render/src/render/style.ts
|
|
5540
|
+
function renderStyleElement(ctx, css) {
|
|
5541
|
+
if (!ctx.renderInlineStyles || !ctx.settings.allowStyleElements) {
|
|
4842
5542
|
return;
|
|
4843
5543
|
}
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
function renderBranchElements(ctx, elements) {
|
|
4848
|
-
let lastIdx = elements.length - 1;
|
|
4849
|
-
while (lastIdx >= 0) {
|
|
4850
|
-
const el = elements[lastIdx];
|
|
4851
|
-
if (el.element === "text" && typeof el.data === "string" && el.data.trim() === "") {
|
|
4852
|
-
lastIdx--;
|
|
4853
|
-
} else {
|
|
4854
|
-
break;
|
|
4855
|
-
}
|
|
5544
|
+
if (ctx.hasActiveStyleSlot()) {
|
|
5545
|
+
ctx.pushToStyleSlot(css);
|
|
5546
|
+
return;
|
|
4856
5547
|
}
|
|
4857
|
-
|
|
5548
|
+
pushStyleTag(ctx, css);
|
|
4858
5549
|
}
|
|
4859
5550
|
|
|
4860
|
-
// packages/render/src/render.ts
|
|
4861
|
-
function renderToHtml(tree, options = {}) {
|
|
4862
|
-
const ctx = new RenderContext(tree, options);
|
|
4863
|
-
renderElements(ctx, tree.elements);
|
|
4864
|
-
if (ctx.settings.allowStyleElements && tree.styles?.length) {
|
|
4865
|
-
for (const style of tree.styles) {
|
|
4866
|
-
if (style.startsWith(import_ast4.STYLE_SLOT_PREFIX)) {
|
|
4867
|
-
const slotId = parseInt(style.slice(import_ast4.STYLE_SLOT_PREFIX.length), 10);
|
|
4868
|
-
for (const css of ctx.getStyleSlotContents(slotId)) {
|
|
4869
|
-
ctx.push(`<style>${escapeStyleContent(css)}</style>`);
|
|
4870
|
-
}
|
|
4871
|
-
} else {
|
|
4872
|
-
ctx.push(`<style>${escapeStyleContent(style)}</style>`);
|
|
4873
|
-
}
|
|
4874
|
-
}
|
|
4875
|
-
}
|
|
4876
|
-
return ctx.getOutput();
|
|
4877
|
-
}
|
|
5551
|
+
// packages/render/src/render/dispatch.ts
|
|
4878
5552
|
function renderElements(ctx, elements) {
|
|
4879
5553
|
for (const element of elements) {
|
|
4880
5554
|
renderElement(ctx, element);
|
|
@@ -4883,7 +5557,7 @@ function renderElements(ctx, elements) {
|
|
|
4883
5557
|
function renderElement(ctx, element) {
|
|
4884
5558
|
switch (element.element) {
|
|
4885
5559
|
case "text":
|
|
4886
|
-
ctx
|
|
5560
|
+
renderTextNode(ctx, element.data);
|
|
4887
5561
|
break;
|
|
4888
5562
|
case "raw":
|
|
4889
5563
|
renderRaw(ctx, element.data);
|
|
@@ -4982,16 +5656,10 @@ function renderElement(ctx, element) {
|
|
|
4982
5656
|
renderIfTags(ctx, element.data);
|
|
4983
5657
|
break;
|
|
4984
5658
|
case "style":
|
|
4985
|
-
|
|
4986
|
-
if (ctx.hasActiveStyleSlot()) {
|
|
4987
|
-
ctx.pushToStyleSlot(element.data);
|
|
4988
|
-
} else {
|
|
4989
|
-
ctx.push(`<style>${escapeStyleContent(element.data)}</style>`);
|
|
4990
|
-
}
|
|
4991
|
-
}
|
|
5659
|
+
renderStyleElement(ctx, element.data);
|
|
4992
5660
|
break;
|
|
4993
5661
|
case "line-break":
|
|
4994
|
-
ctx
|
|
5662
|
+
renderLineBreak(ctx);
|
|
4995
5663
|
break;
|
|
4996
5664
|
case "line-breaks":
|
|
4997
5665
|
renderLineBreaks(ctx, element.data);
|
|
@@ -5000,10 +5668,10 @@ function renderElement(ctx, element) {
|
|
|
5000
5668
|
renderClearFloat(ctx, element.data);
|
|
5001
5669
|
break;
|
|
5002
5670
|
case "horizontal-rule":
|
|
5003
|
-
ctx
|
|
5671
|
+
renderHorizontalRule(ctx);
|
|
5004
5672
|
break;
|
|
5005
5673
|
case "content-separator":
|
|
5006
|
-
ctx
|
|
5674
|
+
renderContentSeparator(ctx);
|
|
5007
5675
|
break;
|
|
5008
5676
|
case "expr":
|
|
5009
5677
|
renderExpr(ctx, element.data);
|
|
@@ -5020,5 +5688,13 @@ function renderElement(ctx, element) {
|
|
|
5020
5688
|
}
|
|
5021
5689
|
}
|
|
5022
5690
|
|
|
5691
|
+
// packages/render/src/render/index.ts
|
|
5692
|
+
function renderToHtml(tree, options = {}) {
|
|
5693
|
+
const ctx = new RenderContext(tree, options);
|
|
5694
|
+
renderElements(ctx, tree.elements);
|
|
5695
|
+
renderCollectedStyles(ctx, tree.styles);
|
|
5696
|
+
return ctx.getOutput();
|
|
5697
|
+
}
|
|
5698
|
+
|
|
5023
5699
|
// packages/render/src/index.ts
|
|
5024
|
-
var
|
|
5700
|
+
var import_ast6 = require("@wdprlib/ast");
|