testing moving the functionality into small modules
This commit is contained in:
245
index.html
245
index.html
@ -125,15 +125,15 @@
|
||||
background: var(--accent);
|
||||
color: var(--background);
|
||||
}
|
||||
.menu-is-open .menu {
|
||||
.is-menu-open .menu {
|
||||
transform: translateX(0);
|
||||
transition-duration: 0.1s;
|
||||
}
|
||||
.menu-is-open .burger {
|
||||
.is-menu-open .burger {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.menu-is-open .burger,
|
||||
.is-menu-open .burger,
|
||||
#Loading {
|
||||
top: 0;
|
||||
right: 0;
|
||||
@ -197,243 +197,8 @@
|
||||
<p>❤</p>
|
||||
</div>
|
||||
<script type="module">
|
||||
//@ts-check
|
||||
|
||||
/*****************************************************************
|
||||
*
|
||||
* "THE FRAMEWORK"
|
||||
* a small set of utilities that help
|
||||
*
|
||||
****************************************************************/
|
||||
/**
|
||||
* markdown parser. Remove if you don't use markdown
|
||||
*/
|
||||
// @ts-ignore
|
||||
import { micromark } from "https://esm.sh/micromark@3?bundle";
|
||||
|
||||
const is_debug_mode = /^localhost|127.0.0.1/.test(
|
||||
window.location.hostname
|
||||
);
|
||||
|
||||
/**
|
||||
* A small utility to query elements and get back an array
|
||||
*/
|
||||
const $ = (parent, selector) => [
|
||||
...(!selector
|
||||
? document.querySelectorAll(parent)
|
||||
: parent.querySelectorAll(selector)),
|
||||
];
|
||||
|
||||
/**
|
||||
* Assumes a provided url is external if it begins by a known protocol
|
||||
* @param {string} url
|
||||
*/
|
||||
const isExternal = (url) =>
|
||||
url && /^(https?|mailto|tel|ftp|ipfs|dat):/.test(url);
|
||||
|
||||
/**
|
||||
* Makes sure urls to local pages get passed through the routing system
|
||||
* @param {HTMLElement} container the element containing links to find
|
||||
*/
|
||||
const rewriteLocalUrls = (container) => {
|
||||
$(container, "a").forEach((a) => {
|
||||
const href = a.getAttribute("href");
|
||||
if (href && !isExternal(href)) {
|
||||
a.setAttribute("href", "#/" + href.replace(/^\.?\//, ""));
|
||||
}
|
||||
});
|
||||
return container;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the hash part of the url, but only if it starts with a `/`
|
||||
* This allows regular hashes to continue to work
|
||||
* It reads also query parameters
|
||||
*/
|
||||
const getCurrentHashUrl = () => {
|
||||
const [path, searchStr] = (
|
||||
window.location.hash[1] === "/" ? window.location.hash.slice(2) : ""
|
||||
).split("?");
|
||||
const params = new URLSearchParams(searchStr);
|
||||
return { path, params };
|
||||
};
|
||||
|
||||
/**
|
||||
* useful to check for transitions while developing styles, if the loading screen disappears too fast
|
||||
* uses micromark. You can plug a different parser if you prefer
|
||||
*/
|
||||
const wait = is_debug_mode
|
||||
? (val) => new Promise((ok) => setTimeout(ok, 1000, val))
|
||||
: (val) => val;
|
||||
|
||||
/**
|
||||
* @param {string} htmlString
|
||||
*/
|
||||
const generateDOM = (htmlString) =>
|
||||
/** @type {HTMLElement} */ (
|
||||
new DOMParser().parseFromString(
|
||||
`<div>${htmlString}</div>`,
|
||||
"text/html"
|
||||
).firstChild
|
||||
);
|
||||
|
||||
/**
|
||||
* Loads and parses a markdown document. Makes use of micromark
|
||||
* @param {string} path the path to load
|
||||
*/
|
||||
const loadMarkdown = (path) =>
|
||||
fetch(is_debug_mode ? `./${path}?rand=${Math.random()}` : `./${path}`)
|
||||
.then((response) => response.text())
|
||||
.then(wait)
|
||||
.then((raw) => {
|
||||
const content = rewriteLocalUrls(generateDOM(micromark(raw)));
|
||||
const firstTitleElement = content.querySelector("h1");
|
||||
const title = firstTitleElement
|
||||
? firstTitleElement.textContent
|
||||
: path.replace(/\.\w{2, 4}$/, "");
|
||||
return { title, raw, content };
|
||||
});
|
||||
|
||||
/**
|
||||
* parses a filelist string. That's a string that looks like
|
||||
* ```
|
||||
* name dd-mm-yyyy link name
|
||||
* name dd-mm-yyyy
|
||||
* name linkname
|
||||
* name
|
||||
* ```
|
||||
* @param {string} lines
|
||||
*/
|
||||
const parseFileList = (lines) =>
|
||||
lines
|
||||
.trim()
|
||||
.split(`\n`)
|
||||
.map((line, index) => {
|
||||
const today = new Date().toISOString().split("T")[0];
|
||||
const result = line.match(
|
||||
/(?<name>.+)\.(?<ext>\w{2,3})(?:\s+(?<date>[\d-]+)?(?<title>.+))?/
|
||||
);
|
||||
if (!result) {
|
||||
console.error(`could not parse line ${index}: [${line}]`);
|
||||
return null;
|
||||
}
|
||||
const {
|
||||
groups: { name, ext, date = today, title = name },
|
||||
} = result;
|
||||
const href = `/${name}.${ext}`;
|
||||
const date_unix = new Date(date).getTime();
|
||||
return { name, href, date, title, date_unix, index };
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort(({ date_unix: a }, { date_unix: b }) => a - b);
|
||||
|
||||
/*****************************************************************
|
||||
*
|
||||
* Creating references to the important stuff
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
/**
|
||||
* The elements we will need
|
||||
*/
|
||||
const [Menu, Body, Loading, Source, Burger] = [
|
||||
"Menu",
|
||||
"Body",
|
||||
"Loading",
|
||||
"Source",
|
||||
"Burger",
|
||||
].map((id) => document.getElementById(id));
|
||||
|
||||
/**
|
||||
* cache the original title to append it to the page title
|
||||
*/
|
||||
const mainTitle = document.title;
|
||||
|
||||
const showLoadingOverlay = () =>
|
||||
document.body.classList.add("is-loading");
|
||||
const hideLoadingOverlay = () =>
|
||||
document.body.classList.remove("is-loading");
|
||||
|
||||
/*****************************************************************
|
||||
*
|
||||
* Router
|
||||
*
|
||||
* Things related to main navigation and to loading pages
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
/**
|
||||
* Listens to hashchange event, and attempts to read the url.
|
||||
* If the url is set,
|
||||
*/
|
||||
const onHashChange = async (evt) => {
|
||||
const { path, params } = getCurrentHashUrl();
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
showLoadingOverlay();
|
||||
const { title, raw, content } = await loadMarkdown(path);
|
||||
document.title = `${title} | ${mainTitle}`;
|
||||
Body.innerHTML = "";
|
||||
Source.innerHTML = raw;
|
||||
Body.appendChild(content);
|
||||
hideLoadingOverlay();
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the file list is obtained (presumably through loading)
|
||||
* parses the file list, then fills the side navigation
|
||||
* If there's no page loaded, it also loads the first page in the list
|
||||
* (the list gets sorted by date, but the first line is the one that gets used)
|
||||
* @param {string} lines
|
||||
*/
|
||||
const fillMenuFromFileList = (lines) => {
|
||||
const links = parseFileList(lines);
|
||||
Menu.innerHTML = links
|
||||
.map(({ href, title }) => `<a data-link href="#${href}">${title}</a>`)
|
||||
.join(`\n`);
|
||||
if (!getCurrentHashUrl().path) {
|
||||
const href = links.find(({ index }) => index === 0).href;
|
||||
window.location.hash = `#${href}`;
|
||||
} else {
|
||||
onHashChange();
|
||||
}
|
||||
};
|
||||
|
||||
/*****************************************************************
|
||||
*
|
||||
* Bootstrapping
|
||||
*
|
||||
* this is where things actually happen
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
const loadFileList = () => {
|
||||
showLoadingOverlay();
|
||||
|
||||
fetch("./files.txt")
|
||||
.then((response) => response.text())
|
||||
.then(fillMenuFromFileList);
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads the article list, parses it, creates the menu items
|
||||
*/
|
||||
(function bootstrap() {
|
||||
Burger.addEventListener("click", () =>
|
||||
document.body.classList.toggle("menu-is-open")
|
||||
);
|
||||
|
||||
window.addEventListener("hashchange", onHashChange);
|
||||
|
||||
loadFileList();
|
||||
|
||||
document.addEventListener(
|
||||
"keyup",
|
||||
({ key }) =>
|
||||
key === "?" && document.body.classList.toggle("is-source-enabled")
|
||||
);
|
||||
})();
|
||||
import blog from "./modules/templateBlog.mjs";
|
||||
blog();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Reference in New Issue
Block a user