("div.mw-headline-cont[id][data-name]")
- );
-
- // Init fuzzy search with headlines
- registerSearchEntries(
- el.map((element: HTMLDivElement, id) => ({
- id,
- page: docname,
- name: element.dataset.name.trim(),
- element,
- alignment: "start",
- }))
- );
-}
-
export function bindFunctions(root: HTMLElement, docname: string): void {
switch (docname) {
case "Guide_to_chemistry":
diff --git a/src/scripts/pages/chemistry.ts b/src/scripts/pages/chemistry.ts
index d320c01..fe1d904 100644
--- a/src/scripts/pages/chemistry.ts
+++ b/src/scripts/pages/chemistry.ts
@@ -1,6 +1,138 @@
import { registerSearchEntries } from "../search";
+import { findParent } from "../utils";
-export default function chemistryScript(root: HTMLElement): void {
+export function processChemistry(root: HTMLElement): void {
+ // Fix inconsistencies with on random parts
+ // Ideally I'd like a
or something on every part, wrapping it completely, but for now let's just kill 'em
+ root
+ .querySelectorAll(
+ "table.wikitable > tbody > tr:not(:first-child) > td:nth-child(2), .tooltiptext"
+ )
+ .forEach((td) => {
+ const tmp = td.cloneNode() as HTMLElement;
+ // The cast to Array is necessary because, while childNodes's NodeList technically has a forEach method, it's a live list and operations mess with its lenght in the middle of the loop.
+ // Nodes can only have one parent so append removes them from the original NodeList and shifts the following one back into the wrong index.
+ Array.from(td.childNodes).forEach((el) => {
+ if (el instanceof HTMLParagraphElement) {
+ tmp.append(...el.childNodes);
+ } else {
+ tmp.append(el);
+ }
+ });
+ td.parentNode.replaceChild(tmp, td);
+ });
+
+ // Enrich "x part" with checkboxes and parts
+ Array.from(root.querySelectorAll("td"))
+ .filter((el) => el.textContent.indexOf(" part") >= 0)
+ .forEach((el) => {
+ el.innerHTML = el.innerHTML.replace(
+ /((\d+)\s+(?:parts?|units?))(.*?(?:<\/a>|\n|$))/gi,
+ (match, ...m) =>
+ `") ? "bgus_part_tooltip" : ""
+ }" data-amount="${
+ m[1]
+ }"> ${m[0]} ${m[2].replace(
+ /()/gi,
+ '$1 '
+ )}`
+ );
+ });
+
+ // Wrap every recipe with extra metadata
+ root.querySelectorAll(".bgus_part").forEach((el) => {
+ if ("parts" in el.parentElement.dataset) {
+ el.parentElement.dataset.parts = (
+ parseInt(el.parentElement.dataset.parts, 10) +
+ parseInt(el.dataset.amount, 10)
+ ).toString();
+ } else {
+ el.parentElement.dataset.parts = el.dataset.amount;
+ }
+ });
+
+ // Remove "Removed medicines" section
+ const remTable = root.querySelector(
+ "#Non-craftable_Medicines + h4 + p + table"
+ );
+ remTable.parentElement.removeChild(remTable);
+
+ // Restructure recipes to work in a narrow window
+ root
+ .querySelectorAll("div[data-name] .wikitable.sortable tr")
+ .forEach((row) => {
+ const sectionEl = findParent(
+ row,
+ (sel) => "name" in sel.dataset && sel.dataset.name !== ""
+ );
+ const section = sectionEl.dataset.name;
+ if (row.querySelector("td") === null) {
+ // Remove unused rows if found
+ const headers = row.querySelectorAll("th");
+ headers.forEach((th, i) => {
+ if (i < 2) {
+ th.classList.add("table-head");
+ return;
+ }
+ th.parentElement.removeChild(th);
+ });
+ return;
+ }
+ const rows = Array.from(row.querySelectorAll("td")).slice(1);
+ let treatment = null;
+ let desc = null;
+ let metabolism = null;
+ let overdose = null;
+ let addiction = null;
+ // Handle special cases
+ switch (section) {
+ case "Components":
+ case "Virology Recipes":
+ [desc] = rows;
+ break;
+ case "Narcotics":
+ [desc, metabolism, overdose, addiction] = rows;
+ break;
+ case "Explosive Strength":
+ case "Other Reagents":
+ case "Mutation Toxins":
+ [desc, metabolism] = rows;
+ break;
+ default:
+ // All fields
+ [treatment, desc, metabolism, overdose, addiction] = rows;
+ }
+ const title = row.querySelector("th");
+ let content = ``;
+ if (treatment) {
+ content += `${treatment.innerHTML}
`;
+ }
+ if (metabolism) {
+ content += `${metabolism.innerHTML}
`;
+ }
+ if (addiction && addiction.innerHTML.trim() !== "N/A") {
+ content += `${addiction.innerHTML}
`;
+ }
+ if (overdose && overdose.innerHTML.trim() !== "N/A") {
+ content += `${overdose.innerHTML}
`;
+ }
+ if (desc) {
+ content += `${desc.innerHTML}
`;
+ }
+ title.classList.add("reagent-ext");
+ title.innerHTML = content;
+ if (desc) desc.parentElement.removeChild(desc);
+ if (treatment) treatment.parentElement.removeChild(treatment);
+ if (metabolism) metabolism.parentElement.removeChild(metabolism);
+ if (overdose) overdose.parentElement.removeChild(overdose);
+ if (addiction) addiction.parentElement.removeChild(addiction);
+ });
+}
+
+export function chemistryScript(root: HTMLElement): void {
// Add event to autofill child checkboxes
root
.querySelectorAll(".bgus_part_tooltip > .bgus_checkbox")
@@ -110,3 +242,5 @@ export default function chemistryScript(root: HTMLElement): void {
}
});
}
+
+export default { chemistryScript, processChemistry };
diff --git a/src/scripts/pages/generic.ts b/src/scripts/pages/generic.ts
new file mode 100644
index 0000000..f21d8bd
--- /dev/null
+++ b/src/scripts/pages/generic.ts
@@ -0,0 +1,20 @@
+import { registerSearchEntries } from "../search";
+
+export function genericScript(root: HTMLElement, docname: string): void {
+ const el = Array.from(
+ root.querySelectorAll("div.mw-headline-cont[id][data-name]")
+ );
+
+ // Init fuzzy search with headlines
+ registerSearchEntries(
+ el.map((element: HTMLDivElement, id) => ({
+ id,
+ page: docname,
+ name: element.dataset.name.trim(),
+ element,
+ alignment: "start",
+ }))
+ );
+}
+
+export default { genericScript };
diff --git a/src/scripts/pages/global.ts b/src/scripts/pages/global.ts
new file mode 100644
index 0000000..70f543b
--- /dev/null
+++ b/src/scripts/pages/global.ts
@@ -0,0 +1,106 @@
+import { findParent } from "../utils";
+import { darken, ColorFmt, lighten } from "../darkmode";
+
+export function processGlobal(root: HTMLElement, docname: string): void {
+ // Add header
+ const header = document.createElement("h1");
+ header.className = "pageheader";
+ header.appendChild(document.createTextNode(docname.replace(/_/g, " ")));
+ root.insertBefore(header, root.firstChild);
+
+ // Lazy load all images
+ root
+ .querySelectorAll("img")
+ .forEach((elem) => elem.setAttribute("loading", "lazy"));
+
+ // Remove edit links
+ root.querySelectorAll(".mw-editsection").forEach((editLink) => {
+ editLink.parentElement.removeChild(editLink);
+ });
+
+ // Darken bgcolor
+ root.querySelectorAll("*[bgcolor]").forEach((td) => {
+ let bgcolor = td.getAttribute("bgcolor");
+ // Shitty way to detect if it's hex or not
+ // Basically, none of the css colors long 6 letters only use hex letters
+ // THANK FUCKING GOD
+ if (bgcolor.length === 6 && !Number.isNaN(parseInt(bgcolor, 16))) {
+ bgcolor = `#${bgcolor}`;
+ }
+ td.setAttribute("bgcolor", darken(bgcolor, ColorFmt.HEX).slice(1));
+ });
+ root.querySelectorAll("*[style]").forEach((td) => {
+ if (td.style.backgroundColor !== "") {
+ td.style.backgroundColor = darken(td.style.backgroundColor, ColorFmt.RGB);
+ }
+ if (td.style.background !== "") {
+ td.style.backgroundColor = darken(td.style.background, ColorFmt.RGB);
+ }
+ });
+
+ // Lighten fgcolors
+ root.querySelectorAll("*[color]").forEach((td) => {
+ let color = td.getAttribute("color");
+ if (color.length === 6 && !Number.isNaN(parseInt(color, 16))) {
+ color = `#${color}`;
+ }
+ td.setAttribute("color", lighten(color, ColorFmt.HEX).slice(1));
+ });
+
+ // Remove fixed widths
+ root.querySelectorAll("table[width]").forEach((td) => {
+ td.setAttribute("width", "100%");
+ });
+ root.querySelectorAll("table[style]").forEach((td: HTMLTableElement) => {
+ if (td.style.width !== "") {
+ td.style.width = "100%";
+ }
+ });
+
+ // Fixup spacing on top quotes
+ root
+ .querySelectorAll("table .floatright > a > img")
+ .forEach((img) => {
+ const row = findParent(img, (el) => el instanceof HTMLTableRowElement);
+ const td = document.createElement("td");
+ row.appendChild(td);
+ });
+
+ // Fuck #toctitle
+ const toc = root.querySelector("#toc");
+ if (toc) {
+ const tocHeader = toc.querySelector("h2");
+ toc.parentNode.insertBefore(tocHeader, toc);
+ toc.removeChild(toc.querySelector("#toctitle"));
+ }
+
+ // Group headers and content so stickies don't overlap
+ root.querySelectorAll("h1,h2,h3").forEach((h3) => {
+ const parent = h3.parentNode;
+ const div = document.createElement("div");
+ parent.insertBefore(div, h3);
+ while (h3.nextSibling && !h3.nextSibling.nodeName.startsWith("H")) {
+ const sibling = h3.nextSibling;
+ parent.removeChild(sibling);
+ div.appendChild(sibling);
+ }
+ h3.parentNode.removeChild(h3);
+ div.insertBefore(h3, div.firstChild);
+ div.className = "mw-headline-cont";
+ });
+
+ // Move id from header to container, if one is found
+ root.querySelectorAll(".mw-headline").forEach((span) => {
+ // Find nearest container
+ const container = findParent(span, (el) =>
+ el.classList.contains("mw-headline-cont")
+ );
+ if (container) {
+ container.id = span.id;
+ span.id += "-span";
+ container.dataset.name = span.textContent;
+ }
+ });
+}
+
+export default { processGlobal };
diff --git a/src/scripts/pages/virology.ts b/src/scripts/pages/virology.ts
index e69de29..ce54a47 100644
--- a/src/scripts/pages/virology.ts
+++ b/src/scripts/pages/virology.ts
@@ -0,0 +1,8 @@
+export function virologyScript(root: HTMLElement): void {
+ const symptoms = document.querySelector("#Symptoms_Table .wikitable");
+ //parseTable(symptoms);
+}
+
+export default {
+ virologyScript,
+};