1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2024-12-22 09:00:10 +00:00
jfa-go/ts/setup.ts
Harvey Tindall 2057823b7a
setup: flex-ify, light/dark, keep page position on reload
got rid of a bunch of m[l/r/x/y]-x tailwind classes and used more
flex-[row/col] gap-2's. UI should be more consistent in general, and
with the admin UI.

The page you were on is actually read from the URL on reload, however
does not keep settings (implemented just for ease of UI editing,
really).

`missing-colors.js` preprocessor script now applies dark prefixes for
<section>s, but like with cards, does not apply a default ~neutral to
those without, so that <section class=""> looks different to <section
class="~neutral">.

Light/dark selector added to setup too, and the actual mode given to the
browser through CSS `color-scheme` is correct, meaning things like textareas, checkboxes and
controls are now colored according to the theme.
2024-08-21 18:31:54 +01:00

669 lines
29 KiB
TypeScript

import { _get, _post, toggleLoader, notificationBox } from "./modules/common.js";
import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
import { ThemeManager } from "./modules/theme.js";
interface sWindow extends Window {
messages: {};
}
declare var window: sWindow;
window.URLBase = "";
const theme = new ThemeManager(document.getElementById("button-theme"));
window.notifications = new notificationBox(document.getElementById('notification-box') as HTMLDivElement, 5);
const get = (id: string): HTMLElement => document.getElementById(id);
const text = (id: string, val: string) => { document.getElementById(id).textContent = val; };
const html = (id: string, val: string) => { document.getElementById(id).innerHTML = val; };
interface boolEvent extends Event {
detail: boolean;
}
class Input {
private _el: HTMLInputElement;
get value(): string { return ""+this._el.value; }
set value(v: string) { this._el.value = v; }
// Nothing depends on input, but we add an empty broadcast function so we can just loop over all settings to fix dependents on start.
broadcast = () => {}
constructor(el: HTMLElement, placeholder?: any, value?: any, depends?: string, dependsTrue?: boolean, section?: string) {
this._el = el as HTMLInputElement;
if (placeholder) { this._el.placeholder = placeholder; }
if (value) { this.value = value; }
if (depends) {
document.addEventListener(`settings-${section}-${depends}`, (event: boolEvent) => {
let el = this._el as HTMLElement;
if (el.parentElement.tagName == "LABEL") { el = el.parentElement; }
if (event.detail !== dependsTrue) {
el.classList.add("unfocused");
} else {
el.classList.remove("unfocused");
}
});
}
}
}
class Checkbox {
private _el: HTMLInputElement;
private _hideEl: HTMLElement;
get value(): string { return this._el.checked ? "true" : "false"; }
set value(v: string) { this._el.checked = (v == "true") ? true : false; }
private _section: string;
private _setting: string;
broadcast = () => {
let state = this._el.checked;
if (this._hideEl.classList.contains("unfocused")) {
state = false;
}
if (this._section && this._setting) {
const ev = new CustomEvent(`settings-${this._section}-${this._setting}`, { "detail": state })
document.dispatchEvent(ev);
}
}
set onchange(f: () => void) {
this._el.addEventListener("change", f);
}
constructor(el: HTMLElement, depends?: string, dependsTrue?: boolean, section?: string, setting?: string) {
this._el = el as HTMLInputElement;
this._hideEl = this._el as HTMLElement;
if (this._hideEl.parentElement.tagName == "LABEL") {
this._hideEl = this._hideEl.parentElement;
} else if (this._hideEl.parentElement.classList.contains("switch")) {
if (this._hideEl.parentElement.parentElement.tagName == "LABEL") {
this._hideEl = this._hideEl.parentElement.parentElement;
} else {
this._hideEl = this._hideEl.parentElement;
}
}
if (section && setting) {
this._section = section;
this._setting = setting;
this._el.onchange = this.broadcast;
}
if (depends) {
document.addEventListener(`settings-${section}-${depends}`, (event: boolEvent) => {
if (event.detail !== dependsTrue) {
this._hideEl.classList.add("unfocused");
this.broadcast();
} else {
this._hideEl.classList.remove("unfocused");
this.broadcast();
}
});
}
/* if (this._el.hasAttribute("checked")) {
this._el.checked = true;
} else {
this._el.checked = false;
} */
this.broadcast();
}
}
class BoolRadios {
private _els: NodeListOf<HTMLInputElement>;
get value(): string { return this._els[0].checked ? "true" : "false" }
set value(v: string) {
const bool = (v == "true") ? true : false;
this._els[0].checked = bool;
this._els[1].checked = !bool;
}
private _section: string;
private _setting: string;
broadcast = () => {
if (this._section && this._setting) {
const ev = new CustomEvent(`settings-${this._section}-${this._setting}`, { "detail": this._els[0].checked })
document.dispatchEvent(ev);
}
}
constructor(name: string, depends?: string, dependsTrue?: boolean, section?: string, setting?: string) {
this._els = document.getElementsByName(name) as NodeListOf<HTMLInputElement>;
if (section && setting) {
this._section = section;
this._setting = setting;
this._els[0].onchange = this.broadcast;
this._els[1].onchange = this.broadcast;
}
if (depends) {
document.addEventListener(`settings-${section}-${depends}`, (event: boolEvent) => {
if (event.detail !== dependsTrue) {
if (this._els[0].parentElement.tagName == "LABEL") {
this._els[0].parentElement.classList.add("unfocused");
}
if (this._els[1].parentElement.tagName == "LABEL") {
this._els[1].parentElement.classList.add("unfocused");
}
} else {
if (this._els[0].parentElement.tagName == "LABEL") {
this._els[0].parentElement.classList.remove("unfocused");
}
if (this._els[1].parentElement.tagName == "LABEL") {
this._els[1].parentElement.classList.remove("unfocused");
}
}
});
}
}
}
// class Radios {
// private _el: HTMLInputElement;
// get value(): string { return this._el.value; }
// set value(v: string) { this._el.value = v; }
// constructor(name: string, depends?: string, dependsTrue?: boolean, section?: string) {
// this._el = document.getElementsByName(name)[0] as HTMLInputElement;
// if (depends) {
// document.addEventListener(`settings-${section}-${depends}`, (event: boolEvent) => {
// let el = this._el as HTMLElement;
// if (el.parentElement.tagName == "LABEL") { el = el.parentElement; }
// if (event.detail !== dependsTrue) {
// el.classList.add("unfocused");
// } else {
// el.classList.remove("unfocused");
// }
// });
// }
// }
// }
class Select {
private _el: HTMLSelectElement;
get value(): string { return this._el.value; }
set value(v: string) { this._el.value = v; }
add = (val: string, label: string) => {
const item = document.createElement("option") as HTMLOptionElement;
item.value = val;
item.textContent = label;
this._el.appendChild(item);
}
set onchange(f: () => void) {
this._el.addEventListener("change", f);
}
private _section: string;
private _setting: string;
broadcast = () => {
if (this._section && this._setting) {
const ev = new CustomEvent(`settings-${this._section}-${this._setting}`, { "detail": this.value ? true : false })
document.dispatchEvent(ev);
}
}
constructor(el: HTMLElement, depends?: string, dependsTrue?: boolean, section?: string, setting?: string) {
this._el = el as HTMLSelectElement;
if (section && setting) {
this._section = section;
this._setting = setting;
this._el.addEventListener("change", this.broadcast);
}
if (depends) {
document.addEventListener(`settings-${section}-${depends}`, (event: boolEvent) => {
let el = this._el as HTMLElement;
while (el.tagName != "LABEL") {
el = el.parentElement;
}
if (event.detail !== dependsTrue) {
el.classList.add("unfocused");
} else {
el.classList.remove("unfocused");
}
});
}
}
}
class LangSelect extends Select {
constructor(page: string, el: HTMLElement, depends?: string, dependsTrue?: boolean, section?: string, setting?: string) {
super(el, depends, dependsTrue, section, setting);
_get("/lang/" + page, null, (req: XMLHttpRequest) => {
if (req.readyState == 4 && req.status == 200) {
for (let code in req.response) {
this.add(code, req.response[code]);
}
this.value = "en-us";
}
}, true);
}
}
const replaceLink = (elName: string, sect: string, name: string, url: string, text: string) => html(elName, window.lang.var(sect, name, `<a class="underline" target="_blank" href="${url}">${text}</a>`));
window.lang = new lang(window.langFile as LangFile);
replaceLink("language-description", "language", "description", "https://weblate.jfa-go.com", "Weblate");
replaceLink("email-description", "email", "description", "https://mailgun.com", "Mailgun");
replaceLink("email-dateformat-notice", "email", "dateFormatNotice", "https://strftime.timpetricola.com/", "strftime.timpetricola.com");
replaceLink("updates-description", "updates", "description", "https://builds.hrfee.dev/view/hrfee/jfa-go", "buildrone");
replaceLink("messages-description", "messages", "description", "https://wiki.jfa-go.com", "Wiki");
replaceLink("password_resets-more-info", "passwordResets", "moreInfo", "https://wiki.jfa-go.com/docs/pwr/", "wiki.jfa-go.com");
replaceLink("ombi-stability-warning", "ombi", "stabilityWarning", "https://wiki.jfa-go.com/docs/ombi/", "wiki.jfa-go.com");
const settings = {
"jellyfin": {
"type": new Select(get("jellyfin-type")),
"server": new Input(get("jellyfin-server")),
"public_server": new Input(get("jellyfin-public_server")),
"username": new Input(get("jellyfin-username")),
"password": new Input(get("jellyfin-password")),
"substitute_jellyfin_strings": new Input(get("jellyfin-substitute_jellyfin_strings"))
},
"updates": {
"enabled": new Checkbox(get("updates-enabled"), "", false, "updates", "enabled"),
"channel": new Select(get("updates-channel"), "enabled", true, "updates")
},
"ui": {
"host": new Input(get("ui-host")),
"port": new Input(get("ui-port")),
"url_base": new Input(get("ui-url_base")),
"jfa_url": new Input(get("ui-jfa_url")),
"theme": new Select(get("ui-theme")),
"language-form": new LangSelect("form", get("ui-language-form")),
"language-admin": new LangSelect("admin", get("ui-language-admin")),
"jellyfin_login": new BoolRadios("ui-jellyfin_login", "", false, "ui", "jellyfin_login"),
"admin_only": new Checkbox(get("ui-admin_only"), "jellyfin_login", true, "ui"),
"allow_all": new Checkbox(get("ui-allow_all"), "jellyfin_login", true, "ui"),
"username": new Input(get("ui-username"), "", "", "jellyfin_login", false, "ui"),
"password": new Input(get("ui-password"), "", "", "jellyfin_login", false, "ui"),
"email": new Input(get("ui-email"), "", "", "jellyfin_login", false, "ui"),
"contact_message": new Input(get("ui-contact_message"), window.messages["ui"]["contact_message"]),
"help_message": new Input(get("ui-help_message"), window.messages["ui"]["help_message"]),
"success_message": new Input(get("ui-success_message"), window.messages["ui"]["success_message"])
},
"password_validation": {
"enabled": new Checkbox(get("password_validation-enabled"), "", false, "password_validation", "enabled"),
"min_length": new Input(get("password_validation-min_length"), "", 8, "enabled", true, "password_validation"),
"upper": new Input(get("password_validation-upper"), "", 1, "enabled", true, "password_validation"),
"lower": new Input(get("password_validation-lower"), "", 0, "enabled", true, "password_validation"),
"number": new Input(get("password_validation-number"), "", 1, "enabled", true, "password_validation"),
"special": new Input(get("password_validation-special"), "", 0, "enabled", true, "password_validation")
},
"messages": {
"enabled": new Checkbox(get("messages-enabled"), "", false, "messages", "enabled"),
"use_24h": new BoolRadios("email-24h", "enabled", true, "messages"),
"date_format": new Input(get("email-date_format"), "", "%d/%m/%y", "enabled", true, "messages"),
"message": new Input(get("email-message"), window.messages["messages"]["message"], "", "enabled", true, "messages")
},
"email": {
"language": new LangSelect("email", get("email-language")),
"no_username": new Checkbox(get("email-no_username"), "method", true, "email"),
"method": new Select(get("email-method"), "", false, "email", "method"),
"address": new Input(get("email-address"), "jellyfin@jellyf.in", "", "method", true, "email"),
"from": new Input(get("email-from"), "", "Jellyfin", "method", true, "email")
},
"password_resets": {
"enabled": new Checkbox(get("password_resets-enabled"), "", false, "password_resets", "enabled"),
"watch_directory": new Input(get("password_resets-watch_directory"), "", "", "enabled", true, "password_resets"),
"subject": new Input(get("password_resets-subject"), "", "", "enabled", true, "password_resets"),
"link_reset": new Checkbox(get("password_resets-link_reset"), "enabled", true, "password_resets", "link_reset"),
"language": new LangSelect("pwr", get("password_resets-language"), "link_reset", true, "password_resets", "language"),
"set_password": new Checkbox(get("password_resets-set_password"), "link_reset", true, "password_resets", "set_password")
},
"notifications": {
"enabled": new Checkbox(get("notifications-enabled"))
},
"user_page": {
"enabled": new Checkbox(get("userpage-enabled"))
},
"welcome_email": {
"enabled": new Checkbox(get("welcome_email-enabled"), "", false, "welcome_email", "enabled"),
"subject": new Input(get("welcome_email-subject"), "", "", "enabled", true, "welcome_email")
},
"invite_emails": {
"enabled": new Checkbox(get("invite_emails-enabled"), "", false, "invite_emails", "enabled"),
"subject": new Input(get("invite_emails-subject"), "", "", "enabled", true, "invite_emails"),
},
"mailgun": {
"api_url": new Input(get("mailgun-api_url")),
"api_key": new Input(get("mailgun-api_key"))
},
"smtp": {
"username": new Input(get("smtp-username")),
"encryption": new Select(get("smtp-encryption")),
"server": new Input(get("smtp-server")),
"port": new Input(get("smtp-port")),
"password": new Input(get("smtp-password"))
},
"ombi": {
"enabled": new Checkbox(get("ombi-enabled"), "", false, "ombi", "enabled"),
"server": new Input(get("ombi-server"), "", "", "enabled", true, "ombi"),
"api_key": new Input(get("ombi-api_key"), "", "", "enabled", true, "ombi")
},
"jellyseerr": {
"enabled": new Checkbox(get("jellyseerr-enabled"), "", false, "jellyseerr", "enabled"),
"server": new Input(get("jellyseerr-server"), "", "", "enabled", true, "jellyseerr"),
"api_key": new Input(get("jellyseerr-api_key"), "", "", "enabled", true, "jellyseerr"),
"import_existing": new Checkbox(get("jellyseerr-import_existing"), "enabled", true, "jellyseerr", "import_existing")
},
"advanced": {
"tls": new Checkbox(get("advanced-tls"), "", false, "advanced", "tls"),
"tls_port": new Input(get("advanced-tls_port"), "", "", "tls", true, "advanced"),
"tls_cert": new Input(get("advanced-tls_cert"), "", "", "tls", true, "advanced"),
"tls_key": new Input(get("advanced-tls_key"), "", "", "tls", true, "advanced"),
"proxy": new Checkbox(get("advanced-proxy"), "", false, "advanced", "proxy"),
"proxy_protocol": new Select(get("advanced-proxy_protocol"), "proxy", true, "advanced"),
"proxy_address": new Input(get("advanced-proxy_address"), "", "", "proxy", true, "advanced"),
"proxy_user": new Input(get("advanced-proxy_user"), "", "", "proxy", true, "advanced"),
"proxy_password": new Input(get("advanced-proxy_password"), "", "", "proxy", true, "advanced")
}
};
const checkTheme = () => {
if (settings["ui"]["theme"].value.includes("Dark")) {
document.documentElement.classList.add("dark-theme");
document.documentElement.classList.remove("light-theme");
} else {
document.documentElement.classList.add("light-theme");
document.documentElement.classList.remove("dark-theme");
}
};
settings["ui"]["theme"].onchange = checkTheme;
checkTheme();
const restartButton = document.getElementById("restart") as HTMLSpanElement;
const serialize = () => {
toggleLoader(restartButton);
let config = {};
for (let section in settings) {
config[section] = {};
for (let setting in settings[section]) {
if (settings[section][setting].value) {
config[section][setting] = settings[section][setting].value;
}
}
}
config["restart-program"] = true;
_post("/config", config, (req: XMLHttpRequest) => {
if (req.readyState == 4) {
toggleLoader(restartButton);
if (req.status == 500) {
if (req.response == null) {
const old = restartButton.textContent;
restartButton.classList.add("~critical");
restartButton.classList.remove("~urge");
restartButton.textContent = window.lang.strings("errorUnknown");
setTimeout(() => {
restartButton.classList.add("~urge");
restartButton.classList.remove("~critical");
restartButton.textContent = old;
}, 5000);
return;
}
if (req.response["error"] as string) {
const old = restartButton.textContent;
restartButton.classList.add("~critical");
restartButton.classList.remove("~urge");
restartButton.textContent = req.response["error"];
setTimeout(() => {
restartButton.classList.add("~urge");
restartButton.classList.remove("~critical");
restartButton.textContent = old;
}, 5000);
return;
}
}
restartButton.parentElement.querySelector("span.back").classList.add("unfocused");
restartButton.classList.add("unfocused");
const refresh = document.getElementById("refresh") as HTMLSpanElement;
refresh.classList.remove("unfocused");
refresh.onclick = () => {
let host = window.location.href.split("#")[0].split("?")[0] + settings["ui"]["url_base"].value;
window.location.href = host;
};
}
}, true, (req: XMLHttpRequest) => {
if (req.status == 0) {
window.notifications.customError("connectionError", window.lang.strings("errorConnectionRefused"));
}
});
}
restartButton.onclick = serialize;
const relatedToEmail = Array.from(document.getElementsByClassName("related-to-email"));
const emailMethodChange = () => {
const val = settings["email"]["method"].value;
const smtp = document.getElementById("email-smtp");
const mailgun = document.getElementById("email-mailgun");
const emailSect = document.getElementById("email-sect");
const enabled = settings["messages"]["enabled"].value;
if (enabled == "false") {
for (let el of relatedToEmail) {
el.classList.add("hidden");
}
emailSect.classList.add("unfocused");
return;
} else {
for (let el of relatedToEmail) {
el.classList.remove("hidden");
}
emailSect.classList.remove("unfocused");
}
if (val == "smtp") {
smtp.classList.remove("unfocused");
mailgun.classList.add("unfocused");
} else if (val == "mailgun") {
mailgun.classList.remove("unfocused");
smtp.classList.add("unfocused");
for (let el of relatedToEmail) {
el.classList.remove("hidden");
}
} else {
mailgun.classList.add("unfocused");
smtp.classList.add("unfocused");
}
};
settings["email"]["method"].onchange = emailMethodChange;
settings["messages"]["enabled"].onchange = emailMethodChange;
emailMethodChange();
const getParentCard = (el: HTMLElement): HTMLDivElement => {
let pEl = el.parentElement;
while (pEl.tagName != "html") {
if (pEl.classList.contains("card")) return pEl as HTMLDivElement;
pEl = pEl.parentElement;
}
return pEl as HTMLDivElement;
};
const jellyfinLoginAccessChange = () => {
const adminOnly = settings["ui"]["admin_only"].value == "true";
const allowAll = settings["ui"]["allow_all"].value == "true";
const adminOnlyEl = document.getElementById("ui-admin_only") as HTMLInputElement;
const allowAllEl = document.getElementById("ui-allow_all") as HTMLInputElement;
const nextButton = getParentCard(adminOnlyEl).querySelector("span.next") as HTMLSpanElement;
if (adminOnly && !allowAll) {
allowAllEl.disabled = true;
adminOnlyEl.disabled = false;
nextButton.removeAttribute("disabled");
} else if (!adminOnly && allowAll) {
adminOnlyEl.disabled = true;
allowAllEl.disabled = false;
nextButton.removeAttribute("disabled");
} else {
adminOnlyEl.disabled = false;
allowAllEl.disabled = false;
nextButton.setAttribute("disabled", "true")
}
};
settings["ui"]["admin_only"].onchange = jellyfinLoginAccessChange;
settings["ui"]["allow_all"].onchange = jellyfinLoginAccessChange;
jellyfinLoginAccessChange();
const embyHidePWR = () => {
const pwr = document.getElementById("password-resets");
const val = settings["jellyfin"]["type"].value;
if (val == "jellyfin") {
pwr.classList.remove("hidden");
} else if (val == "emby") {
pwr.classList.add("hidden");
}
}
settings["jellyfin"]["type"].onchange = embyHidePWR;
embyHidePWR();
(window as any).settings = settings;
for (let section in settings) {
for (let setting in settings[section]) {
settings[section][setting].broadcast();
}
}
const pageNames: string[][] = [];
(() => {
const pushState = window.history.pushState;
window.history.pushState = function (data: any, __: string, _: string | URL) {
pushState.apply(window.history, arguments);
let ev = { state: data as string } as PopStateEvent;
window.onpopstate(ev);
};
})();
window.onpopstate = (event: PopStateEvent) => {
console.log("CALLLLLLLL", event);
if (event.state === "welcome") {
cards[0].classList.remove("unfocused");
for (let i = 1; i < cards.length; i++) { cards[i].classList.add("unfocused"); }
return;
}
for (let i = 0; i < cards.length; i++) {
if (event.state === pageNames[i][0]) {
cards[i].classList.remove("unfocused");
} else {
cards[i].classList.add("unfocused");
}
}
};
const cards = Array.from(document.getElementsByClassName("page-container")[0].querySelectorAll(".card.sectioned")) as Array<HTMLDivElement>;
(window as any).cards = cards;
const changePageToIndex = (title: string, pageTitle: string, i: number) => {
cards[i].classList.remove("unfocused");
const urlParams = new URLSearchParams(window.location.search);
const lang = urlParams.get("lang");
let page = "/#" + title;
if (lang) { page += "?lang=" + lang; }
console.log("pushing", title, pageTitle, page);
window.history.pushState(title || "welcome", pageTitle, page);
};
const changePage = (title: string, pageTitle: string) => {
let found = false;
for (let i = 0; i < cards.length; i++) {
if (!found && pageNames[i][0] == title && !(cards[i].classList.contains("hidden"))) {
found = true;
changePageToIndex(title, pageTitle, i);
} else {
cards[i].classList.add("unfocused");
}
}
if (!found) {
changePageToIndex(title, pageTitle, 0);
}
window.scrollTo(0, 0);
};
(() => {
for (let i = 0; i < cards.length; i++) {
const card = cards[i];
const back = card.getElementsByClassName("back")[0] as HTMLSpanElement;
const next = card.getElementsByClassName("next")[0] as HTMLSpanElement;
const titleEl = cards[i].querySelector("span.heading") as HTMLElement;
let title = titleEl.textContent.replace("/", "_").replace(" ", "-");
if (titleEl.classList.contains("welcome")) {
title = "";
}
let pageTitle = titleEl.textContent + " - jfa-go";
pageNames.push([title, pageTitle]);
if (back) { back.addEventListener("click", () => {
for (let ind = cards.length - 1; ind >= 0; ind--) {
if (ind < i && !(cards[ind].classList.contains("hidden"))) {
changePage(pageNames[ind][0], pageNames[ind][1]);
break;
}
}
}); }
if (next) {
const func = () => {
if (next.hasAttribute("disabled")) return;
for (let ind = 0; ind < cards.length; ind++) {
if (ind > i && !(cards[ind].classList.contains("hidden"))) {
changePage(pageNames[ind][0], pageNames[ind][1]);
break;
}
}
};
next.addEventListener("click", func)
}
}
})();
(() => {
let initialLocation = window.location.hash.replace("#", "") || "welcome";
changePage(initialLocation, "Setup - jfa-go");
})();
// window.history.replaceState("welcome", "Setup - jfa-go",);
(() => {
const button = document.getElementById("jellyfin-test-connection") as HTMLSpanElement;
const ogText = button.textContent;
const nextButton = button.parentElement.querySelector("span.next") as HTMLSpanElement;
button.onclick = () => {
toggleLoader(button);
let send = {
"type": settings["jellyfin"]["type"].value,
"server": settings["jellyfin"]["server"].value,
"username": settings["jellyfin"]["username"].value,
"password": settings["jellyfin"]["password"].value,
"proxy": settings["advanced"]["proxy"].value == "true",
"proxy_protocol": settings["advanced"]["proxy_protocol"].value,
"proxy_address": settings["advanced"]["proxy_address"].value,
"proxy_user": settings["advanced"]["proxy_user"].value,
"proxy_password": settings["advanced"]["proxy_password"].value
};
_post("/jellyfin/test", send, (req: XMLHttpRequest) => {
if (req.readyState == 4) {
toggleLoader(button);
if (req.status != 200) {
nextButton.setAttribute("disabled", "");
button.classList.add("~critical");
button.classList.remove("~urge");
setTimeout(() => {
button.textContent = ogText;
button.classList.add("~urge");
button.classList.remove("~critical");
}, 5000);
const errorMsg = req.response["error"] as string;
if (!errorMsg) {
button.textContent = window.lang.strings("error");
} else {
button.textContent = window.lang.strings(errorMsg);
}
return;
}
nextButton.removeAttribute("disabled");
button.textContent = window.lang.strings("success");
button.classList.add("~positive");
button.classList.remove("~urge");
setTimeout(() => {
button.textContent = ogText;
button.classList.add("~urge");
button.classList.remove("~positive");
}, 5000);
}
}, true, (req: XMLHttpRequest) => {
if (req.status == 0) {
window.notifications.customError("connectionError", window.lang.strings("errorConnectionRefused"));
}
});
};
})();
loadLangSelector("setup");