Spaces:
Running
Running
File size: 4,240 Bytes
0e9afe0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | import { renderSafeMarkdown } from "/js/safe-markdown.js";
const FOOTNOTE_DEF_RE = /^\[\^([^\]]+)\]:\s*(.*)$/;
export function renderEditorPreviewMarkdown(markdown = "", fullMarkdown = markdown) {
return renderSafeMarkdown(prepareFootnotes(markdown, fullMarkdown), {
allowDataImages: true,
allowLatex: true,
openExternalLinksInNewTab: true,
});
}
export function buildMarkdownPages(markdown = "", fallbackTitle = "Markdown") {
const source = String(markdown || "");
return [{
index: 0,
title: fallbackTitle,
level: 0,
anchor: slugifyHeading(fallbackTitle),
start: 0,
end: source.length,
markdown: source,
}];
}
export function slugifyHeading(text = "", used = new Map()) {
const base = String(text || "")
.toLowerCase()
.replace(/<[^>]+>/g, "")
.replace(/[`*_~[\]()]/g, "")
.replace(/&[a-z0-9#]+;/gi, "")
.replace(/[^a-z0-9\s-]/g, "")
.trim()
.replace(/\s+/g, "-")
.replace(/-+/g, "-") || "section";
const count = used.get(base) || 0;
used.set(base, count + 1);
return count ? `${base}-${count + 1}` : base;
}
export function resolveDocumentRelativePath(documentPath = "", target = "") {
const value = String(target || "").trim();
if (!value) return "";
if (value.startsWith("/")) return normalizePath(value);
const base = parentPath(documentPath);
return normalizePath(`${base}/${value}`);
}
export function splitHref(href = "") {
const value = String(href || "").trim();
const hashIndex = value.indexOf("#");
if (hashIndex < 0) return { path: value, fragment: "" };
return {
path: value.slice(0, hashIndex),
fragment: decodeURIComponent(value.slice(hashIndex + 1) || ""),
};
}
export function isExternalHref(href = "") {
const value = String(href || "").trim();
return /^[a-zA-Z][a-zA-Z\d+.-]*:/.test(value) || value.startsWith("//");
}
export function isMarkdownPath(path = "") {
return /\.md(?:own)?$/i.test(String(path || "").split(/[?#]/, 1)[0]);
}
function prepareFootnotes(markdown = "", fullMarkdown = markdown) {
const definitions = [];
const body = [];
for (const line of String(fullMarkdown || "").split("\n")) {
const match = line.match(FOOTNOTE_DEF_RE);
if (match) {
definitions.push({ id: match[1], text: match[2] });
}
}
for (const line of String(markdown || "").split("\n")) {
if (line.match(FOOTNOTE_DEF_RE)) {
continue;
}
body.push(line);
}
if (!definitions.length) return markdown;
const counts = new Map();
let prepared = body.join("\n").replace(/\[\^([^\]]+)\]/g, (_all, id) => {
const number = definitions.findIndex((item) => item.id === id) + 1;
if (number <= 0) return `[^${id}]`;
const count = (counts.get(id) || 0) + 1;
counts.set(id, count);
const safeId = footnoteId(id);
return `<sup class="editor-footnote-ref"><a id="fnref-${safeId}-${count}" href="#fn-${safeId}">${number}</a></sup>`;
});
prepared += "\n\n<section class=\"editor-footnotes\" aria-label=\"Footnotes\">\n<ol>\n";
for (const definition of definitions) {
const safeId = footnoteId(definition.id);
prepared += `<li id="fn-${safeId}">${escapeHtml(definition.text)} <a class="editor-footnote-backref" href="#fnref-${safeId}-1">Back</a></li>\n`;
}
prepared += "</ol>\n</section>";
return prepared;
}
function footnoteId(id = "") {
return String(id || "")
.toLowerCase()
.replace(/[^a-z0-9_-]+/g, "-")
.replace(/^-+|-+$/g, "") || "note";
}
function parentPath(path = "") {
const normalized = String(path || "").split(/[?#]/, 1)[0].replace(/\/+$/, "");
const index = normalized.lastIndexOf("/");
if (index <= 0) return "/";
return normalized.slice(0, index);
}
function normalizePath(path = "") {
const absolute = String(path || "").startsWith("/");
const parts = [];
for (const part of String(path || "").split("/")) {
if (!part || part === ".") continue;
if (part === "..") {
parts.pop();
continue;
}
parts.push(part);
}
return `${absolute ? "/" : ""}${parts.join("/")}`;
}
function escapeHtml(value = "") {
return String(value || "")
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """);
}
|