Compare commits
No commits in common. "e700bb9c309627b944618152a7d8e936ae7a05db" and "7930f961710641f022ef0cd3ad3277ffd4eab7eb" have entirely different histories.
e700bb9c30
...
7930f96171
11 changed files with 270 additions and 455 deletions
|
@ -19,7 +19,6 @@ module.exports = {
|
|||
"no-param-reassign": "off",
|
||||
"no-alert": "off",
|
||||
"no-console": "off",
|
||||
"func-names": "off",
|
||||
"@typescript-eslint/ban-ts-comment": [
|
||||
"error",
|
||||
{
|
||||
|
|
23
LICENSE
23
LICENSE
|
@ -1,23 +0,0 @@
|
|||
This code is license under ISC, full copy below:
|
||||
|
||||
Copyright 2020, Alessandro Gatti
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
This code uses idb-keyval, licensed under Apache 2:
|
||||
|
||||
Copyright 2016, Jake Archibald
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -8,4 +8,4 @@
|
|||
|
||||
## Development
|
||||
|
||||
`yarn dev`
|
||||
`yarn && yarn dev`
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-import": "2.21.2",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"idb-keyval": "^3.2.0",
|
||||
"parcel-bundler": "^1.12.4",
|
||||
"parcel-plugin-sw-cache": "^0.3.1",
|
||||
"prettier": "^2.0.5",
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
// @ts-expect-error: Asset imports are handled by parcel
|
||||
import speen from "~/assets/images/speen.svg";
|
||||
import { getPageHTML } from "./wiki";
|
||||
import { processHTML, bindFunctions, CURRENT_VERSION } from "./userscript";
|
||||
import cache from "./cache";
|
||||
import { nextAnimationFrame } from "./utils";
|
||||
import userscript from "./userscript";
|
||||
|
||||
// @ts-expect-error: Parcel image import
|
||||
import unknown from "~/assets/images/tab-icons/unknown.svg";
|
||||
|
@ -24,52 +22,21 @@ function initWaiting(elem: HTMLElement) {
|
|||
}
|
||||
|
||||
async function loadPage(page: string, elem: HTMLElement) {
|
||||
let html: string | null = null;
|
||||
const key = `page:${page}`;
|
||||
|
||||
// Check cache for pre-processed page
|
||||
try {
|
||||
const cachedPage = await cache.get<string>(key);
|
||||
if (cachedPage) {
|
||||
if (cachedPage.version === CURRENT_VERSION) {
|
||||
console.log(`${page}: found cached entry`);
|
||||
html = cachedPage.value;
|
||||
} else {
|
||||
console.log(`${page}: found outdated cache entry`);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`${page}: failed to retrieve cache entry:`, e);
|
||||
}
|
||||
|
||||
// Fetch page content
|
||||
if (!html) {
|
||||
console.log(`${page}: fetching`);
|
||||
html = await getPageHTML(page);
|
||||
console.log(`${page}: fetching`);
|
||||
let html = await getPageHTML(page);
|
||||
|
||||
// Convert relative links to absolute (and proxied)
|
||||
html = html.replace(/"\/wiki/gi, '"//tgproxy.ovo.ovh/wiki');
|
||||
// Convert relative links to absolute
|
||||
html = html.replace(/"\/wiki/gi, '"//tgstation13.org/wiki');
|
||||
|
||||
await nextAnimationFrame();
|
||||
|
||||
// Set as HTML content and run HTML manipulations on it
|
||||
// Set as HTML content and run HTML manipulations on it
|
||||
requestAnimationFrame(() => {
|
||||
elem.innerHTML = html;
|
||||
|
||||
console.log(`${page}: processing`);
|
||||
processHTML(elem, page);
|
||||
|
||||
// Save result to cache
|
||||
cache.set(key, elem.outerHTML, CURRENT_VERSION).then(() => {
|
||||
console.log(`${page}: saved to cache`);
|
||||
});
|
||||
} else {
|
||||
// Set cached content as HTML
|
||||
elem.outerHTML = html;
|
||||
}
|
||||
|
||||
bindFunctions(elem, page);
|
||||
console.log(`${page}: userscript applied`);
|
||||
elem.classList.remove("waiting");
|
||||
userscript(elem, page);
|
||||
console.log(`${page}: userscript applied`);
|
||||
elem.classList.remove("waiting");
|
||||
});
|
||||
}
|
||||
|
||||
type TabElements = {
|
||||
|
|
110
src/cache.ts
110
src/cache.ts
|
@ -1,110 +0,0 @@
|
|||
/* eslint-disable no-shadow */
|
||||
|
||||
interface CacheEntry<T> {
|
||||
version: string;
|
||||
value: T;
|
||||
}
|
||||
|
||||
export class Store {
|
||||
readonly dbp: Promise<IDBDatabase>;
|
||||
|
||||
constructor(dbName = "tg-cache", readonly storeName = "keyval") {
|
||||
this.dbp = new Promise((resolve, reject) => {
|
||||
const openreq = indexedDB.open(dbName, 1);
|
||||
openreq.onerror = () => reject(openreq.error);
|
||||
openreq.onsuccess = () => resolve(openreq.result);
|
||||
|
||||
// First time setup: create an empty object store
|
||||
openreq.onupgradeneeded = () => {
|
||||
openreq.result.createObjectStore(storeName);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
withIDBStore(
|
||||
type: IDBTransactionMode,
|
||||
callback: (store: IDBObjectStore) => void
|
||||
): Promise<void> {
|
||||
return this.dbp.then(
|
||||
(db) =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const transaction = db.transaction(this.storeName, type);
|
||||
transaction.oncomplete = () => resolve();
|
||||
transaction.onabort = () => reject(transaction.error);
|
||||
transaction.onerror = () => reject(transaction.error);
|
||||
callback(transaction.objectStore(this.storeName));
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let defaultStore: Store;
|
||||
|
||||
function getDefaultStore() {
|
||||
if (!defaultStore) defaultStore = new Store();
|
||||
return defaultStore;
|
||||
}
|
||||
|
||||
export function get<Type>(
|
||||
key: IDBValidKey,
|
||||
store = getDefaultStore()
|
||||
): Promise<CacheEntry<Type>> {
|
||||
let req: IDBRequest;
|
||||
return store
|
||||
.withIDBStore("readonly", (store) => {
|
||||
req = store.get(key);
|
||||
})
|
||||
.then(() => req.result);
|
||||
}
|
||||
|
||||
export function set<Type>(
|
||||
key: IDBValidKey,
|
||||
value: Type,
|
||||
version: string,
|
||||
store = getDefaultStore()
|
||||
): Promise<void> {
|
||||
return store.withIDBStore("readwrite", (store) => {
|
||||
store.put({ version, value }, key);
|
||||
});
|
||||
}
|
||||
|
||||
export function del(
|
||||
key: IDBValidKey,
|
||||
store = getDefaultStore()
|
||||
): Promise<void> {
|
||||
return store.withIDBStore("readwrite", (store) => {
|
||||
store.delete(key);
|
||||
});
|
||||
}
|
||||
|
||||
export function clear(store = getDefaultStore()): Promise<void> {
|
||||
return store.withIDBStore("readwrite", (store) => {
|
||||
store.clear();
|
||||
});
|
||||
}
|
||||
|
||||
export function keys(store = getDefaultStore()): Promise<IDBValidKey[]> {
|
||||
const dbkeys: IDBValidKey[] = [];
|
||||
|
||||
return store
|
||||
.withIDBStore("readonly", (store) => {
|
||||
// This would be store.getAllKeys(), but it isn't supported by Edge or Safari.
|
||||
// And openKeyCursor isn't supported by Safari.
|
||||
(store.openKeyCursor || store.openCursor).call(
|
||||
store
|
||||
).onsuccess = function () {
|
||||
if (!this.result) return;
|
||||
dbkeys.push(this.result.key);
|
||||
this.result.continue();
|
||||
};
|
||||
})
|
||||
.then(() => dbkeys);
|
||||
}
|
||||
|
||||
export default {
|
||||
get,
|
||||
set,
|
||||
keys,
|
||||
del,
|
||||
clear,
|
||||
};
|
|
@ -2,145 +2,7 @@ import { darken, ColorFmt, lighten } from "./darkmode";
|
|||
import searchBox from "./search";
|
||||
import { findParent } from "./utils";
|
||||
|
||||
// This is used for cache busting when userscript changes significantly.
|
||||
// Only change it when you made changes to the processHTML part!
|
||||
export const CURRENT_VERSION = "df914edcb5522670309ceb8dfd0195dc70fb81d4";
|
||||
|
||||
function chemistryFixups(root: HTMLElement) {
|
||||
// Enable page-specific CSS rules
|
||||
root.classList.add("bchem");
|
||||
|
||||
// Fix inconsistencies with <p> on random parts
|
||||
// Ideally I'd like a <p> 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) =>
|
||||
`<label class="bgus_part ${
|
||||
m[2].includes("</a>") ? "bgus_part_tooltip" : ""
|
||||
}" data-amount="${
|
||||
m[1]
|
||||
}"><input type="checkbox" class='bgus_checkbox bgus_hidden'/> <span class="bgus_part_label" data-src="${
|
||||
m[0]
|
||||
}">${m[0]}</span></label>${m[2].replace(
|
||||
/(<a .+?<\/a>)/gi,
|
||||
'<span class="bgus_nobreak bgus_nested_element">$1<span class="bgus_twistie"></span></span>'
|
||||
)}`
|
||||
);
|
||||
});
|
||||
|
||||
// Wrap every recipe with extra metadata
|
||||
root.querySelectorAll<HTMLElement>(".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<HTMLElement>("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 = `<div class="reagent-header">${title.innerHTML}</div>`;
|
||||
if (treatment) {
|
||||
content += `<p class="treatment">${treatment.innerHTML}</p>`;
|
||||
}
|
||||
if (metabolism) {
|
||||
content += `<p class="metabolism">${metabolism.innerHTML}</p>`;
|
||||
}
|
||||
if (addiction && addiction.innerHTML.trim() !== "N/A") {
|
||||
content += `<p class="addiction">${addiction.innerHTML}</p>`;
|
||||
}
|
||||
if (overdose && overdose.innerHTML.trim() !== "N/A") {
|
||||
content += `<p class="overdose">${overdose.innerHTML}</p>`;
|
||||
}
|
||||
if (desc) {
|
||||
content += `<p>${desc.innerHTML}</p>`;
|
||||
}
|
||||
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 processHTML(root: HTMLElement, docname: string): void {
|
||||
export default function userscript(root: HTMLElement, docname: string): void {
|
||||
// Add header
|
||||
const header = document.createElement("h1");
|
||||
header.className = "pageheader";
|
||||
|
@ -236,149 +98,281 @@ export function processHTML(root: HTMLElement, docname: string): void {
|
|||
}
|
||||
});
|
||||
|
||||
switch (docname) {
|
||||
case "Guide_to_chemistry":
|
||||
chemistryFixups(root);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
// Tell user that better chemistry is loading
|
||||
const postbody = root;
|
||||
const statusMessage = document.createElement("div");
|
||||
statusMessage.innerHTML = `
|
||||
<table style="background-color: black; margin-bottom:10px;" width="95%" align="center">
|
||||
<tbody><tr><td align="center">
|
||||
<b>Hang on...</b> Better guides is loading.
|
||||
</td></tr></tbody>
|
||||
</table>`;
|
||||
postbody.insertBefore(statusMessage, postbody.firstChild);
|
||||
|
||||
function chemistryScript(root: HTMLElement) {
|
||||
// Add event to autofill child checkboxes
|
||||
root
|
||||
.querySelectorAll(".bgus_part_tooltip > .bgus_checkbox")
|
||||
.forEach((box: HTMLInputElement) => {
|
||||
const tooltip = box.parentElement.nextElementSibling;
|
||||
box.addEventListener("click", () => {
|
||||
tooltip
|
||||
.querySelectorAll(".bgus_checkbox")
|
||||
.forEach((el: HTMLInputElement) => {
|
||||
el.checked = box.checked;
|
||||
});
|
||||
function betterChemistry() {
|
||||
// Fix inconsistencies with <p> on random parts
|
||||
// Ideally I'd like a <p> or something on every part, wrapping it completely, but for now let's just kill 'em
|
||||
document
|
||||
.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(document.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) =>
|
||||
`<label class="bgus_part ${
|
||||
m[2].includes("</a>") ? "bgus_part_tooltip" : ""
|
||||
}" data-amount="${
|
||||
m[1]
|
||||
}"><input type="checkbox" class='bgus_checkbox bgus_hidden'/> <span class="bgus_part_label" data-src="${
|
||||
m[0]
|
||||
}">${m[0]}</span></label>${m[2].replace(
|
||||
/(<a .+?<\/a>)/gi,
|
||||
'<span class="bgus_nobreak bgus_nested_element">$1<span class="bgus_twistie"></span></span>'
|
||||
)}`
|
||||
);
|
||||
});
|
||||
// Add event to autofill child checkboxes
|
||||
root
|
||||
.querySelectorAll(".bgus_part_tooltip > .bgus_checkbox")
|
||||
.forEach((box: HTMLInputElement) => {
|
||||
const tooltip = box.parentElement.nextElementSibling;
|
||||
box.addEventListener("click", () => {
|
||||
tooltip
|
||||
.querySelectorAll(".bgus_checkbox")
|
||||
.forEach((el: HTMLInputElement) => {
|
||||
el.checked = box.checked;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Add event to collapse subsections
|
||||
root.querySelectorAll(".bgus_nested_element").forEach((twistie) => {
|
||||
twistie.addEventListener("click", () => {
|
||||
twistie.classList.toggle("bgus_collapsed");
|
||||
});
|
||||
});
|
||||
|
||||
// Add event to collapse subsections
|
||||
root.querySelectorAll(".bgus_nested_element").forEach((twistie) => {
|
||||
twistie.addEventListener("click", () => {
|
||||
twistie.classList.toggle("bgus_collapsed");
|
||||
});
|
||||
});
|
||||
|
||||
const setPartSize = (labels, ml) => {
|
||||
labels.forEach((el) => {
|
||||
const part = el.parentElement.dataset.amount;
|
||||
const total = el.parentElement.parentElement.dataset.parts;
|
||||
const amt = Math.ceil(ml * (part / total));
|
||||
el.innerHTML = `${amt} ml`;
|
||||
// Lookup tooltips
|
||||
let next = el.parentElement.nextElementSibling;
|
||||
while (next) {
|
||||
if (next.classList.contains("tooltip")) {
|
||||
const sublabels = [];
|
||||
next.querySelector(".tooltiptext").childNodes.forEach((ch) => {
|
||||
if (ch.classList && ch.classList.contains("bgus_part")) {
|
||||
sublabels.push(ch.querySelector(".bgus_part_label"));
|
||||
}
|
||||
});
|
||||
setPartSize(sublabels, amt);
|
||||
}
|
||||
if (next.classList.contains("bgus_part")) {
|
||||
// Done searching
|
||||
break;
|
||||
}
|
||||
next = next.nextElementSibling;
|
||||
// Wrap every recipe with extra metadata
|
||||
root.querySelectorAll<HTMLElement>(".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;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Init fuzzy search with elements
|
||||
const el = Array.from(
|
||||
root.querySelectorAll<HTMLElement>(
|
||||
"table.wikitable > tbody > tr:not(:first-child) > th"
|
||||
)
|
||||
);
|
||||
const name = el.map((elem) =>
|
||||
elem.querySelector(".reagent-header").textContent.trim().replace("▮", "")
|
||||
);
|
||||
const box = searchBox(
|
||||
el,
|
||||
name.map((e, i) => ({ id: i, str: e }))
|
||||
);
|
||||
document.body.appendChild(box);
|
||||
|
||||
document.body.addEventListener("keydown", (ev) => {
|
||||
if (ev.shiftKey) {
|
||||
switch (ev.keyCode) {
|
||||
// SHIFT+C = Toggle checkboxes
|
||||
case 67: {
|
||||
root.classList.toggle("bgus_cbox");
|
||||
root
|
||||
.querySelectorAll(".bgus_checkbox:checked")
|
||||
.forEach((sel: HTMLInputElement) => {
|
||||
sel.checked = false;
|
||||
const setPartSize = (labels, ml) => {
|
||||
labels.forEach((el) => {
|
||||
const part = el.parentElement.dataset.amount;
|
||||
const total = el.parentElement.parentElement.dataset.parts;
|
||||
const amt = Math.ceil(ml * (part / total));
|
||||
el.innerHTML = `${amt} ml`;
|
||||
// Lookup tooltips
|
||||
let next = el.parentElement.nextElementSibling;
|
||||
while (next) {
|
||||
if (next.classList.contains("tooltip")) {
|
||||
const sublabels = [];
|
||||
next.querySelector(".tooltiptext").childNodes.forEach((ch) => {
|
||||
if (ch.classList && ch.classList.contains("bgus_part")) {
|
||||
sublabels.push(ch.querySelector(".bgus_part_label"));
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// SHIFT+B = Set whole size (beaker?) for parts/units
|
||||
case 66: {
|
||||
const size = parseInt(
|
||||
prompt("Write target ml (0 to reset)", "90"),
|
||||
10
|
||||
);
|
||||
if (Number.isNaN(size) || size <= 0) {
|
||||
// Reset to parts/unit
|
||||
root
|
||||
.querySelectorAll(".bgus_part_label")
|
||||
.forEach((sel: HTMLElement) => {
|
||||
sel.innerHTML = sel.dataset.src;
|
||||
});
|
||||
return;
|
||||
setPartSize(sublabels, amt);
|
||||
}
|
||||
setPartSize(
|
||||
root.querySelectorAll("td > .bgus_part > .bgus_part_label"),
|
||||
+size
|
||||
);
|
||||
break;
|
||||
if (next.classList.contains("bgus_part")) {
|
||||
// Done searching
|
||||
break;
|
||||
}
|
||||
next = next.nextElementSibling;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
root.classList.add("bchem");
|
||||
// Init fuzzy search with elements
|
||||
const el = Array.from(
|
||||
root.querySelectorAll<HTMLElement>(
|
||||
"table.wikitable > tbody > tr:not(:first-child) > th"
|
||||
)
|
||||
);
|
||||
const name = el.map((elem) => {
|
||||
let partial = "";
|
||||
elem.childNodes.forEach((t) => {
|
||||
if (t instanceof Text) {
|
||||
partial += t.textContent;
|
||||
}
|
||||
});
|
||||
return partial.trim();
|
||||
});
|
||||
const box = searchBox(
|
||||
el,
|
||||
name.map((e, i) => ({ id: i, str: e }))
|
||||
);
|
||||
document.body.appendChild(box);
|
||||
|
||||
// Remove "Removed medicines" section
|
||||
const remTable = root.querySelector(
|
||||
"#Non-craftable_Medicines + h4 + p + table"
|
||||
);
|
||||
remTable.parentElement.removeChild(remTable);
|
||||
|
||||
root
|
||||
.querySelectorAll<HTMLElement>("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 = `<div class="reagent-header">${title.innerHTML}</div>`;
|
||||
if (treatment) {
|
||||
content += `<p class="treatment">${treatment.innerHTML}</p>`;
|
||||
}
|
||||
if (metabolism) {
|
||||
content += `<p class="metabolism">${metabolism.innerHTML}</p>`;
|
||||
}
|
||||
if (addiction && addiction.innerHTML.trim() !== "N/A") {
|
||||
content += `<p class="addiction">${addiction.innerHTML}</p>`;
|
||||
}
|
||||
if (overdose && overdose.innerHTML.trim() !== "N/A") {
|
||||
content += `<p class="overdose">${overdose.innerHTML}</p>`;
|
||||
}
|
||||
if (desc) {
|
||||
content += `<p>${desc.innerHTML}</p>`;
|
||||
}
|
||||
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);
|
||||
});
|
||||
|
||||
document.body.addEventListener("keydown", (ev) => {
|
||||
if (ev.shiftKey) {
|
||||
switch (ev.keyCode) {
|
||||
// SHIFT+C = Toggle checkboxes
|
||||
case 67: {
|
||||
root.classList.toggle("bgus_cbox");
|
||||
root
|
||||
.querySelectorAll(".bgus_checkbox:checked")
|
||||
.forEach((sel: HTMLInputElement) => {
|
||||
sel.checked = false;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// SHIFT+B = Set whole size (beaker?) for parts/units
|
||||
case 66: {
|
||||
const size = parseInt(
|
||||
prompt("Write target ml (0 to reset)", "90"),
|
||||
10
|
||||
);
|
||||
if (Number.isNaN(size) || size <= 0) {
|
||||
// Reset to parts/unit
|
||||
root
|
||||
.querySelectorAll(".bgus_part_label")
|
||||
.forEach((sel: HTMLElement) => {
|
||||
sel.innerHTML = sel.dataset.src;
|
||||
});
|
||||
return;
|
||||
}
|
||||
setPartSize(
|
||||
root.querySelectorAll("td > .bgus_part > .bgus_part_label"),
|
||||
+size
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function genericScript(root: HTMLElement) {
|
||||
const el = Array.from(
|
||||
root.querySelectorAll<HTMLElement>("div.mw-headline-cont[id][data-name]")
|
||||
);
|
||||
const name = el.map((elem: HTMLDivElement) => elem.dataset.name.trim());
|
||||
function betterGeneric() {
|
||||
const el = Array.from(
|
||||
root.querySelectorAll<HTMLElement>("div.mw-headline-cont[id][data-name]")
|
||||
);
|
||||
const name = el.map((elem: HTMLDivElement) => elem.dataset.name.trim());
|
||||
|
||||
// Init fuzzy search with headlines
|
||||
const box = searchBox(
|
||||
el,
|
||||
name.map((e, i) => ({ id: i, str: e })),
|
||||
{ alignment: "start" }
|
||||
);
|
||||
root.appendChild(box);
|
||||
}
|
||||
// Init fuzzy search with headlines
|
||||
const box = searchBox(
|
||||
el,
|
||||
name.map((e, i) => ({ id: i, str: e })),
|
||||
{ alignment: "start" }
|
||||
);
|
||||
root.appendChild(box);
|
||||
}
|
||||
|
||||
export function bindFunctions(root: HTMLElement, docname: string): void {
|
||||
switch (docname) {
|
||||
case "Guide_to_chemistry":
|
||||
chemistryScript(root);
|
||||
betterChemistry();
|
||||
break;
|
||||
default:
|
||||
genericScript(root);
|
||||
betterGeneric();
|
||||
break;
|
||||
}
|
||||
// Everything is loaded, remove loading bar
|
||||
statusMessage.innerHTML = "";
|
||||
}
|
||||
|
||||
export default {
|
||||
CURRENT_VERSION,
|
||||
processHTML,
|
||||
bindFunctions,
|
||||
};
|
||||
|
|
|
@ -12,8 +12,4 @@ export function findParent(
|
|||
return parent;
|
||||
}
|
||||
|
||||
export function nextAnimationFrame(): Promise<void> {
|
||||
return new Promise((resolve) => requestAnimationFrame(() => resolve()));
|
||||
}
|
||||
|
||||
export default { findParent, nextAnimationFrame };
|
||||
export default { findParent };
|
||||
|
|
|
@ -66,7 +66,6 @@ body {
|
|||
overflow: hidden;
|
||||
.page {
|
||||
//visibility: hidden;
|
||||
will-change: display;
|
||||
display: none;
|
||||
overflow-y: scroll;
|
||||
grid-row: 1;
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"baseUrl": ".",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"paths": {
|
||||
"~*": ["./src/*"]
|
||||
}
|
||||
|
|
|
@ -3230,11 +3230,6 @@ icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
|
||||
integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=
|
||||
|
||||
idb-keyval@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-3.2.0.tgz#cbbf354deb5684b6cdc84376294fc05932845bd6"
|
||||
integrity sha512-slx8Q6oywCCSfKgPgL0sEsXtPVnSbTLWpyiDcu6msHOyKOLari1TD1qocXVCft80umnkk3/Qqh3lwoFt8T/BPQ==
|
||||
|
||||
ieee754@^1.1.4:
|
||||
version "1.1.13"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
|
||||
|
|
Reference in a new issue