1(async () => {
2 const readme = document.querySelector("#readme .markdown");
3 if (!readme || !window.markdownit || !window.DOMPurify) return;
4
5 const response = await fetch("/snowbound/plain/main/readme.md");
6 if (!response.ok) return;
7
8 const markdown = await response.text();
9 const rendered = window.markdownit({ html: true }).render(markdown);
10 const fragment = window.DOMPurify.sanitize(rendered, {
11 USE_PROFILES: { html: true },
12 RETURN_DOM_FRAGMENT: true,
13 });
14
15 const mediaBase = new URL("/snowbound/plain/main/", location.origin);
16 const linkBase = new URL("/snowbound/tree/main/", location.origin);
17 const rebase = (value, base) =>
18 value && !value.startsWith("/") && !value.startsWith("#") && !/^[a-z][a-z\d+.-]*:/i.test(value)
19 ? new URL(value, base).href
20 : value;
21
22 for (const image of fragment.querySelectorAll("img[src]")) {
23 image.src = rebase(image.getAttribute("src"), mediaBase);
24 for (const dimension of ["width", "height"]) {
25 const value = image.getAttribute(dimension);
26 if (value && /^\d+$/.test(value)) image.style[dimension] = `${value}px`;
27 }
28 }
29 for (const pictureSource of fragment.querySelectorAll("source[srcset]")) {
30 pictureSource.srcset = pictureSource.srcset.split(",").map(candidate => {
31 const [, path, descriptor] = candidate.trim().match(/^(\S+)(.*)$/) || [];
32 return path ? rebase(path, mediaBase) + descriptor : candidate;
33 }).join(", ");
34 }
35 for (const link of fragment.querySelectorAll("a[href]")) {
36 link.href = rebase(link.getAttribute("href"), linkBase);
37 }
38 readme.replaceChildren(fragment);
39})().catch(console.error);