2020-06-17 13:16:54 +00:00
|
|
|
// @ts-expect-error: Asset imports are handled by parcel
|
2020-06-17 09:43:21 +00:00
|
|
|
import speen from "~/assets/images/speen.svg";
|
|
|
|
import { getPageHTML } from "./wiki";
|
|
|
|
import userscript from "./userscript";
|
|
|
|
|
2020-06-19 18:26:43 +00:00
|
|
|
// @ts-expect-error: Parcel image import
|
|
|
|
import unknown from "~/assets/images/tab-icons/unknown.svg";
|
|
|
|
|
2020-06-17 09:43:21 +00:00
|
|
|
function initWaiting(elem: HTMLElement) {
|
|
|
|
// Add spinner
|
|
|
|
const spinnerContainer = document.createElement("div");
|
|
|
|
spinnerContainer.className = "speen";
|
|
|
|
const spinnerImg = document.createElement("img");
|
|
|
|
spinnerImg.src = speen;
|
|
|
|
spinnerContainer.appendChild(spinnerImg);
|
|
|
|
const spinnerText = document.createElement("p");
|
|
|
|
spinnerText.appendChild(
|
|
|
|
document.createTextNode("You start skimming through the manual...")
|
|
|
|
);
|
|
|
|
spinnerContainer.appendChild(spinnerText);
|
|
|
|
elem.appendChild(spinnerContainer);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function loadPage(page: string, elem: HTMLElement) {
|
2020-06-17 09:59:00 +00:00
|
|
|
// Fetch page content
|
2020-06-17 13:16:54 +00:00
|
|
|
console.log(`${page}: fetching`);
|
2020-06-17 09:43:21 +00:00
|
|
|
let html = await getPageHTML(page);
|
2020-06-17 09:59:00 +00:00
|
|
|
|
2020-06-17 09:43:21 +00:00
|
|
|
// Convert relative links to absolute
|
|
|
|
html = html.replace(/"\/wiki/gi, '"//tgstation13.org/wiki');
|
2020-06-17 09:59:00 +00:00
|
|
|
|
|
|
|
// Set as HTML content and run HTML manipulations on it
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
elem.innerHTML = html;
|
2020-06-17 13:16:54 +00:00
|
|
|
console.log(`${page}: processing`);
|
2020-06-17 09:59:00 +00:00
|
|
|
userscript(elem, page);
|
2020-06-17 13:16:54 +00:00
|
|
|
console.log(`${page}: userscript applied`);
|
2020-06-17 09:59:00 +00:00
|
|
|
elem.classList.remove("waiting");
|
|
|
|
});
|
2020-06-17 09:43:21 +00:00
|
|
|
}
|
|
|
|
|
2020-06-19 18:26:43 +00:00
|
|
|
type TabElements = {
|
|
|
|
tabListItem: HTMLElement;
|
|
|
|
tabContentItem: HTMLElement;
|
|
|
|
};
|
|
|
|
|
|
|
|
interface Section {
|
|
|
|
name: string;
|
|
|
|
element: HTMLElement;
|
|
|
|
tabs: Record<string, TabElements>;
|
|
|
|
}
|
2020-06-17 09:43:21 +00:00
|
|
|
|
|
|
|
export default class TabManager {
|
2020-06-19 18:26:43 +00:00
|
|
|
sectionListContainer: HTMLElement;
|
|
|
|
|
2020-06-17 09:43:21 +00:00
|
|
|
tabListContainer: HTMLElement;
|
2020-06-17 13:16:54 +00:00
|
|
|
|
2020-06-17 09:43:21 +00:00
|
|
|
tabContentContainer: HTMLElement;
|
2020-06-17 13:16:54 +00:00
|
|
|
|
2020-06-19 18:26:43 +00:00
|
|
|
sections: Record<string, Section> = {};
|
2020-06-17 09:43:21 +00:00
|
|
|
|
2020-06-19 18:26:43 +00:00
|
|
|
constructor(
|
|
|
|
sectionlist: HTMLElement,
|
|
|
|
tablist: HTMLElement,
|
|
|
|
tabcontent: HTMLElement
|
|
|
|
) {
|
|
|
|
this.sectionListContainer = sectionlist;
|
2020-06-17 09:43:21 +00:00
|
|
|
this.tabListContainer = tablist;
|
|
|
|
this.tabContentContainer = tabcontent;
|
|
|
|
}
|
|
|
|
|
2020-06-19 18:26:43 +00:00
|
|
|
/**
|
|
|
|
* Create section and add it to the section list
|
|
|
|
* @param name Section name
|
|
|
|
*/
|
|
|
|
createSection(name: string): void {
|
|
|
|
// Create section element
|
|
|
|
const sectionItem = document.createElement("div");
|
|
|
|
sectionItem.className = "section";
|
|
|
|
sectionItem.dataset.section = name;
|
|
|
|
sectionItem.appendChild(document.createTextNode(name));
|
|
|
|
sectionItem.addEventListener("click", () => {
|
|
|
|
if (sectionItem.classList.contains("active")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.showSection(name);
|
|
|
|
});
|
|
|
|
this.sectionListContainer.appendChild(sectionItem);
|
|
|
|
|
|
|
|
this.sections[name] = { name, element: sectionItem, tabs: {} };
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Show tabs of a specific section
|
|
|
|
* @param name Section name
|
|
|
|
*/
|
|
|
|
showSection(name: string): void {
|
|
|
|
const active = this.sectionListContainer.querySelector<HTMLElement>(
|
|
|
|
".active"
|
|
|
|
);
|
|
|
|
if (active) {
|
|
|
|
// De-activate current section
|
|
|
|
active.classList.remove("active");
|
|
|
|
|
|
|
|
// Hide all tabs
|
|
|
|
this.tabListContainer
|
|
|
|
.querySelectorAll(`div[data-section=${active.dataset.section}]`)
|
|
|
|
.forEach((tab) => tab.classList.add("hidden"));
|
|
|
|
}
|
|
|
|
// Set section as active
|
|
|
|
this.sections[name].element.classList.add("active");
|
|
|
|
|
|
|
|
// Show all tabs of that section
|
|
|
|
this.tabListContainer
|
|
|
|
.querySelectorAll(`div[data-section=${name}]`)
|
|
|
|
.forEach((tab) => tab.classList.remove("hidden"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open tab page and add it to the tab list
|
|
|
|
* @param section Section to add the tab button to
|
|
|
|
* @param page Page name
|
|
|
|
* @param icon Icon to show
|
|
|
|
* @param setActive Also set the tab as active
|
|
|
|
*/
|
|
|
|
openTab(
|
|
|
|
section: string,
|
|
|
|
page: string,
|
|
|
|
options: {
|
|
|
|
icon?: string;
|
|
|
|
active?: boolean;
|
|
|
|
text?: string;
|
|
|
|
}
|
|
|
|
): void {
|
2020-06-17 09:43:21 +00:00
|
|
|
// Create tab list item
|
|
|
|
const tabListItem = document.createElement("div");
|
|
|
|
tabListItem.className = "tab";
|
2020-06-19 18:26:43 +00:00
|
|
|
tabListItem.dataset.section = section;
|
2020-06-17 09:43:21 +00:00
|
|
|
tabListItem.dataset.tab = page;
|
|
|
|
tabListItem.addEventListener("click", () => {
|
|
|
|
if (tabListItem.classList.contains("active")) {
|
|
|
|
return;
|
|
|
|
}
|
2020-06-19 18:26:43 +00:00
|
|
|
this.setActive(section, page);
|
2020-06-17 09:43:21 +00:00
|
|
|
});
|
2020-06-19 18:26:43 +00:00
|
|
|
const iconElement = document.createElement("img");
|
|
|
|
iconElement.src = options.icon || unknown;
|
|
|
|
tabListItem.title = page.replace(/_/gi, " ");
|
|
|
|
tabListItem.appendChild(iconElement);
|
|
|
|
const shortTitle =
|
|
|
|
options.text || page.substr(page.lastIndexOf("_") + 1, 4);
|
|
|
|
tabListItem.appendChild(document.createTextNode(shortTitle));
|
2020-06-17 09:43:21 +00:00
|
|
|
this.tabListContainer.appendChild(tabListItem);
|
|
|
|
|
|
|
|
// Create tab content container
|
|
|
|
const tabContentItem = document.createElement("div");
|
|
|
|
tabContentItem.className = "page waiting";
|
|
|
|
tabContentItem.dataset.tab = page;
|
|
|
|
initWaiting(tabContentItem);
|
|
|
|
|
|
|
|
// Start loading page for new tab
|
|
|
|
loadPage(page, tabContentItem);
|
|
|
|
|
2020-06-19 18:26:43 +00:00
|
|
|
this.tabContentContainer.appendChild(tabContentItem);
|
|
|
|
|
2020-06-17 09:43:21 +00:00
|
|
|
// Create tab entry
|
2020-06-19 18:26:43 +00:00
|
|
|
this.sections[section].tabs[page] = { tabListItem, tabContentItem };
|
|
|
|
|
|
|
|
// Hide tab if section is hidden
|
|
|
|
if (!this.sections[section].element.classList.contains("active")) {
|
|
|
|
tabListItem.classList.add("hidden");
|
|
|
|
}
|
2020-06-17 09:43:21 +00:00
|
|
|
|
|
|
|
// If asked for, set it to active
|
2020-06-19 18:26:43 +00:00
|
|
|
if (options.active) {
|
|
|
|
this.setActive(section, page);
|
2020-06-17 09:43:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-19 18:26:43 +00:00
|
|
|
/**
|
|
|
|
* Set a specific page to be the active/visible one
|
|
|
|
* @param section Section name
|
|
|
|
* @param page Page name
|
|
|
|
*/
|
|
|
|
setActive(section: string, page: string): void {
|
2020-06-17 09:43:21 +00:00
|
|
|
// Make sure tab exists (why wouldn't it?!)
|
2020-06-19 18:26:43 +00:00
|
|
|
if (!(section in this.sections)) {
|
|
|
|
throw new Error("section not found");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(page in this.sections[section].tabs)) {
|
2020-06-17 09:43:21 +00:00
|
|
|
throw new Error("tab not found");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deactivate current active tab
|
2020-06-19 18:26:43 +00:00
|
|
|
this.sectionListContainer
|
|
|
|
.querySelectorAll(".active")
|
|
|
|
.forEach((it) => it.classList.remove("active"));
|
2020-06-17 09:43:21 +00:00
|
|
|
this.tabListContainer
|
|
|
|
.querySelectorAll(".active")
|
|
|
|
.forEach((it) => it.classList.remove("active"));
|
|
|
|
this.tabContentContainer
|
|
|
|
.querySelectorAll(".active")
|
|
|
|
.forEach((it) => it.classList.remove("active"));
|
|
|
|
|
2020-06-19 18:26:43 +00:00
|
|
|
// If section is not shown, show it!
|
|
|
|
if (!this.sections[section].element.classList.contains("active")) {
|
|
|
|
this.showSection(section);
|
|
|
|
}
|
|
|
|
|
2020-06-17 09:43:21 +00:00
|
|
|
// Activate new tab
|
2020-06-19 18:26:43 +00:00
|
|
|
const { tabListItem, tabContentItem } = this.sections[section].tabs[page];
|
|
|
|
this.sections[section].element.classList.add("active");
|
2020-06-17 09:43:21 +00:00
|
|
|
tabListItem.classList.add("active");
|
|
|
|
tabContentItem.classList.add("active");
|
|
|
|
}
|
|
|
|
}
|