import h from "hyperscript"; import "normalize.css/normalize.css"; import "milligram/dist/milligram.css"; import "../css/common.css"; import "../css/nav.css"; /** * Alias for `document.querySelector`. * * @param q {string} the query string * @returns {HTMLElement} the element identified by the query string */ export const $ = q => document.querySelector(q); /** * Runs the given function once the page is loaded. * * This function can be used multiple times. It does not overwrite existing callbacks for the page load event. * * @param fun {function(...*): *} the function to run */ export const doAfterLoad = function (fun) { const oldOnLoad = window.onload || (() => { }); window.onload = (() => { oldOnLoad(); fun(); }); }; /** * Creates a navigation element for navigating through the website. * * Fetches entries asynchronously from the website's API. * * @returns {Promise} a navigation element, eventually */ export const nav = async function () { const entries = await fetch("https://fwdekker.com/api/nav/") .then(it => it.json()) .then(it => it.entries) .then(it => it.map(entry => unpackEntry(entry))) .catch(e => { console.error("Failed to fetch navigation elements", e); return []; }); const prefix = h("li", h("a", {href: "https://fwdekker.com/"}, h("div.logo", h("img.logo", {src: "https://fwdekker.com/favicon.png"})), h("b", "FWDekker") )); return h("nav.nav", h("ul", prefix, ...entries)); }; /** * Unpacks a navigation entry returned from the navigation API into an HTML element. * * @param entry {Object} the entry to unpack * @returns {HTMLElement} the navigation list entry as HTML, described by its children */ const unpackEntry = function(entry) { if (entry.entries.length === 0) return h("li", h("a", {href: entry.link, innerHTML: entry.name})); return h("li", h("a", {href: entry.link, innerHTML: `${entry.name} ▾`}), h("ul", entry.entries.map(it => unpackEntry(it))) ); }; /** * Creates a header element with the given title and description. * * @param title {string} the title to display, possibly including HTML * @param [description] {string} the description to display, possibly including HTML * @returns {HTMLElement} a header element */ export const header = function ({title, description}) { return h("header.header", h("section.container", h("h1", {innerHTML: title}), description !== undefined ? h("p", h("em", {innerHTML: description})) : undefined ) ); }; /** * Creates a footer element with the given data. * * @param [author] {string} the author * @param [authorURL] {string} the URL to link the author's name to * @param [license] {string} the type of license * @param [licenseURL] {string} the URL to the license file * @param [vcs] {string} the type of version control * @param [vcsURL] {string} the URL to the repository * @param [version] {string} the page version * @returns {HTMLElement} a footer element */ export const footer = function ({author, authorURL, license, licenseURL, vcs, vcsURL, version}) { return h("footer.footer", h("section.container", footerLink("Made by ", author, authorURL, ". "), footerLink("Licensed under the ", license, licenseURL, ". "), footerLink("Source code available on ", vcs, vcsURL, ". "), h("div", version || "", {style: {"float": "right"}}) ) ); }; /** * Constructs a link that is used in footers. * * @param prefix {string} the text to display before the text if the text is not undefined * @param text {string|undefined} the text to display, or `undefined` if the returned element should be empty * @param url {string|undefined} the URL to link the text to, or `undefined` if the text should not be a link * @param suffix {string} the text to display after the text if the text is not undefined * @returns {HTMLElement} a footer link element */ const footerLink = function (prefix, text, url, suffix) { if (text === undefined) return h("span"); return h("span", h("span", prefix), url !== undefined ? h("a", text, {href: url}) : h("span", text), h("span", suffix) ); };