You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
5.4 KiB
157 lines
5.4 KiB
"use strict";
|
|
/* eslint-env es2017, browser */
|
|
/* exported BASE_URL, _post */
|
|
|
|
function getBaseUrl() {
|
|
// If the base URL is `https://vaultwarden.example.com/base/path/admin/`,
|
|
// `window.location.href` should have one of the following forms:
|
|
//
|
|
// - `https://vaultwarden.example.com/base/path/admin`
|
|
// - `https://vaultwarden.example.com/base/path/admin/#/some/route[?queryParam=...]`
|
|
//
|
|
// We want to get to just `https://vaultwarden.example.com/base/path`.
|
|
const pathname = window.location.pathname;
|
|
const adminPos = pathname.indexOf("/admin");
|
|
const newPathname = pathname.substring(0, adminPos != -1 ? adminPos : pathname.length);
|
|
return `${window.location.origin}${newPathname}`;
|
|
}
|
|
const BASE_URL = getBaseUrl();
|
|
|
|
function reload() {
|
|
// Reload the page by setting the exact same href
|
|
// Using window.location.reload() could cause a repost.
|
|
window.location = window.location.href;
|
|
}
|
|
|
|
function msg(text, reload_page = true) {
|
|
text && alert(text);
|
|
reload_page && reload();
|
|
}
|
|
|
|
function _fetch(method, url, successMsg, errMsg, body, reload_page = true) {
|
|
let respStatus;
|
|
let respStatusText;
|
|
fetch(url, {
|
|
method: method,
|
|
body: body,
|
|
mode: "same-origin",
|
|
credentials: "same-origin",
|
|
headers: { "Content-Type": "application/json" }
|
|
}).then(resp => {
|
|
if (resp.ok) {
|
|
msg(successMsg, reload_page);
|
|
// Abuse the catch handler by setting error to false and continue
|
|
return Promise.reject({ error: false });
|
|
}
|
|
respStatus = resp.status;
|
|
respStatusText = resp.statusText;
|
|
return resp.text();
|
|
}).then(respText => {
|
|
try {
|
|
const respJson = JSON.parse(respText);
|
|
if (respJson.errorModel && respJson.errorModel.message) {
|
|
return respJson.errorModel.message;
|
|
} else {
|
|
return Promise.reject({ body: `${respStatus} - ${respStatusText}\n\nUnknown error`, error: true });
|
|
}
|
|
} catch (e) {
|
|
return Promise.reject({ body: `${respStatus} - ${respStatusText}\n\n[Catch] ${e}`, error: true });
|
|
}
|
|
}).then(apiMsg => {
|
|
msg(`${errMsg}\n${apiMsg}`, reload_page);
|
|
}).catch(e => {
|
|
if (e.error === false) { return true; }
|
|
else { msg(`${errMsg}\n${e.body}`, reload_page); }
|
|
});
|
|
}
|
|
|
|
function _post(url, successMsg, errMsg, body, reload_page = true) {
|
|
return _fetch("POST", url, successMsg, errMsg, body, reload_page);
|
|
}
|
|
|
|
function _delete(url, successMsg, errMsg, body, reload_page = true) {
|
|
return _fetch("DELETE", url, successMsg, errMsg, body, reload_page);
|
|
}
|
|
|
|
// Bootstrap Theme Selector
|
|
const getStoredTheme = () => localStorage.getItem("theme");
|
|
const setStoredTheme = theme => localStorage.setItem("theme", theme);
|
|
|
|
const getPreferredTheme = () => {
|
|
const storedTheme = getStoredTheme();
|
|
if (storedTheme) {
|
|
return storedTheme;
|
|
}
|
|
|
|
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
};
|
|
|
|
const setTheme = theme => {
|
|
if (theme === "auto" && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
document.documentElement.setAttribute("data-bs-theme", "dark");
|
|
} else {
|
|
document.documentElement.setAttribute("data-bs-theme", theme);
|
|
}
|
|
};
|
|
|
|
setTheme(getPreferredTheme());
|
|
|
|
const showActiveTheme = (theme, focus = false) => {
|
|
const themeSwitcher = document.querySelector("#bd-theme");
|
|
|
|
if (!themeSwitcher) {
|
|
return;
|
|
}
|
|
|
|
const themeSwitcherText = document.querySelector("#bd-theme-text");
|
|
const activeThemeIcon = document.querySelector(".theme-icon-active use");
|
|
const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`);
|
|
const svgOfActiveBtn = btnToActive.querySelector("span use").textContent;
|
|
|
|
document.querySelectorAll("[data-bs-theme-value]").forEach(element => {
|
|
element.classList.remove("active");
|
|
element.setAttribute("aria-pressed", "false");
|
|
});
|
|
|
|
btnToActive.classList.add("active");
|
|
btnToActive.setAttribute("aria-pressed", "true");
|
|
activeThemeIcon.textContent = svgOfActiveBtn;
|
|
const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`;
|
|
themeSwitcher.setAttribute("aria-label", themeSwitcherLabel);
|
|
|
|
if (focus) {
|
|
themeSwitcher.focus();
|
|
}
|
|
};
|
|
|
|
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
|
const storedTheme = getStoredTheme();
|
|
if (storedTheme !== "light" && storedTheme !== "dark") {
|
|
setTheme(getPreferredTheme());
|
|
}
|
|
});
|
|
|
|
|
|
// onLoad events
|
|
document.addEventListener("DOMContentLoaded", (/*event*/) => {
|
|
showActiveTheme(getPreferredTheme());
|
|
|
|
document.querySelectorAll("[data-bs-theme-value]")
|
|
.forEach(toggle => {
|
|
toggle.addEventListener("click", () => {
|
|
const theme = toggle.getAttribute("data-bs-theme-value");
|
|
setStoredTheme(theme);
|
|
setTheme(theme);
|
|
showActiveTheme(theme, true);
|
|
});
|
|
});
|
|
|
|
// get current URL path and assign "active" class to the correct nav-item
|
|
const pathname = window.location.pathname;
|
|
if (pathname === "") return;
|
|
const navItem = document.querySelectorAll(`.navbar-nav .nav-item a[href="${pathname}"]`);
|
|
if (navItem.length === 1) {
|
|
navItem[0].className = navItem[0].className + " active";
|
|
navItem[0].setAttribute("aria-current", "page");
|
|
}
|
|
});
|
|
|