@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
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { MathData } from "@wdprlib/ast";
|
|
2
|
+
import type { RenderContext } from "../../context";
|
|
3
|
+
import { escapeAttr, escapeHtml } from "../../escape";
|
|
4
|
+
import { renderLatexToMathML } from "./latex";
|
|
5
|
+
import { pushHiddenLatexSource, pushMathRender } from "./source";
|
|
6
|
+
|
|
7
|
+
export function renderMath(ctx: RenderContext, data: MathData): void {
|
|
8
|
+
const index = ctx.nextEquationIndex() + 1;
|
|
9
|
+
const latex = data["latex-source"];
|
|
10
|
+
const mathml = renderLatexToMathML(latex, true);
|
|
11
|
+
|
|
12
|
+
const id = data.name
|
|
13
|
+
? ctx.generateId("equation-", data.name)
|
|
14
|
+
: ctx.generateId("equation-", index);
|
|
15
|
+
const dataName = data.name ? ` data-name="${escapeAttr(data.name)}"` : "";
|
|
16
|
+
|
|
17
|
+
ctx.push(`<div class="math-block" id="${escapeAttr(id)}"${dataName}>`);
|
|
18
|
+
if (data.name) {
|
|
19
|
+
ctx.push(`<span class="equation-number">(${index})</span>`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pushHiddenLatexSource(ctx, latex);
|
|
23
|
+
pushMathRender(ctx, mathml, () => {
|
|
24
|
+
ctx.push(`<span class="math-error">`);
|
|
25
|
+
ctx.push(escapeHtml(latex));
|
|
26
|
+
ctx.push(`</span>`);
|
|
27
|
+
});
|
|
28
|
+
ctx.push("</div>");
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RenderContext } from "../../context";
|
|
2
|
+
import { escapeAttr, escapeHtml } from "../../escape";
|
|
3
|
+
|
|
4
|
+
export function renderEquationRef(ctx: RenderContext, name: string): void {
|
|
5
|
+
const id = ctx.generateId("equation-", name);
|
|
6
|
+
ctx.push(`<span class="eref" data-target="${escapeAttr(id)}">`);
|
|
7
|
+
ctx.push(`<a class="eref-link" href="#${escapeAttr(id)}">`);
|
|
8
|
+
ctx.push(escapeHtml(name));
|
|
9
|
+
ctx.push(`</a>`);
|
|
10
|
+
ctx.push(`<span class="eref-tooltip" aria-hidden="true"></span>`);
|
|
11
|
+
ctx.push("</span>");
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Renderers for Wikidot mathematical notation elements.
|
|
4
|
+
*
|
|
5
|
+
* - `[[math]]...[[/math]]` -- display-mode (block) math
|
|
6
|
+
* - `[[$ ... $]]` -- inline math
|
|
7
|
+
* - `[[eref name]]` -- equation reference
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export { renderMath } from "./block";
|
|
13
|
+
export { renderMathInline } from "./inline";
|
|
14
|
+
export { renderEquationRef } from "./equation-ref";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MathInlineData } from "@wdprlib/ast";
|
|
2
|
+
import type { RenderContext } from "../../context";
|
|
3
|
+
import { escapeHtml } from "../../escape";
|
|
4
|
+
import { renderLatexToMathML } from "./latex";
|
|
5
|
+
import { pushHiddenLatexSource, pushMathRender } from "./source";
|
|
6
|
+
|
|
7
|
+
export function renderMathInline(ctx: RenderContext, data: MathInlineData): void {
|
|
8
|
+
const latex = data["latex-source"];
|
|
9
|
+
const mathml = renderLatexToMathML(latex, false);
|
|
10
|
+
|
|
11
|
+
ctx.push(`<span class="math-inline">`);
|
|
12
|
+
pushHiddenLatexSource(ctx, latex);
|
|
13
|
+
pushMathRender(ctx, mathml, () => {
|
|
14
|
+
ctx.push(`<span class="math-error">$`);
|
|
15
|
+
ctx.push(escapeHtml(latex));
|
|
16
|
+
ctx.push(`$</span>`);
|
|
17
|
+
});
|
|
18
|
+
ctx.push("</span>");
|
|
19
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import temml from "temml";
|
|
2
|
+
|
|
3
|
+
export function renderLatexToMathML(latex: string, displayMode: boolean): string {
|
|
4
|
+
try {
|
|
5
|
+
let processedLatex = latex;
|
|
6
|
+
if (displayMode && needsAlignedWrapper(latex)) {
|
|
7
|
+
processedLatex = `\\begin{aligned}\n${latex}\n\\end{aligned}`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return temml.renderToString(processedLatex, {
|
|
11
|
+
displayMode,
|
|
12
|
+
throwOnError: false,
|
|
13
|
+
annotate: false,
|
|
14
|
+
});
|
|
15
|
+
} catch {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function needsAlignedWrapper(latex: string): boolean {
|
|
21
|
+
if (/\\begin\s*\{/.test(latex)) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const withoutEscaped = latex.replace(/\\&/g, "");
|
|
26
|
+
return withoutEscaped.includes("&");
|
|
27
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RenderContext } from "../../context";
|
|
2
|
+
import { escapeHtml } from "../../escape";
|
|
3
|
+
|
|
4
|
+
export function pushHiddenLatexSource(ctx: RenderContext, latex: string): void {
|
|
5
|
+
ctx.push(`<code class="math-source" hidden aria-hidden="true">`);
|
|
6
|
+
ctx.push(escapeHtml(latex));
|
|
7
|
+
ctx.push(`</code>`);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function pushMathRender(ctx: RenderContext, mathml: string, fallback: () => void): void {
|
|
11
|
+
ctx.push(`<span class="math-render">`);
|
|
12
|
+
if (mathml) {
|
|
13
|
+
ctx.push(mathml);
|
|
14
|
+
} else {
|
|
15
|
+
fallback();
|
|
16
|
+
}
|
|
17
|
+
ctx.push(`</span>`);
|
|
18
|
+
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import type { Module } from "@wdprlib/ast";
|
|
14
14
|
import type { RenderContext } from "../../context";
|
|
15
|
+
import { renderIndentedEmptyModuleContainer } from "./empty-container";
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Render a `[[module Backlinks]]` element as an empty container.
|
|
@@ -24,5 +25,5 @@ export function renderBacklinks(
|
|
|
24
25
|
_data: Extract<Module, { module: "backlinks" }>,
|
|
25
26
|
): void {
|
|
26
27
|
// Wikidot outputs just the container div (backlinks are populated at runtime)
|
|
27
|
-
ctx
|
|
28
|
+
renderIndentedEmptyModuleContainer(ctx, "backlinks-module-box");
|
|
28
29
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import type { Module } from "@wdprlib/ast";
|
|
13
13
|
import type { RenderContext } from "../../context";
|
|
14
|
+
import { renderEmptyModuleContainer } from "./empty-container";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Render a `[[module Categories]]` element as an empty container.
|
|
@@ -22,6 +23,5 @@ export function renderCategories(
|
|
|
22
23
|
ctx: RenderContext,
|
|
23
24
|
_data: Extract<Module, { module: "categories" }>,
|
|
24
25
|
): void {
|
|
25
|
-
ctx
|
|
26
|
-
ctx.push("</div>");
|
|
26
|
+
renderEmptyModuleContainer(ctx, "categories-module-box");
|
|
27
27
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RenderContext } from "../../context";
|
|
2
|
+
|
|
3
|
+
export function renderEmptyModuleContainer(ctx: RenderContext, className: string): void {
|
|
4
|
+
ctx.push(`<div class="${className}">`);
|
|
5
|
+
ctx.push("</div>");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function renderIndentedEmptyModuleContainer(ctx: RenderContext, className: string): void {
|
|
9
|
+
ctx.push(`<div class="${className}">\n\t</div>`);
|
|
10
|
+
}
|
|
@@ -22,6 +22,7 @@ import { renderPageTree } from "./page-tree";
|
|
|
22
22
|
import { renderRate } from "./rate";
|
|
23
23
|
import { renderListUsers } from "./listusers";
|
|
24
24
|
import { renderListPages } from "./listpages";
|
|
25
|
+
import { renderUnknownModule } from "./unknown";
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Render a `[[module]]` element by dispatching on the module name.
|
|
@@ -37,10 +38,7 @@ import { renderListPages } from "./listpages";
|
|
|
37
38
|
export function renderModule(ctx: RenderContext, data: Module): void {
|
|
38
39
|
switch (data.module) {
|
|
39
40
|
case "unknown":
|
|
40
|
-
|
|
41
|
-
ctx.push(
|
|
42
|
-
`<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>`,
|
|
43
|
-
);
|
|
41
|
+
renderUnknownModule(ctx, data);
|
|
44
42
|
break;
|
|
45
43
|
case "backlinks":
|
|
46
44
|
renderBacklinks(ctx, data);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Module } from "@wdprlib/ast";
|
|
2
|
+
import { escapeAttr, escapeHtml } from "../../escape";
|
|
3
|
+
|
|
4
|
+
type JoinModule = Extract<Module, { module: "join" }>;
|
|
5
|
+
|
|
6
|
+
export function renderJoinMarkup(data: JoinModule): string {
|
|
7
|
+
const buttonText = data["button-text"] ?? "Join";
|
|
8
|
+
const className = data.attributes?.class ?? "join-box";
|
|
9
|
+
return `<div class="${escapeAttr(className)}"><a href="javascript:;">${escapeHtml(buttonText)}</a></div>`;
|
|
10
|
+
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import type { Module } from "@wdprlib/ast";
|
|
13
13
|
import type { RenderContext } from "../../context";
|
|
14
|
-
import {
|
|
14
|
+
import { renderJoinMarkup } from "./join-markup";
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Render a `[[module Join]]` element with a clickable join button.
|
|
@@ -24,10 +24,5 @@ import { escapeAttr, escapeHtml } from "../../escape";
|
|
|
24
24
|
* @param data - Join module data with optional `button-text` and CSS class.
|
|
25
25
|
*/
|
|
26
26
|
export function renderJoin(ctx: RenderContext, data: Extract<Module, { module: "join" }>): void {
|
|
27
|
-
|
|
28
|
-
const attrs = data.attributes ?? {};
|
|
29
|
-
const className = attrs.class ?? "join-box";
|
|
30
|
-
ctx.push(`<div class="${escapeAttr(className)}">`);
|
|
31
|
-
ctx.push(`<a href="javascript:;">${escapeHtml(buttonText)}</a>`);
|
|
32
|
-
ctx.push("</div>");
|
|
27
|
+
ctx.push(renderJoinMarkup(data));
|
|
33
28
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import type { Module } from "@wdprlib/ast";
|
|
13
13
|
import type { RenderContext } from "../../context";
|
|
14
|
+
import { renderEmptyModuleContainer } from "./empty-container";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Render a `[[module ListPages]]` element as an empty container.
|
|
@@ -22,6 +23,5 @@ export function renderListPages(
|
|
|
22
23
|
ctx: RenderContext,
|
|
23
24
|
_data: Extract<Module, { module: "list-pages" }>,
|
|
24
25
|
): void {
|
|
25
|
-
ctx
|
|
26
|
-
ctx.push("</div>");
|
|
26
|
+
renderEmptyModuleContainer(ctx, "list-pages-box");
|
|
27
27
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import type { Module } from "@wdprlib/ast";
|
|
13
13
|
import type { RenderContext } from "../../context";
|
|
14
|
+
import { renderEmptyModuleContainer } from "./empty-container";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Render a `[[module ListUsers]]` element as an empty container.
|
|
@@ -22,6 +23,5 @@ export function renderListUsers(
|
|
|
22
23
|
ctx: RenderContext,
|
|
23
24
|
_data: Extract<Module, { module: "list-users" }>,
|
|
24
25
|
): void {
|
|
25
|
-
ctx
|
|
26
|
-
ctx.push("</div>");
|
|
26
|
+
renderEmptyModuleContainer(ctx, "list-users-module-box");
|
|
27
27
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import type { Module } from "@wdprlib/ast";
|
|
13
13
|
import type { RenderContext } from "../../context";
|
|
14
|
+
import { renderEmptyModuleContainer } from "./empty-container";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Render a `[[module PageTree]]` element as an empty container.
|
|
@@ -22,6 +23,5 @@ export function renderPageTree(
|
|
|
22
23
|
ctx: RenderContext,
|
|
23
24
|
_data: Extract<Module, { module: "page-tree" }>,
|
|
24
25
|
): void {
|
|
25
|
-
ctx
|
|
26
|
-
ctx.push("</div>");
|
|
26
|
+
renderEmptyModuleContainer(ctx, "page-tree-module-box");
|
|
27
27
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function getRateWidgetParts(): string[] {
|
|
2
|
+
return [
|
|
3
|
+
`<div class="page-rate-widget-box">`,
|
|
4
|
+
`<span class="rate-points">rating: <span class="number prw54353">0</span></span>`,
|
|
5
|
+
`<span class="rateup btn btn-default"><a title="I like it" href="javascript:;">+</a></span>`,
|
|
6
|
+
`<span class="ratedown btn btn-default"><a title="I don't like it" href="javascript:;">–</a></span>`,
|
|
7
|
+
`<span class="cancel btn btn-default"><a title="Cancel my vote" href="javascript:;">x</a></span>`,
|
|
8
|
+
"</div>",
|
|
9
|
+
];
|
|
10
|
+
}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import type { RenderContext } from "../../context";
|
|
18
|
+
import { getRateWidgetParts } from "./rate-markup";
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Render a `[[module Rate]]` page rating widget.
|
|
@@ -28,17 +29,7 @@ import type { RenderContext } from "../../context";
|
|
|
28
29
|
* @param ctx - The current render context.
|
|
29
30
|
*/
|
|
30
31
|
export function renderRate(ctx: RenderContext): void {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
`<span class="rateup btn btn-default"><a title="I like it" href="javascript:;">+</a></span>`,
|
|
35
|
-
);
|
|
36
|
-
// – is en-dash
|
|
37
|
-
ctx.push(
|
|
38
|
-
`<span class="ratedown btn btn-default"><a title="I don't like it" href="javascript:;">–</a></span>`,
|
|
39
|
-
);
|
|
40
|
-
ctx.push(
|
|
41
|
-
`<span class="cancel btn btn-default"><a title="Cancel my vote" href="javascript:;">x</a></span>`,
|
|
42
|
-
);
|
|
43
|
-
ctx.push("</div>");
|
|
32
|
+
for (const part of getRateWidgetParts()) {
|
|
33
|
+
ctx.push(part);
|
|
34
|
+
}
|
|
44
35
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Module } from "@wdprlib/ast";
|
|
2
|
+
import type { RenderContext } from "../../context";
|
|
3
|
+
|
|
4
|
+
export function renderUnknownModule(
|
|
5
|
+
ctx: RenderContext,
|
|
6
|
+
data: Extract<Module, { module: "unknown" }>,
|
|
7
|
+
): void {
|
|
8
|
+
ctx.push(
|
|
9
|
+
`<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>`,
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { TabData } from "@wdprlib/ast";
|
|
2
|
+
import type { RenderContext } from "../../context";
|
|
3
|
+
import { syncHashMd5 } from "../../hash";
|
|
4
|
+
|
|
5
|
+
export function getTabViewWidgetId(
|
|
6
|
+
ctx: RenderContext,
|
|
7
|
+
tabs: TabData[],
|
|
8
|
+
tabViewIndex: number,
|
|
9
|
+
): string {
|
|
10
|
+
const labelString = tabs.map((tab) => tab.label).join("");
|
|
11
|
+
return ctx.generateFixedId(`wiki-tabview-${tabViewIndex}-${syncHashMd5(labelString)}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getTabPanelId(ctx: RenderContext, tabViewIndex: number, tabIndex: number): string {
|
|
15
|
+
return ctx.generateId(`wiki-tab-${tabViewIndex}-`, tabIndex);
|
|
16
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Renderer for `[[tabview]]...[[/tabview]]` tab containers.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { TabData } from "@wdprlib/ast";
|
|
9
|
+
import type { RenderContext } from "../../context";
|
|
10
|
+
import { renderTabPanels } from "./panels";
|
|
11
|
+
import { renderTabNavigation } from "./navigation";
|
|
12
|
+
import { getTabViewWidgetId } from "./ids";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Render a `[[tabview]]` element with YUI-compatible HTML structure.
|
|
16
|
+
*
|
|
17
|
+
* The first tab is selected by default (visible, with the `selected` class
|
|
18
|
+
* on its nav item). All other tabs have `display:none` on their content divs.
|
|
19
|
+
*
|
|
20
|
+
* @param ctx - The current render context.
|
|
21
|
+
* @param tabs - Array of tab data, each with a label and child elements.
|
|
22
|
+
*/
|
|
23
|
+
export function renderTabView(ctx: RenderContext, tabs: TabData[]): void {
|
|
24
|
+
const tabViewIndex = ctx.nextTabViewIndex();
|
|
25
|
+
const widgetId = getTabViewWidgetId(ctx, tabs, tabViewIndex);
|
|
26
|
+
|
|
27
|
+
ctx.push(`<div id="${widgetId}" class="yui-navset">`);
|
|
28
|
+
renderTabNavigation(ctx, tabs);
|
|
29
|
+
renderTabPanels(ctx, tabs, tabViewIndex);
|
|
30
|
+
ctx.push("</div>");
|
|
31
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TabData } from "@wdprlib/ast";
|
|
2
|
+
import type { RenderContext } from "../../context";
|
|
3
|
+
import { escapeHtml } from "../../escape";
|
|
4
|
+
|
|
5
|
+
export function renderTabNavigation(ctx: RenderContext, tabs: TabData[]): void {
|
|
6
|
+
ctx.push(`<ul class="yui-nav">`);
|
|
7
|
+
for (let i = 0; i < tabs.length; i++) {
|
|
8
|
+
const tab = tabs[i]!;
|
|
9
|
+
const selectedClass = i === 0 ? ` class="selected"` : "";
|
|
10
|
+
ctx.push(`<li${selectedClass}>`);
|
|
11
|
+
ctx.push(`<a href="javascript:;"><em>${escapeHtml(tab.label)}</em></a>`);
|
|
12
|
+
ctx.push("</li>");
|
|
13
|
+
}
|
|
14
|
+
ctx.push("</ul>");
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { TabData } from "@wdprlib/ast";
|
|
2
|
+
import type { RenderContext } from "../../context";
|
|
3
|
+
import { renderElements } from "../../render";
|
|
4
|
+
import { getTabPanelId } from "./ids";
|
|
5
|
+
|
|
6
|
+
export function renderTabPanels(ctx: RenderContext, tabs: TabData[], tabViewIndex: number): void {
|
|
7
|
+
ctx.push(`<div class="yui-content">`);
|
|
8
|
+
for (let i = 0; i < tabs.length; i++) {
|
|
9
|
+
const tab = tabs[i]!;
|
|
10
|
+
const displayStyle = i === 0 ? "" : ` style="display:none"`;
|
|
11
|
+
ctx.push(`<div id="${getTabPanelId(ctx, tabViewIndex, i)}"${displayStyle}>`);
|
|
12
|
+
renderElements(ctx, tab.elements);
|
|
13
|
+
ctx.push("</div>");
|
|
14
|
+
}
|
|
15
|
+
ctx.push("</div>");
|
|
16
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { escapeAttr, sanitizeAttributes } from "../../escape";
|
|
2
|
+
|
|
3
|
+
export { renderTableCellAttrs } from "./cell-attributes";
|
|
4
|
+
|
|
5
|
+
export function renderTableAttrs(attributes: Record<string, string>): string {
|
|
6
|
+
let hasRenderableAttributes = false;
|
|
7
|
+
for (const key in attributes) {
|
|
8
|
+
if (!key.startsWith("_")) {
|
|
9
|
+
hasRenderableAttributes = true;
|
|
10
|
+
break;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
if (!hasRenderableAttributes) return "";
|
|
14
|
+
|
|
15
|
+
const safe = sanitizeAttributes(attributes);
|
|
16
|
+
let result = "";
|
|
17
|
+
for (const key in safe) {
|
|
18
|
+
if (key.startsWith("_")) continue;
|
|
19
|
+
const value = safe[key]!;
|
|
20
|
+
result += ` ${key}="${escapeAttr(value)}"`;
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { TableCell } from "@wdprlib/ast";
|
|
2
|
+
import { escapeAttr, sanitizeAttributes } from "../../escape";
|
|
3
|
+
|
|
4
|
+
export function renderTableCellAttrs(cell: TableCell): string {
|
|
5
|
+
const attrs: string[] = [];
|
|
6
|
+
const safeCellAttrs = sanitizeAttributes(cell.attributes);
|
|
7
|
+
|
|
8
|
+
appendColumnSpan(attrs, cell);
|
|
9
|
+
appendRowSpan(attrs, safeCellAttrs);
|
|
10
|
+
appendAlignmentStyle(attrs, cell, safeCellAttrs);
|
|
11
|
+
appendRemainingCellAttributes(attrs, cell, safeCellAttrs);
|
|
12
|
+
|
|
13
|
+
return attrs.length > 0 ? " " + attrs.join(" ") : "";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function appendColumnSpan(attrs: string[], cell: TableCell): void {
|
|
17
|
+
if (cell["column-span"] > 1) {
|
|
18
|
+
attrs.push(`colspan="${cell["column-span"]}"`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function appendRowSpan(attrs: string[], safeCellAttrs: Record<string, string>): void {
|
|
23
|
+
if (!safeCellAttrs.rowspan) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const rowspan = parseInt(safeCellAttrs.rowspan, 10);
|
|
28
|
+
if (rowspan > 1) {
|
|
29
|
+
attrs.push(`rowspan="${rowspan}"`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function appendAlignmentStyle(
|
|
34
|
+
attrs: string[],
|
|
35
|
+
cell: TableCell,
|
|
36
|
+
safeCellAttrs: Record<string, string>,
|
|
37
|
+
): void {
|
|
38
|
+
if (!cell.align) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const existingStyle = safeCellAttrs.style ?? "";
|
|
43
|
+
const alignStyle = `text-align: ${cell.align};`;
|
|
44
|
+
if (existingStyle) {
|
|
45
|
+
attrs.push(`style="${escapeAttr(existingStyle + "; " + alignStyle)}"`);
|
|
46
|
+
} else {
|
|
47
|
+
attrs.push(`style="${alignStyle}"`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function appendRemainingCellAttributes(
|
|
52
|
+
attrs: string[],
|
|
53
|
+
cell: TableCell,
|
|
54
|
+
safeCellAttrs: Record<string, string>,
|
|
55
|
+
): void {
|
|
56
|
+
for (const key in safeCellAttrs) {
|
|
57
|
+
if (key === "style" && cell.align) continue;
|
|
58
|
+
if (key === "rowspan") continue;
|
|
59
|
+
const value = safeCellAttrs[key]!;
|
|
60
|
+
attrs.push(`${key}="${escapeAttr(value)}"`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TableCell } from "@wdprlib/ast";
|
|
2
|
+
import type { RenderContext } from "../../context";
|
|
3
|
+
import { renderElements } from "../../render";
|
|
4
|
+
import { renderTableCellAttrs } from "./attributes";
|
|
5
|
+
|
|
6
|
+
export function renderTableCell(ctx: RenderContext, cell: TableCell): void {
|
|
7
|
+
const tag = cell.header ? "th" : "td";
|
|
8
|
+
const attrStr = renderTableCellAttrs(cell);
|
|
9
|
+
|
|
10
|
+
ctx.push(`<${tag}${attrStr}>`);
|
|
11
|
+
renderElements(ctx, cell.elements);
|
|
12
|
+
ctx.push(`</${tag}>`);
|
|
13
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Renderer for Wikidot table elements.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { TableData } from "@wdprlib/ast";
|
|
9
|
+
import type { RenderContext } from "../../context";
|
|
10
|
+
import { renderTableAttrs } from "./attributes";
|
|
11
|
+
import { renderTableCell } from "./cell";
|
|
12
|
+
|
|
13
|
+
export function renderTable(ctx: RenderContext, data: TableData): void {
|
|
14
|
+
const isPipeTable = data.attributes._source === "pipe";
|
|
15
|
+
const classAttr = isPipeTable ? ' class="wiki-content-table"' : "";
|
|
16
|
+
ctx.push(`<table${classAttr}${renderTableAttrs(data.attributes)}>`);
|
|
17
|
+
|
|
18
|
+
for (const row of data.rows) {
|
|
19
|
+
ctx.push(`<tr${renderTableAttrs(row.attributes)}>`);
|
|
20
|
+
for (const cell of row.cells) {
|
|
21
|
+
renderTableCell(ctx, cell);
|
|
22
|
+
}
|
|
23
|
+
ctx.push("</tr>");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
ctx.push("</table>");
|
|
27
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { RenderContext } from "../../context";
|
|
2
|
+
import { escapeAttr, escapeHtml, isValidEmail } from "../../escape";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Render an email address element as a `mailto:` link.
|
|
6
|
+
*
|
|
7
|
+
* The email is validated before creating the link. Invalid email addresses
|
|
8
|
+
* are rendered as plain escaped text to prevent `mailto:` injection.
|
|
9
|
+
*
|
|
10
|
+
* @param ctx - The current render context.
|
|
11
|
+
* @param email - The email address string.
|
|
12
|
+
*/
|
|
13
|
+
export function renderEmail(ctx: RenderContext, email: string): void {
|
|
14
|
+
if (!isValidEmail(email)) {
|
|
15
|
+
ctx.pushEscaped(email);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
ctx.push(`<a href="mailto:${escapeAttr(email)}">${escapeHtml(email)}</a>`);
|
|
20
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RenderContext } from "../../context";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Render a plain text node by HTML-escaping and appending to the output.
|
|
5
|
+
*
|
|
6
|
+
* @param ctx - The current render context.
|
|
7
|
+
* @param data - The raw text content.
|
|
8
|
+
*/
|
|
9
|
+
export function renderText(ctx: RenderContext, data: string): void {
|
|
10
|
+
ctx.pushEscaped(data);
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { RenderContext } from "../../context";
|
|
2
|
+
import { escapeHtml } from "../../escape";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Render raw/literal text (Wikidot `@@...@@` syntax).
|
|
6
|
+
*
|
|
7
|
+
* Raw text is rendered inside a `<span style="white-space: pre-wrap;">` with
|
|
8
|
+
* spaces encoded as ` ` to preserve Wikidot's exact formatting. Empty
|
|
9
|
+
* strings produce no output.
|
|
10
|
+
*
|
|
11
|
+
* @param ctx - The current render context.
|
|
12
|
+
* @param data - The raw text content.
|
|
13
|
+
*/
|
|
14
|
+
export function renderRaw(ctx: RenderContext, data: string): void {
|
|
15
|
+
if (data === "") return;
|
|
16
|
+
|
|
17
|
+
ctx.push(`<span style="white-space: pre-wrap;">`);
|
|
18
|
+
ctx.push(escapeHtml(data).replace(/ /g, " "));
|
|
19
|
+
ctx.push("</span>");
|
|
20
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RenderContext } from "../../context";
|
|
2
|
+
import { renderTocEntries } from "./entries";
|
|
3
|
+
|
|
4
|
+
export function renderTocBody(ctx: RenderContext): void {
|
|
5
|
+
ctx.push(
|
|
6
|
+
`<div id="toc-action-bar"><a href="javascript:;">Fold</a><a style="display: none" href="javascript:;">Unfold</a></div>`,
|
|
7
|
+
);
|
|
8
|
+
ctx.push(`<div class="title">Table of Contents</div>`);
|
|
9
|
+
ctx.push(`<div id="toc-list">`);
|
|
10
|
+
renderTocEntries(ctx, ctx.tocElements);
|
|
11
|
+
ctx.push("</div>");
|
|
12
|
+
}
|