From b63129d4b8a46b91a4e4c78cdd016c6f9825a13a Mon Sep 17 00:00:00 2001 From: D <@> Date: Sat, 20 Jun 2020 17:13:49 +0200 Subject: [PATCH] webscale --- src/TabManager.ts | 17 ++-- src/darkmode.ts | 196 +++++++++++++++++++++++++++++++++++++++++++--- src/userscript.ts | 114 ++++++++++++++++----------- src/utils.ts | 6 +- 4 files changed, 266 insertions(+), 67 deletions(-) diff --git a/src/TabManager.ts b/src/TabManager.ts index 0b038e0..a48b15e 100644 --- a/src/TabManager.ts +++ b/src/TabManager.ts @@ -2,6 +2,7 @@ import speen from "~/assets/images/speen.svg"; import { getPageHTML } from "./wiki"; import userscript from "./userscript"; +import { nextAnimationFrame } from "./utils"; function initWaiting(elem: HTMLElement) { // Add spinner @@ -27,13 +28,11 @@ async function loadPage(page: string, elem: HTMLElement) { html = html.replace(/"\/wiki/gi, '"//tgstation13.org/wiki'); // Set as HTML content and run HTML manipulations on it - requestAnimationFrame(() => { - elem.innerHTML = html; - console.log(`${page}: processing`); - userscript(elem, page); - console.log(`${page}: userscript applied`); - elem.classList.remove("waiting"); - }); + await nextAnimationFrame(); + elem.innerHTML = html; + await nextAnimationFrame(); + await userscript(elem, page); + elem.classList.remove("waiting"); } type TabElements = { tabListItem: HTMLElement; tabContentItem: HTMLElement }; @@ -50,7 +49,7 @@ export default class TabManager { this.tabContentContainer = tabcontent; } - openTab(page: string, setActive: boolean): void { + async openTab(page: string, setActive: boolean): void { // Create tab list item const tabListItem = document.createElement("div"); tabListItem.className = "tab"; @@ -72,7 +71,7 @@ export default class TabManager { this.tabContentContainer.appendChild(tabContentItem); // Start loading page for new tab - loadPage(page, tabContentItem); + await loadPage(page, tabContentItem); // Create tab entry this.tabs[page] = { tabListItem, tabContentItem }; diff --git a/src/darkmode.ts b/src/darkmode.ts index 4d697e1..1adae05 100644 --- a/src/darkmode.ts +++ b/src/darkmode.ts @@ -1,3 +1,5 @@ +import { nextAnimationFrame } from "./utils"; + interface ColorRGB { r: number; g: number; @@ -15,6 +17,166 @@ export enum ColorFmt { HEX, // #RRGGBB } +/* https://developer.mozilla.org/en-US/docs/Web/CSS/color_value + $$('#colors_table tbody tr') + .map((row) => { + const [name, hex] = Array.from(row.children).slice(-3); + return [name.innerText.trim().replace(/\s.+/g, ''), hex.innerText.trim()]; + }).sort((a, b) => [a[0], b[0]].sort()[0] === a[0] ? -1 : 1) + .map(([name, hex]) => `${name} : '${hex}',`) + .join('\n') +*/ +const namedColors = { + aliceblue : '#f0f8ff', + antiquewhite : '#faebd7', + aqua : '#00ffff', + aquamarine : '#7fffd4', + azure : '#f0ffff', + beige : '#f5f5dc', + bisque : '#ffe4c4', + black : '#000000', + blanchedalmond : '#ffebcd', + blue : '#0000ff', + blueviolet : '#8a2be2', + brown : '#a52a2a', + burlywood : '#deb887', + cadetblue : '#5f9ea0', + chartreuse : '#7fff00', + chocolate : '#d2691e', + coral : '#ff7f50', + cornflowerblue : '#6495ed', + cornsilk : '#fff8dc', + crimson : '#dc143c', + cyan : '#00ffff', + darkblue : '#00008b', + darkcyan : '#008b8b', + darkgoldenrod : '#b8860b', + darkgray : '#a9a9a9', + darkgreen : '#006400', + darkgrey : '#a9a9a9', + darkkhaki : '#bdb76b', + darkmagenta : '#8b008b', + darkolivegreen : '#556b2f', + darkorange : '#ff8c00', + darkorchid : '#9932cc', + darkred : '#8b0000', + darksalmon : '#e9967a', + darkseagreen : '#8fbc8f', + darkslateblue : '#483d8b', + darkslategray : '#2f4f4f', + darkslategrey : '#2f4f4f', + darkturquoise : '#00ced1', + darkviolet : '#9400d3', + deeppink : '#ff1493', + deepskyblue : '#00bfff', + dimgray : '#696969', + dimgrey : '#696969', + dodgerblue : '#1e90ff', + firebrick : '#b22222', + floralwhite : '#fffaf0', + forestgreen : '#228b22', + fuchsia : '#ff00ff', + gainsboro : '#dcdcdc', + ghostwhite : '#f8f8ff', + gold : '#ffd700', + goldenrod : '#daa520', + gray : '#808080', + green : '#008000', + greenyellow : '#adff2f', + grey : '#808080', + honeydew : '#f0fff0', + hotpink : '#ff69b4', + indianred : '#cd5c5c', + indigo : '#4b0082', + ivory : '#fffff0', + khaki : '#f0e68c', + lavender : '#e6e6fa', + lavenderblush : '#fff0f5', + lawngreen : '#7cfc00', + lemonchiffon : '#fffacd', + lightblue : '#add8e6', + lightcoral : '#f08080', + lightcyan : '#e0ffff', + lightgoldenrodyellow : '#fafad2', + lightgray : '#d3d3d3', + lightgreen : '#90ee90', + lightgrey : '#d3d3d3', + lightpink : '#ffb6c1', + lightsalmon : '#ffa07a', + lightseagreen : '#20b2aa', + lightskyblue : '#87cefa', + lightslategray : '#778899', + lightslategrey : '#778899', + lightsteelblue : '#b0c4de', + lightyellow : '#ffffe0', + lime : '#00ff00', + limegreen : '#32cd32', + linen : '#faf0e6', + magenta : '#ff00ff', + maroon : '#800000', + mediumaquamarine : '#66cdaa', + mediumblue : '#0000cd', + mediumorchid : '#ba55d3', + mediumpurple : '#9370db', + mediumseagreen : '#3cb371', + mediumslateblue : '#7b68ee', + mediumspringgreen : '#00fa9a', + mediumturquoise : '#48d1cc', + mediumvioletred : '#c71585', + midnightblue : '#191970', + mintcream : '#f5fffa', + mistyrose : '#ffe4e1', + moccasin : '#ffe4b5', + navajowhite : '#ffdead', + navy : '#000080', + oldlace : '#fdf5e6', + olive : '#808000', + olivedrab : '#6b8e23', + orange : '#ffa500', + orangered : '#ff4500', + orchid : '#da70d6', + palegoldenrod : '#eee8aa', + palegreen : '#98fb98', + paleturquoise : '#afeeee', + palevioletred : '#db7093', + papayawhip : '#ffefd5', + peachpuff : '#ffdab9', + peru : '#cd853f', + pink : '#ffc0cb', + plum : '#dda0dd', + powderblue : '#b0e0e6', + purple : '#800080', + rebeccapurple : '#663399', + red : '#ff0000', + rosybrown : '#bc8f8f', + royalblue : '#4169e1', + saddlebrown : '#8b4513', + salmon : '#fa8072', + sandybrown : '#f4a460', + seagreen : '#2e8b57', + seashell : '#fff5ee', + sienna : '#a0522d', + silver : '#c0c0c0', + skyblue : '#87ceeb', + slateblue : '#6a5acd', + slategray : '#708090', + slategrey : '#708090', + snow : '#fffafa', + springgreen : '#00ff7f', + steelblue : '#4682b4', + tan : '#d2b48c', + teal : '#008080', + thistle : '#d8bfd8', + tomato : '#ff6347', + turquoise : '#40e0d0', + violet : '#ee82ee', + wheat : '#f5deb3', + white : '#ffffff', + whitesmoke : '#f5f5f5', + yellow : '#ffff00', + yellowgreen : '#9acd32', +} + function hsvToRgb({ h, s, v }: ColorHSV): ColorRGB { const i = Math.floor(h * 6); const f = h * 6 - i; @@ -72,18 +234,30 @@ function rgbToHsv({ r, g, b }: ColorRGB): ColorHSV { } // Hacky way to get RGB values FOR SURE! -function nameToRGB(name: string): ColorRGB { +const nameToRGB = function self(name: string): ColorRGB { + if ( namedColors[name] ) { + return parseColor(namedColors[name]); + } + // Create fake div - const fakeDiv = document.createElement("div"); - fakeDiv.style.color = name; - document.body.appendChild(fakeDiv); + self.fakeDiv = self.fakeDiv || (() => { + const tmp = document.createElement("div"); + Object.assign(tmp.style, { + position: 'fixed'; + height: 0; + width: 0; + top: -99; + left: -99; + }); + document.body.appendChild(tmp); + return tmp; + })(); + + self.fakeDiv.style.color = name; // Get color of div - const cs = window.getComputedStyle(fakeDiv); - const pv = cs.getPropertyValue("color"); - - // Remove div after obtaining desired color value - document.body.removeChild(fakeDiv); + const cs = window.getComputedStyle(self.fakeDiv); + const pv = cs.color; //TODO slow return parseColor(pv); } @@ -92,10 +266,10 @@ function nameToRGB(name: string): ColorRGB { // https://stackoverflow.com/questions/11068240/what-is-the-most-efficient-way-to-parse-a-css-color-in-javascript function parseColor(input: string): ColorRGB { // Hex format - if (input.substr(0, 1) === "#") { + if (input[0] === "#") { const collen = (input.length - 1) / 3; const fact = [17, 1, 0.062272][collen - 1]; - return { + return { //TODO refactor with substring r: Math.round(parseInt(input.substr(1, collen), 16) * fact) / 256, g: Math.round(parseInt(input.substr(1 + collen, collen), 16) * fact) / 256, diff --git a/src/userscript.ts b/src/userscript.ts index 43a1de8..96ea902 100644 --- a/src/userscript.ts +++ b/src/userscript.ts @@ -1,10 +1,10 @@ import { darken, ColorFmt, lighten } from "./darkmode"; import searchBox from "./search"; -import { findParent } from "./utils"; +import { nextAnimationFrame, findParent } from "./utils"; -export default function userscript(root: HTMLElement, docname: string): void { +export default async function userscript(root: HTMLElement, docname: string): void { root.querySelectorAll(".mw-editsection").forEach((editLink) => { - editLink.parentElement.removeChild(editLink); + window.requestAnimationFrame(() => editLink.remove()) }); // Darken bgcolor @@ -37,6 +37,7 @@ export default function userscript(root: HTMLElement, docname: string): void { }); // Remove fixed widths + await nextAnimationFrame(); root.querySelectorAll("table[width]").forEach((td) => { td.setAttribute("width", "100%"); }); @@ -47,16 +48,18 @@ export default function userscript(root: HTMLElement, docname: string): void { }); // 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); - }); + const tmpFloatRows = Array.from(root.querySelectorAll("table .floatright > a > img")) + .map((img) => { + return findParent(img, (el) => el instanceof HTMLTableRowElement); + }) + await nextAnimationFrame(); + tmpFloatRows.forEach((row) => { + const td = document.createElement("td"); + row.appendChild(td); + }); // Group headers and content so stickies don't overlap - root.querySelectorAll("h3,h2").forEach((h3) => { + root.querySelectorAll("h3,h2").forEach((h3) => { //NOTE slow const parent = h3.parentNode; const div = document.createElement("div"); parent.insertBefore(div, h3); @@ -71,17 +74,28 @@ export default function userscript(root: HTMLElement, docname: string): void { }); // 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.innerText; - } - }); + const tmpHeaders = Array.from(root.querySelectorAll(".mw-headline")) + .map((span) => { + // Find nearest container + const container = findParent(span, (el) => + el.classList.contains("mw-headline-cont") + ); + if (container) { + return [container, span, span.id, span.textContent]; //NOTE slow + } else { + return null; + } + }) + .filter((e) => e !== null); + await nextAnimationFrame(); + for (const [container, span, spanId, spanInnerText] of tmpHeaders) { + container.id = spanId; + span.id += "-span"; + } + await nextAnimationFrame(); + for (const [container, span, spanId, spanInnerText] of tmpHeaders) { + container.dataset.name = spanInnerText; + } // Tell user that better chemistry is loading const postbody = root; @@ -94,32 +108,35 @@ export default function userscript(root: HTMLElement, docname: string): void { `; postbody.insertBefore(statusMessage, postbody.firstChild); - function betterChemistry() { + async function betterChemistry() { // 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 - document - .querySelectorAll( - "table.wikitable > tbody > tr:not(:first-child) > td:nth-child(2), .tooltiptext" - ) - .forEach((td) => { + const tmpTooltiptext = Array.from(document.querySelectorAll( + "table.wikitable > tbody > tr:not(:first-child) > td:nth-child(2), .tooltiptext" + )) + .map((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) => { + Array.from(td.childNodes).forEach((el) => { //TODO really slow if (el instanceof HTMLParagraphElement) { tmp.append(...el.childNodes); } else { tmp.append(el); } }); - td.parentNode.replaceChild(tmp, td); + return [td, td.parentNode, tmp]; }); + await nextAnimationFrame(); + for (const [td, parent, newTD] of tmpTooltiptext) { + parent.replaceChild(newTD, td); + } // Enrich "x part" with checkboxes and parts - Array.from(document.querySelectorAll("td")) - .filter((el) => el.innerText.indexOf(" part") >= 0) - .forEach((el) => { - el.innerHTML = el.innerHTML.replace( + const tmpParts = Array.from(document.querySelectorAll("td")) + .filter((el) => el.textContent.indexOf(" part") >= 0) + .map((el) => { + const newInnerHTML = el.innerHTML.replace( //TODO slow /((\d+)\s+(?:parts?|units?))(.*?(?:<\/a>|\n|$))/gi, (match, ...m) => `