/* SPDX-License-Identifier: Apache-2.0
*
* The documentation reader.
*
* Renders the project's Markdown in the site's own theme, so the docs can be
* read without leaving the page and without a build step. No dependency: the
* parser below handles exactly what docs/ actually uses -- headings, fenced
* code, tables, lists, quotes, and inline marks. Pulling in a 40 KB Markdown
* library to render 29 files would outweigh every other asset here combined.
*
* Everything is escaped before any tag is emitted. The only HTML that reaches
* the page is HTML this file wrote.
*/
(function () {
"use strict";
/* ---- the index ---------------------------------------------------- */
var DOCS = [
{ group: "Start here", items: [
["README.md", "Overview"],
["plan/1.0.md", "The 1.0 definition"],
["licensing.md", "Licensing"]
]},
{ group: "Architecture", items: [
["architecture/README.md", "The system, layer by layer"],
["architecture/boot.md", "Boot"],
["architecture/kernel.md", "Kernel"],
["architecture/syscalls.md", "Syscalls"],
["architecture/filesystem.md", "The VFS"],
["architecture/schfs.md", "SchFS"],
["architecture/terminals.md", "Virtual terminals"],
["architecture/devices.md", "Device model"],
["architecture/usb.md", "USB"],
["architecture/graphics.md", "Graphics"],
["architecture/networking.md", "Networking"],
["architecture/init.md", "init"]
]},
{ group: "Guides", items: [
["guides/building.md", "Building"],
["guides/testing.md", "Testing"],
["guides/layout.md", "Tree layout"],
["guides/adding-a-driver.md", "Adding a driver"]
]},
{ group: "Plan", items: [
["plan/README.md", "What plan/ is for"],
["plan/roadmap.md", "Roadmap"],
["plan/foundations.md", "Foundations"],
["plan/init.md", "schinit"],
["plan/wayland.md", "Wayland"],
["plan/hybrid.md", "Hybrid kernel"],
["plan/schapi.md", "SchAPI"]
]},
{ group: "History", items: [
["history/README.md", "Why history/ exists"],
["history/kernel.md", "Kernel"],
["history/schfs.md", "SchFS"],
["history/moving-to-linux.md", "Moving to Linux"]
]},
{ group: "Branding", items: [
["branding/README.md", "The three marks"]
]}
];
/* ---- helpers ------------------------------------------------------- */
function esc(s) {
return s.replace(/&/g, "&").replace(//g, ">");
}
/* Resolve a doc-relative link against the doc it appeared in, so
"../history/schfs.md" inside architecture/ lands on history/schfs.md. */
function resolve(from, href) {
var base = from.split("/").slice(0, -1);
var parts = href.split("/");
for (var i = 0; i < parts.length; i++) {
if (parts[i] === "." || parts[i] === "") continue;
if (parts[i] === "..") base.pop();
else base.push(parts[i]);
}
return base.join("/");
}
/* encodeURIComponent turns every "/" into %2F, which works but makes a
shared link unreadable. Slashes are legal in a query value, so keep them. */
function docHref(p) {
return "?doc=" + encodeURIComponent(p).replace(/%2F/g, "/");
}
function slug(s) {
return s.toLowerCase().replace(/[^\w\s-]/g, "").trim().replace(/\s+/g, "-");
}
/* ---- inline marks --------------------------------------------------- */
/* U+0001 as the placeholder sentinel: it cannot occur in a Markdown source
file, so a code span lifted out here can never be confused with document
text when it is put back. */
var SENTINEL = "\u0001";
function inline(s, from) {
/* Code spans come out first and go back last, so nothing inside a backtick
pair is mistaken for emphasis or for a link. */
var spans = [];
s = s.replace(/`([^`]+)`/g, function (_, code) {
spans.push("" + esc(code) + "");
return SENTINEL + (spans.length - 1) + SENTINEL;
});
s = esc(s);
s = s.replace(/!\[([^\]]*)\]\(([^)\s]+)[^)]*\)/g, function (_, alt, src) {
return '';
});
s = s.replace(/\[([^\]]+)\]\(([^)\s]+)[^)]*\)/g, function (_, text, href) {
if (/^https?:/.test(href))
return '' + text + "";
if (href.charAt(0) === "#")
return '' + text + "";
if (/\.md($|#)/.test(href)) {
var cut = href.indexOf("#");
var hash = cut >= 0 ? href.slice(cut) : "";
var path = cut >= 0 ? href.slice(0, cut) : href;
return '' + text + "";
}
/* Anything else is a file in the repo rather than a document. Link at it
relative to docs/, which is where this page's ../ points. */
return '' + text + "";
});
s = s.replace(/\*\*([^*]+)\*\*/g, "$1");
s = s.replace(/(^|[^*])\*([^*\n]+)\*/g, "$1$2");
var restore = new RegExp(SENTINEL + "(\\d+)" + SENTINEL, "g");
return s.replace(restore, function (_, n) { return spans[+n]; });
}
/* ---- blocks --------------------------------------------------------- */
function render(md, from, headings) {
md = md.replace(/\r\n?/g, "\n").replace(//g, "");
var L = md.split("\n"), out = [], i = 0, m;
function listBlock(ordered) {
var tag = ordered ? "ol" : "ul", items = [];
while (i < L.length) {
var li = L[i].match(ordered ? /^\s*\d+\.\s+(.*)$/ : /^\s*[-*+]\s+(.*)$/);
if (!li) {
/* an indented continuation line belongs to the item above it */
if (items.length && /^\s+\S/.test(L[i])) {
items[items.length - 1] += " " + L[i].trim();
i++;
continue;
}
break;
}
items.push(li[1]);
i++;
}
out.push("<" + tag + ">");
items.forEach(function (t) { out.push("
" + esc(buf.join("\n")) + "");
continue;
}
/* table: a pipe row followed by a separator row */
if (/^\s*\|/.test(line) && /^\s*\|?[\s:|-]+$/.test(L[i + 1] || "") && /-/.test(L[i + 1] || "")) {
var head = cells(line);
i += 2;
out.push('| " + inline(c, from) + " | "); }); out.push("
|---|
| " + inline(c, from) + " | "); }); out.push("
" + render(q.join("\n"), from, []) + ""); continue; } if (/^\s*[-*+]\s+/.test(line)) { listBlock(false); continue; } if (/^\s*\d+\.\s+/.test(line)) { listBlock(true); continue; } /* paragraph: runs to the next blank line or block opener */ var para = []; while (i < L.length && L[i].trim() && !/^\s*(```|>|#{1,6}\s|[-*+]\s|\d+\.\s|\|)/.test(L[i]) && !/^\s*(-{3,}|\*{3,}|_{3,})\s*$/.test(L[i])) { para.push(L[i++]); } if (para.length) out.push("
" + inline(para.join(" "), from) + "
"); else i++; /* a block opener we do not handle: never spin on it */ } return out.join("\n"); } /* ---- the page ------------------------------------------------------- */ var nav = document.getElementById("docnav"); var article = document.getElementById("doc"); var tocEl = document.getElementById("toc"); function buildNav() { DOCS.forEach(function (g) { var h = document.createElement("p"); h.className = "navgroup"; h.textContent = g.group; nav.appendChild(h); var ul = document.createElement("ul"); g.items.forEach(function (it) { var li = document.createElement("li"); var a = document.createElement("a"); a.href = docHref(it[0]); a.textContent = it[1]; a.dataset.path = it[0]; li.appendChild(a); ul.appendChild(li); }); nav.appendChild(ul); }); } function markCurrent(path) { Array.prototype.forEach.call(nav.querySelectorAll("a"), function (a) { a.classList.toggle("on", a.dataset.path === path); }); } function buildToc(headings) { tocEl.innerHTML = ""; var real = headings.filter(function (h) { return h.level >= 2; }); if (real.length < 2) { tocEl.hidden = true; return; } tocEl.hidden = false; var t = document.createElement("p"); t.className = "navgroup"; t.textContent = "On this page"; tocEl.appendChild(t); var ul = document.createElement("ul"); real.forEach(function (h) { var li = document.createElement("li"); li.className = "lvl" + h.level; var a = document.createElement("a"); a.href = "#" + h.id; a.textContent = h.text; li.appendChild(a); ul.appendChild(li); }); tocEl.appendChild(ul); } function fail(title, body) { article.innerHTML = "Browsers block fetch() on file:// URLs, so the " +
"reader cannot load the Markdown. The rest of the site works from a file; " +
"this page does not. Serve the repository root:
cd /home/nexoniarz/Dokumenty/Projects/SchOS\n' +
"python3 -m http.server 8000" +
"Then open http://localhost:8000/docs/Website/docs.html.
Or read this one directly: ' + esc(path) + "
"); } else { fail("Could not load that document", "" + esc(path) + " — " + esc(e.message) + "