Compare commits
4 commits
df3e997f36
...
77ddefc3d1
| Author | SHA1 | Date | |
|---|---|---|---|
| 77ddefc3d1 | |||
| 3b41d48b9b | |||
| b76a359e15 | |||
| 2b242229e9 |
5 changed files with 54 additions and 22 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -0,0 +1 @@
|
||||||
|
dist.html
|
||||||
22
README.txt
Normal file
22
README.txt
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
the tough guide to fantasyland
|
||||||
|
===============================
|
||||||
|
|
||||||
|
an interactive web version of diana wynne jones' satirical encyclopedia
|
||||||
|
of fantasy tropes. click any ALL CAPS term to jump to its definition.
|
||||||
|
|
||||||
|
features
|
||||||
|
--------
|
||||||
|
- auto-linking of capitalized terms to their definitions
|
||||||
|
- searchable sidebar with alphabetical navigation
|
||||||
|
- collapsible table of contents
|
||||||
|
- handles plurals, special cases, and alternate spellings
|
||||||
|
|
||||||
|
files
|
||||||
|
-----
|
||||||
|
index.html - the book content (converted from epub via calibre/tidy)
|
||||||
|
script.js - term detection, linking, toc generation
|
||||||
|
style.css - parchment aesthetic
|
||||||
|
|
||||||
|
usage
|
||||||
|
-----
|
||||||
|
just open index.html in a browser. no build step.
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
/>
|
/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>The Tough Guide to Fantasyland</title>
|
<title>The Tough Guide to Fantasyland</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" inline />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
|
|
@ -17597,9 +17597,6 @@
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script
|
<script type="text/javascript" src="script.js" inline></script>
|
||||||
type="text/javascript"
|
|
||||||
src="script.js"
|
|
||||||
></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
11
justfile
Normal file
11
justfile
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
deploy_dir := "/var/www/html/dungeon.red/fantasyland"
|
||||||
|
|
||||||
|
default: deploy
|
||||||
|
|
||||||
|
compile:
|
||||||
|
bunx inline-source-cli index.html dist.html
|
||||||
|
|
||||||
|
deploy: compile
|
||||||
|
mkdir -p {{deploy_dir}}
|
||||||
|
cp dist.html {{deploy_dir}}/index.html
|
||||||
|
@echo "deployed to {{deploy_dir}}/index.html"
|
||||||
35
script.js
35
script.js
|
|
@ -1,3 +1,5 @@
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
const cleanTerm = (text) => text.replace(/\s+/g, " ").replace(/\.$/, "").trim();
|
const cleanTerm = (text) => text.replace(/\s+/g, " ").replace(/\.$/, "").trim();
|
||||||
|
|
||||||
const slugify = (text) =>
|
const slugify = (text) =>
|
||||||
|
|
@ -38,7 +40,7 @@ termEntries.forEach(([term, id]) => {
|
||||||
link.textContent = term;
|
link.textContent = term;
|
||||||
const item = document.createElement("li");
|
const item = document.createElement("li");
|
||||||
item.appendChild(link);
|
item.appendChild(link);
|
||||||
tocList.appendChild(item);
|
tocList?.appendChild(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
Array.from(firstByLetter.entries())
|
Array.from(firstByLetter.entries())
|
||||||
|
|
@ -47,13 +49,13 @@ Array.from(firstByLetter.entries())
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.href = `#${id}`;
|
link.href = `#${id}`;
|
||||||
link.textContent = letter;
|
link.textContent = letter;
|
||||||
tocLetters.appendChild(link);
|
tocLetters?.appendChild(link);
|
||||||
});
|
});
|
||||||
|
|
||||||
const searchInput = document.getElementById("toc-search");
|
const searchInput = document.getElementById("toc-search");
|
||||||
searchInput.addEventListener("input", (event) => {
|
searchInput?.addEventListener("input", (event) => {
|
||||||
const query = event.target.value.trim().toLowerCase();
|
const query = event.target.value.trim().toLowerCase();
|
||||||
Array.from(tocList.querySelectorAll("li")).forEach((item) => {
|
Array.from(tocList?.querySelectorAll("li") ?? []).forEach((item) => {
|
||||||
const text = item.textContent.toLowerCase();
|
const text = item.textContent.toLowerCase();
|
||||||
item.style.display = text.includes(query) ? "" : "none";
|
item.style.display = text.includes(query) ? "" : "none";
|
||||||
});
|
});
|
||||||
|
|
@ -252,10 +254,9 @@ const linkifyTextNode = (node) => {
|
||||||
node.parentNode.replaceChild(fragment, node);
|
node.parentNode.replaceChild(fragment, node);
|
||||||
};
|
};
|
||||||
|
|
||||||
const walker = document.createTreeWalker(
|
const contentEl = document.getElementById("content");
|
||||||
document.getElementById("content"),
|
if (contentEl) {
|
||||||
NodeFilter.SHOW_TEXT,
|
const walker = document.createTreeWalker(contentEl, NodeFilter.SHOW_TEXT, {
|
||||||
{
|
|
||||||
acceptNode: (node) => {
|
acceptNode: (node) => {
|
||||||
const parent = node.parentElement;
|
const parent = node.parentElement;
|
||||||
if (!parent) {
|
if (!parent) {
|
||||||
|
|
@ -269,16 +270,16 @@ const walker = document.createTreeWalker(
|
||||||
}
|
}
|
||||||
return NodeFilter.FILTER_ACCEPT;
|
return NodeFilter.FILTER_ACCEPT;
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const nodesToLinkify = [];
|
const nodesToLinkify = [];
|
||||||
while (walker.nextNode()) {
|
while (walker.nextNode()) {
|
||||||
nodesToLinkify.push(walker.currentNode);
|
nodesToLinkify.push(walker.currentNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
nodesToLinkify.forEach((node) => linkifyTextNode(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
nodesToLinkify.forEach((node) => linkifyTextNode(node));
|
|
||||||
|
|
||||||
// TOC collapse/expand toggle
|
// TOC collapse/expand toggle
|
||||||
const tocToggle = document.createElement("button");
|
const tocToggle = document.createElement("button");
|
||||||
tocToggle.className = "toc-toggle";
|
tocToggle.className = "toc-toggle";
|
||||||
|
|
@ -290,7 +291,7 @@ const toc = document.querySelector(".toc");
|
||||||
const layout = document.querySelector(".layout");
|
const layout = document.querySelector(".layout");
|
||||||
|
|
||||||
tocToggle.addEventListener("click", () => {
|
tocToggle.addEventListener("click", () => {
|
||||||
toc.classList.toggle("collapsed");
|
toc?.classList.toggle("collapsed");
|
||||||
layout.classList.toggle("toc-hidden");
|
layout?.classList.toggle("toc-hidden");
|
||||||
document.body.classList.toggle("toc-collapsed");
|
document.body.classList.toggle("toc-collapsed");
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue