summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
Diffstat (limited to 'js')
-rw-r--r--js/init.ts118
-rw-r--r--js/main.ts36
-rw-r--r--js/navigation.ts174
-rw-r--r--js/theme.ts70
4 files changed, 246 insertions, 152 deletions
diff --git a/js/init.ts b/js/init.ts
index 020171b..f249902 100644
--- a/js/init.ts
+++ b/js/init.ts
@@ -1,12 +1,114 @@
-/// <reference path="navigation.ts" />
-/// <reference path="theme.ts" />
+namespace Ach {
+ export async function init() {
+ Ach.loadTheme();
-function init() {
- window.addEventListener("popstate", (_e) => {
- location.reload();
- });
+ Ach.initImages();
+ Ach.initLinks();
- loadTheme();
+ window.addEventListener("popstate", (_e) => {
+ location.reload();
+ });
+ }
- initImages();
+ export async function initImages() {
+ let page = Ach.getOnlyElement(document, "page");
+
+ let imageList = Array.from(page.getElementsByTagName("x-image"));
+
+ for (let image of imageList) {
+ let alt = image.getAttribute("alt");
+ let file = image.getAttribute("data-file");
+
+ if (!alt) {
+ alert("stupid superuser forgot to add image caption, please notify");
+ alt = "";
+ }
+
+ if (!file) {
+ throw new Error("file not set for image element");
+ }
+
+ console.log("initialising image that links to \"" + file + "\"");
+
+ let sourceUrl = "/image/source/" + file + ".webp";
+ let thumbnailUrl = "/image/thumbnail/" + file + ".avif";
+
+ let blurElement = document.createElement("img");
+ blurElement.setAttribute("class", "blur");
+ blurElement.setAttribute("src", thumbnailUrl);
+
+ let hyperlinkElement = document.createElement("a");
+ hyperlinkElement.setAttribute("href", sourceUrl);
+ hyperlinkElement.setAttribute("rel", "noopener noreferrer");
+ hyperlinkElement.setAttribute("target", "_blank");
+
+ let image_element = document.createElement("img");
+ image_element.setAttribute("alt", alt);
+ image_element.setAttribute("src", thumbnailUrl);
+
+ hyperlinkElement.appendChild(image_element);
+
+ image.appendChild(blurElement);
+ image.appendChild(hyperlinkElement);
+ }
+ }
+
+ export async function initLinks() {
+ console.log("initialising links");
+
+ let stats = {
+ total: 0x0,
+ overrideCount: 0x0,
+ currentCount: 0x0,
+ };
+
+ for (let link of document.getElementsByTagName("a")) {
+ ++stats.total;
+
+ let address = link.getAttribute("href");
+
+ if (!address) {
+ console.log("note: skipping override of empty link");
+ continue;
+ }
+
+ let pageAnchor = Ach.parseInternalLink(address);
+
+ if (!pageAnchor) {
+ console.log(`note: skipping override of link to "${address}"`);
+ continue;
+ }
+
+ console.log(`note: overriding link to "${address}"`);
+ ++stats.overrideCount;
+
+ let command = `Ach.loadPage(${JSON.stringify(pageAnchor[0x0])}, ${JSON.stringify(pageAnchor[0x1])})`
+
+ link.removeAttribute("href");
+ link.setAttribute("data-page", pageAnchor[0x0]);
+ link.setAttribute("onclick", command);
+
+ if (pageAnchor[0x1]) {
+ link.setAttribute("data-anchor", pageAnchor[0x1]);
+ }
+ }
+
+ let currentPage = Ach.currentPage();
+
+ for (let link of document.getElementsByTagName("a")) {
+ let page = link.getAttribute("data-page");
+ let anchor = link.getAttribute("data-anchor");
+
+ if (page == currentPage && !anchor) {
+ ++stats.currentCount;
+ link.setAttribute("aria-current", "page");
+ } else {
+ link.setAttribute("aria-current", "false");
+ }
+ }
+
+ console.log(`note: initialised (${stats.total}) links`);
+ console.log(`note: of which (${stats.overrideCount}) overrides were done`);
+ console.log(`note: and of which (${stats.currentCount}) were marked as current`);
+ }
}
diff --git a/js/main.ts b/js/main.ts
new file mode 100644
index 0000000..44d28ad
--- /dev/null
+++ b/js/main.ts
@@ -0,0 +1,36 @@
+/// <reference path="init.ts" />
+/// <reference path="navigation.ts" />
+/// <reference path="theme.ts" />
+
+namespace Ach {
+ export function currentPage() {
+ let body = Ach.getFirstElement(document, "body");
+ let page = body.getAttribute("data-page");
+
+ if (!page) {
+ throw new Error("body does not specify page");
+ }
+
+ return page;
+ }
+
+ export function getFirstElement(dom: Document, tag: string): Element {
+ let elements = dom.getElementsByTagName(tag);
+
+ if (elements.length < 0x0) {
+ throw new Error(`unable to find <${tag}> element`);
+ }
+
+ return elements[0x0];
+ }
+
+ export function getOnlyElement(dom: Document, id: string): Element {
+ let element = dom.getElementById(id);
+
+ if (!element) {
+ throw new Error(`unable to find #${id} element`);
+ }
+
+ return element;
+ }
+}
diff --git a/js/navigation.ts b/js/navigation.ts
index 4a83be6..c1af99f 100644
--- a/js/navigation.ts
+++ b/js/navigation.ts
@@ -1,145 +1,97 @@
-async function loadPage(page_name: string, anchor?: string) {
- console.log(`loading page \`${page_name}\``);
+namespace Ach {
+ export async function loadPage(page_name: string, anchor?: string) {
+ console.log(`loading page \`${page_name}\``);
- let url = `/html/${page_name}.html`;
- console.log(`note: page is at "${url}"`);
+ window.scrollTo({
+ top: 0.0,
+ left: undefined,
+ behavior: "smooth",
+ });
- window.history.pushState(page_name, "", url);
+ let url = `/html/${page_name}.html`;
+ console.log(`note: page is at "${url}"`);
- let response = await fetch(url);
+ window.history.pushState(page_name, "", url);
- if (!response.ok) {
- throw new Error(`unable to load page: \"${response.status}\"`);
- }
+ let response = await fetch(url);
- let markup = await response.text();
+ if (!response.ok) {
+ throw new Error(`unable to load page: \"${response.status}\"`);
+ }
- let parser = new DOMParser();
- let dom = parser.parseFromString(markup, "text/html");
+ let markup = await response.text();
- let titles = document.getElementsByTagName("title");
- let bodies = document.getElementsByTagName("body");
- let page = document.getElementById("page")!;
+ let parser = new DOMParser();
+ let dom = parser.parseFromString(markup, "text/html");
- if (titles.length < 0x1) {
- throw new Error("unable to find title");
- }
+ let body = Ach.getFirstElement(document, "body");
- if (bodies.length < 0x1) {
- throw new Error("unable to find body");
- }
+ let title = Ach.getFirstElement(document, "title");
+ let page = Ach.getOnlyElement(document, "page");
- if (!page) {
- throw new Error("unable to find page element");
- }
+ let newTitle = Ach.getFirstElement(dom, "title");
+ let newPage = Ach.getOnlyElement(dom, "page");
- let newTitles = dom.getElementsByTagName("title");
- let newPage = dom.getElementById("page");
+ title.replaceWith(newTitle);
+ body.setAttribute("data-page", page_name);
+ page.replaceWith(newPage);
- if (newTitles.length < 0x1) {
- throw new Error("unable to find new title");
- }
+ initImages();
+ initLinks();
- if (!newPage) {
- throw new Error("unable to find new page element");
- }
+ if (anchor) {
+ console.log(`going to anchor \`${anchor}\``);
- titles[0x0].replaceWith(newTitles[0x0]);
- bodies[0x0].setAttribute("data-page", page_name);
- page.replaceWith(newPage);
+ anchor = `anchor.${anchor}`;
- initImages();
+ console.log(`note: anchor has id "${anchor}"`);
- if (anchor) {
- console.log(`going to anchor \`${anchor}\``);
+ let anchor_element = document.getElementById(anchor);
- anchor = `anchor.${anchor}`;
+ if (!anchor_element) {
+ throw new Error(`unable to find anchor "${anchor}"`);
+ }
- console.log(`note: anchor has id "${anchor}"`);
-
- let anchor_element = document.getElementById(anchor);
-
- if (!anchor_element) {
- throw new Error(`unable to find anchor "${anchor}"`);
+ anchor_element.scrollIntoView({
+ behavior: "smooth",
+ });
}
+ }
- anchor_element.scrollIntoView({
+ export function toggleSideMenu() {
+ window.scrollTo({
+ top: 0.0,
+ left: undefined,
behavior: "smooth",
});
- }
-}
-
-function toggleSideMenu() {
- scrollToTop();
-
- let sideMenu = document.getElementById("sideMenu");
- let navBar = document.getElementById("navBar");
- let glyph = document.getElementById("glyph");
-
- if (!sideMenu) {
- throw new Error("unable to find sideMenu");
- }
-
- if (!navBar) {
- throw new Error("unable to find navigation bar");
- }
-
- if (!glyph) {
- throw new Error("unable to find glyph");
- }
- sideMenu.classList.toggle("visible");
- glyph.classList.toggle("hidden");
+ let sideMenu = Ach.getOnlyElement(document, "sideMenu");
+ let navBar = Ach.getOnlyElement(document, "navBar");
+ let glyph = Ach.getOnlyElement(document, "glyph");
- for (let link of navBar.getElementsByTagName("a")) {
- link.classList.toggle("hidden");
- }
-}
-
-async function scrollToTop() {
- window.scroll({
- top: 0,
- left: 0,
- behavior: "smooth",
- });
-}
+ sideMenu.classList.toggle("visible");
+ glyph.classList.toggle("hidden");
-function initImages() {
- let page = document.getElementById("page");
-
- if (!page) {
- throw new Error("unable to find page");
+ for (let link of navBar.getElementsByTagName("a")) {
+ link.classList.toggle("hidden");
+ }
}
- let imageList = Array.from(page.getElementsByTagName("x-image"));
+ export function parseInternalLink(address: string): [string, string | undefined] | undefined {
+ let regex = /\/html\/([A-Za-z0-9]+)\.html(?:#([A-Za-z0-9]+)){0,1}/;
+ let regex_result = regex.exec(address);
- for (let image of imageList) {
- let file = image.getAttribute("data-file");
-
- if (!file) {
- throw new Error("file not set for image element")
+ if (!regex_result) {
+ return;
}
- console.log("initialising image that links to \"" + file + "\"");
-
- let sourceUrl = "/image/source/" + file + ".webp";
- let thumbnailUrl = "/image/thumbnail/" + file + ".avif";
+ let page = regex_result[0x1];
+ let anchor = regex_result[0x2];
- let blurElement = document.createElement("img");
- blurElement.setAttribute("class", "blur");
- blurElement.setAttribute("src", thumbnailUrl);
-
- let hyperlinkElement = document.createElement("a");
- hyperlinkElement.setAttribute("href", sourceUrl);
- hyperlinkElement.setAttribute("rel", "noopener noreferrer");
- hyperlinkElement.setAttribute("target", "_blank");
-
- let image_element = document.createElement("img");
- image_element.setAttribute("src", thumbnailUrl);
-
- hyperlinkElement.appendChild(image_element);
+ if (!page) {
+ return;
+ }
- image.appendChild(blurElement);
- image.appendChild(hyperlinkElement);
+ return [page, anchor];
}
}
diff --git a/js/theme.ts b/js/theme.ts
index 36959bc..45101da 100644
--- a/js/theme.ts
+++ b/js/theme.ts
@@ -1,51 +1,55 @@
-const DEFAULT_THEME = "dark";
+namespace Ach {
+ export function toggleTheme() {
+ let body = Ach.getFirstElement(document, "body");
-function toggleTheme() {
- let bodies = document.getElementsByTagName("body");
+ let theme = "light";
- if (!bodies) {
- throw new Error("unable to find body");
- }
+ if (body.classList.contains("light")) {
+ theme = "dark";
+ }
- let theme = "light";
+ console.log("setting theme to `" + theme + "`");
- if (bodies[0x0].classList.contains("light")) {
- theme = "dark";
+ body.classList.toggle("light");
+ sessionStorage.setItem("theme", theme);
}
- console.log("setting theme to `" + theme + "`");
+ export function loadTheme() {
+ let theme = sessionStorage.getItem("theme");
- bodies[0x0].classList.toggle("light");
- localStorage.setItem("theme", theme);
-}
+ if (!theme) {
+ console.log("theme not set, using default");
+ theme = defaultTheme();
+ }
-function loadTheme() {
- let theme = localStorage.getItem("theme");
+ switch (theme) {
+ case "dark":
+ // We assume this theme in our stylesheets.
+ break;
- if (!theme) {
- console.log("theme not set, using default");
- theme = DEFAULT_THEME;
- }
+ case "light":
+ let body = Ach.getFirstElement(document, "body");
+ body.classList.add("light");
- switch (theme) {
- case "dark":
- // We assume this theme in our stylesheets.
- break;
+ break;
- case "light":
- let bodies = document.getElementsByTagName("body");
+ default:
+ console.log(`invalid theme \"${theme}\", using default`);
+ //theme = DEFAULT_THEME; // Redundant now.
- if (!bodies) {
- throw new Error("unable to find body");
+ break;
}
- bodies[0x0].classList.add("light");
- break;
+ console.log(`note: theme is now \`${theme}\``);
+ }
- default:
- console.log(`invalid theme \"${theme}\", using default`);
- //theme = DEFAULT_THEME; // Redundant now.
+ function defaultTheme(): string {
+ let hour = new Date().getHours();
- break;
+ if (hour >= 0x6 && hour <= 0x12) {
+ return "light";
+ } else {
+ return "dark";
+ }
}
}