123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299 |
- import { compress, decompress, debounce, isScrollable } from "@sv443-network/userutils";
- import { defaultConfig, getFeatures, migrations, saveFeatures, setDefaultFeatures } from "../config";
- import { compressionFormat, host, scriptInfo } from "../constants";
- import { featInfo, disableBeforeUnload } from "../features/index";
- import { error, getResourceUrl, info, log, resourceToHTMLString, warn, getLocale, hasKey, initTranslations, setLocale, t, parseMarkdown, getChangelogMd, compressionSupported } from "../utils";
- import { formatVersion } from "../config";
- import { emitSiteEvent, siteEvents } from "../siteEvents";
- import type { FeatureCategory, FeatureKey, FeatureConfig, HotkeyObj, FeatureInfo } from "../types";
- import "./menu_old.css";
- import { createHotkeyInput } from "../components";
- import pkg from "../../package.json" assert { type: "json" };
- //#MARKER create menu elements
- let isCfgMenuAdded = false;
- export let isCfgMenuOpen = false;
- /** Threshold in pixels from the top of the options container that dictates for how long the scroll indicator is shown */
- const scrollIndicatorOffsetThreshold = 30;
- let scrollIndicatorEnabled = true;
- /** Locale at the point of initializing the config menu */
- let initLocale: string | undefined;
- /** Stringified config at the point of initializing the config menu */
- let initConfig: string | undefined;
- /**
- * Adds an element to open the BetterYTM menu
- * @deprecated to be replaced with new menu - see https://github.com/Sv443/BetterYTM/issues/23
- */
- async function addCfgMenu() {
- if(isCfgMenuAdded)
- return;
- isCfgMenuAdded = true;
- initLocale = getFeatures().locale;
- initConfig = JSON.stringify(getFeatures());
- const initLangReloadText = t("lang_changed_prompt_reload");
- const toggled_on = t("toggled_on");
- const toggled_off = t("toggled_off");
- //#SECTION backdrop & menu container
- const backgroundElem = document.createElement("div");
- backgroundElem.id = "bytm-cfg-menu-bg";
- backgroundElem.classList.add("bytm-menu-bg");
- backgroundElem.ariaLabel = backgroundElem.title = t("close_menu_tooltip");
- backgroundElem.style.visibility = "hidden";
- backgroundElem.style.display = "none";
- backgroundElem.addEventListener("click", (e) => {
- if(isCfgMenuOpen && (e.target as HTMLElement)?.id === "bytm-cfg-menu-bg")
- closeCfgMenu(e);
- });
- document.body.addEventListener("keydown", (e) => {
- if(isCfgMenuOpen && e.key === "Escape")
- closeCfgMenu(e);
- });
- const menuContainer = document.createElement("div");
- menuContainer.ariaLabel = menuContainer.title = ""; // prevent bg title from propagating downwards
- menuContainer.classList.add("bytm-menu");
- menuContainer.id = "bytm-cfg-menu";
- //#SECTION title bar
- const headerElem = document.createElement("div");
- headerElem.classList.add("bytm-menu-header");
- const titleCont = document.createElement("div");
- titleCont.className = "bytm-menu-titlecont";
- titleCont.role = "heading";
- titleCont.ariaLevel = "1";
- const titleElem = document.createElement("h2");
- titleElem.className = "bytm-menu-title";
- const titleTextElem = document.createElement("div");
- titleTextElem.textContent = t("config_menu_title", scriptInfo.name);
- titleElem.appendChild(titleTextElem);
- const linksCont = document.createElement("div");
- linksCont.id = "bytm-menu-linkscont";
- linksCont.role = "navigation";
- const addLink = (imgSrc: string, href: string, title: string) => {
- const anchorElem = document.createElement("a");
- anchorElem.className = "bytm-menu-link bytm-no-select";
- anchorElem.rel = "noopener noreferrer";
- anchorElem.href = href;
- anchorElem.target = "_blank";
- anchorElem.tabIndex = 0;
- anchorElem.role = "button";
- anchorElem.ariaLabel = anchorElem.title = title;
- const imgElem = document.createElement("img");
- imgElem.className = "bytm-menu-img";
- imgElem.src = imgSrc;
- imgElem.style.width = "32px";
- imgElem.style.height = "32px";
- anchorElem.appendChild(imgElem);
- linksCont.appendChild(anchorElem);
- };
- addLink(await getResourceUrl("img-discord"), "https://dc.sv443.net/", t("open_discord"));
- const links: [name: string, ...Parameters<typeof addLink>][] = [
- ["github", await getResourceUrl("img-github"), scriptInfo.namespace, t("open_github", scriptInfo.name)],
- ["greasyfork", await getResourceUrl("img-greasyfork"), pkg.hosts.greasyfork, t("open_greasyfork", scriptInfo.name)],
- ["openuserjs", await getResourceUrl("img-openuserjs"), pkg.hosts.openuserjs, t("open_openuserjs", scriptInfo.name)],
- ];
- const hostLink = links.find(([name]) => name === host);
- const otherLinks = links.filter(([name]) => name !== host);
- const reorderedLinks = hostLink ? [hostLink, ...otherLinks] : links;
- for(const [, ...args] of reorderedLinks)
- addLink(...args);
- const closeElem = document.createElement("img");
- closeElem.classList.add("bytm-menu-close");
- closeElem.role = "button";
- closeElem.tabIndex = 0;
- closeElem.src = await getResourceUrl("img-close");
- closeElem.ariaLabel = closeElem.title = t("close_menu_tooltip");
- closeElem.addEventListener("click", closeCfgMenu);
- closeElem.addEventListener("keydown", ({ key }) => key === "Enter" && closeCfgMenu());
- titleCont.appendChild(titleElem);
- titleCont.appendChild(linksCont);
- headerElem.appendChild(titleCont);
- headerElem.appendChild(closeElem);
- //#SECTION footer
- const footerCont = document.createElement("div");
- footerCont.className = "bytm-menu-footer-cont";
- const footerElemCont = document.createElement("div");
- const footerElem = document.createElement("div");
- footerElem.classList.add("bytm-menu-footer", "hidden");
- footerElem.textContent = t("reload_hint");
- const reloadElem = document.createElement("button");
- reloadElem.classList.add("bytm-btn");
- reloadElem.style.marginLeft = "10px";
- reloadElem.textContent = t("reload_now");
- reloadElem.ariaLabel = reloadElem.title = t("reload_tooltip");
- reloadElem.addEventListener("click", () => {
- closeCfgMenu();
- disableBeforeUnload();
- location.reload();
- });
- footerElem.appendChild(reloadElem);
- footerElemCont.appendChild(footerElem);
- const resetElem = document.createElement("button");
- resetElem.classList.add("bytm-btn");
- resetElem.ariaLabel = resetElem.title = t("reset_tooltip");
- resetElem.textContent = t("reset");
- resetElem.addEventListener("click", async () => {
- if(confirm(t("reset_confirm"))) {
- await setDefaultFeatures();
- closeCfgMenu();
- disableBeforeUnload();
- location.reload();
- }
- });
- const exportElem = document.createElement("button");
- exportElem.classList.add("bytm-btn");
- exportElem.ariaLabel = exportElem.title = t("export_tooltip");
- exportElem.textContent = t("export");
- exportElem.addEventListener("click", async () => {
- await openExportMenu();
- closeCfgMenu(undefined, false);
- });
- const importElem = document.createElement("button");
- importElem.classList.add("bytm-btn");
- importElem.ariaLabel = importElem.title = t("import_tooltip");
- importElem.textContent = t("import");
- importElem.addEventListener("click", async () => {
- await openImportMenu();
- closeCfgMenu(undefined, false);
- });
- const buttonsCont = document.createElement("div");
- buttonsCont.id = "bytm-menu-footer-buttons-cont";
- buttonsCont.appendChild(exportElem);
- buttonsCont.appendChild(importElem);
- buttonsCont.appendChild(resetElem);
- footerCont.appendChild(footerElemCont);
- footerCont.appendChild(buttonsCont);
- //#SECTION feature list
- const featuresCont = document.createElement("div");
- featuresCont.id = "bytm-menu-opts";
- /** Gets called whenever the feature config is changed */
- const confChanged = debounce(async (key: keyof typeof defaultConfig, initialVal: number | boolean | Record<string, unknown>, newVal: number | boolean | Record<string, unknown>) => {
- const fmt = (val: unknown) => typeof val === "object" ? JSON.stringify(val) : String(val);
- info(`Feature config changed at key '${key}', from value '${fmt(initialVal)}' to '${fmt(newVal)}'`);
- const featConf = JSON.parse(JSON.stringify(getFeatures()));
- featConf[key] = newVal as never;
- await saveFeatures(featConf);
- if(initConfig !== JSON.stringify(featConf))
- footerElem.classList.remove("hidden");
- else
- footerElem.classList.add("hidden");
- if(initLocale !== featConf.locale) {
- await initTranslations(featConf.locale);
- setLocale(featConf.locale);
- const newText = t("lang_changed_prompt_reload");
- const confirmText = newText !== initLangReloadText ? `${newText}\n\n────────────────────────────────\n\n${initLangReloadText}` : newText;
- if(confirm(confirmText)) {
- closeCfgMenu();
- disableBeforeUnload();
- location.reload();
- }
- }
- else if(getLocale() !== featConf.locale)
- setLocale(featConf.locale);
- });
- const featureCfg = getFeatures();
- const featureCfgWithCategories = Object.entries(featInfo)
- .reduce(
- (acc, [key, { category }]) => {
- if(!acc[category])
- acc[category] = {} as Record<FeatureKey, unknown>;
- acc[category][key as FeatureKey] = featureCfg[key as FeatureKey];
- return acc;
- },
- {} as Record<FeatureCategory, Record<FeatureKey, unknown>>,
- );
- const fmtVal = (v: unknown) => String(v).trim();
- const toggleLabelText = (toggled: boolean) => toggled ? toggled_on : toggled_off;
- for(const category in featureCfgWithCategories) {
- const featObj = featureCfgWithCategories[category as FeatureCategory];
- const catHeaderElem = document.createElement("h3");
- catHeaderElem.classList.add("bytm-ftconf-category-header");
- catHeaderElem.role = "heading";
- catHeaderElem.ariaLevel = "2";
- catHeaderElem.textContent = `${t(`feature_category_${category}`)}:`;
- featuresCont.appendChild(catHeaderElem);
- for(const featKey in featObj) {
- const ftInfo = featInfo[featKey as keyof typeof featureCfg] as FeatureInfo[keyof typeof featureCfg];
- // @ts-ignore
- if(!ftInfo || ftInfo.hidden === true)
- continue;
- const { type, default: ftDefault } = ftInfo;
- // @ts-ignore
- const step = ftInfo?.step ?? undefined;
- const val = featureCfg[featKey as keyof typeof featureCfg];
- const initialVal = val ?? ftDefault ?? undefined;
- const ftConfElem = document.createElement("div");
- ftConfElem.classList.add("bytm-ftitem");
- {
- const featLeftSideElem = document.createElement("div");
- featLeftSideElem.classList.add("bytm-ftitem-leftside");
- const textElem = document.createElement("span");
- textElem.textContent = t(`feature_desc_${featKey}`);
- let adornmentElem: undefined | HTMLElement;
- const adornContent = ftInfo.textAdornment?.();
- if(typeof adornContent === "string" || adornContent instanceof Promise) {
- adornmentElem = document.createElement("span");
- adornmentElem.id = `bytm-ftitem-${featKey}-adornment`;
- adornmentElem.classList.add("bytm-ftitem-adornment");
- adornmentElem.innerHTML = adornContent instanceof Promise ? await adornContent : adornContent;
- }
- let helpElem: undefined | HTMLDivElement;
- // @ts-ignore
- const hasHelpTextFunc = typeof featInfo[featKey as keyof typeof featInfo]?.helpText === "function";
- // @ts-ignore
- const helpTextVal: string | undefined = hasHelpTextFunc && featInfo[featKey as keyof typeof featInfo]!.helpText();
- if(hasKey(`feature_helptext_${featKey}`) || (helpTextVal && hasKey(helpTextVal))) {
- const helpElemImgHtml = await resourceToHTMLString("img-help");
- if(helpElemImgHtml) {
- helpElem = document.createElement("div");
- helpElem.classList.add("bytm-ftitem-help-btn", "bytm-generic-btn");
- helpElem.ariaLabel = helpElem.title = t("feature_help_button_tooltip");
- helpElem.role = "button";
- helpElem.tabIndex = 0;
- helpElem.innerHTML = helpElemImgHtml;
- const helpElemClicked = (e: MouseEvent | KeyboardEvent) => {
- e.preventDefault();
- e.stopPropagation();
- openHelpDialog(featKey as FeatureKey);
- };
- helpElem.addEventListener("click", helpElemClicked);
- helpElem.addEventListener("keydown", (e) => e.key === "Enter" && helpElemClicked(e));
- }
- else {
- error(`Couldn't create help button SVG element for feature '${featKey}'`);
- }
- }
- featLeftSideElem.appendChild(textElem);
- adornmentElem && featLeftSideElem.appendChild(adornmentElem);
- helpElem && featLeftSideElem.appendChild(helpElem);
- ftConfElem.appendChild(featLeftSideElem);
- }
- {
- let inputType: string | undefined = "text";
- let inputTag: string | undefined = "input";
- switch(type)
- {
- case "toggle":
- inputType = "checkbox";
- break;
- case "slider":
- inputType = "range";
- break;
- case "number":
- inputType = "number";
- break;
- case "select":
- inputTag = "select";
- inputType = undefined;
- break;
- case "hotkey":
- inputTag = undefined;
- inputType = undefined;
- break;
- }
- const inputElemId = `bytm-ftconf-${featKey}-input`;
- const ctrlElem = document.createElement("span");
- ctrlElem.classList.add("bytm-ftconf-ctrl");
- if(inputTag) {
- // standard input element:
- const inputElem = document.createElement(inputTag) as HTMLInputElement;
- inputElem.classList.add("bytm-ftconf-input");
- inputElem.id = inputElemId;
- if(inputType)
- inputElem.type = inputType;
- // @ts-ignore
- if(typeof ftInfo.min !== "undefined" && ftInfo.max !== "undefined") {
- // @ts-ignore
- inputElem.min = ftInfo.min;
- // @ts-ignore
- inputElem.max = ftInfo.max;
- }
- if(typeof initialVal !== "undefined")
- inputElem.value = String(initialVal);
- if(type === "number" || type === "slider" && step)
- inputElem.step = String(step);
- if(type === "toggle" && typeof initialVal !== "undefined")
- inputElem.checked = Boolean(initialVal);
- // @ts-ignore
- const unitTxt = typeof ftInfo.unit === "string" ? " " + ftInfo.unit : "";
- let labelElem: HTMLLabelElement | undefined;
- if(type === "slider") {
- labelElem = document.createElement("label");
- labelElem.classList.add("bytm-ftconf-label", "bytm-slider-label");
- labelElem.textContent = fmtVal(initialVal) + unitTxt;
- inputElem.addEventListener("input", () => {
- if(labelElem)
- labelElem.textContent = fmtVal(Number(inputElem.value)) + unitTxt;
- });
- }
- else if(type === "toggle") {
- labelElem = document.createElement("label");
- labelElem.classList.add("bytm-ftconf-label", "bytm-toggle-label");
- labelElem.textContent = toggleLabelText(Boolean(initialVal)) + unitTxt;
- inputElem.addEventListener("input", () => {
- if(labelElem)
- labelElem.textContent = toggleLabelText(inputElem.checked) + unitTxt;
- });
- }
- else if(type === "select") {
- const ftOpts = typeof ftInfo.options === "function"
- ? ftInfo.options()
- : ftInfo.options;
- for(const { value, label } of ftOpts) {
- const optionElem = document.createElement("option");
- optionElem.value = String(value);
- optionElem.textContent = label;
- if(value === initialVal)
- optionElem.selected = true;
- inputElem.appendChild(optionElem);
- }
- }
- inputElem.addEventListener("input", () => {
- let v: string | number = String(inputElem.value).trim();
- if(["number", "slider"].includes(type) || v.match(/^-?\d+$/))
- v = Number(v);
- if(typeof initialVal !== "undefined")
- confChanged(featKey as keyof FeatureConfig, initialVal, (type !== "toggle" ? v : inputElem.checked));
- });
- if(labelElem) {
- labelElem.id = `bytm-ftconf-${featKey}-label`;
- labelElem.htmlFor = inputElemId;
- ctrlElem.appendChild(labelElem);
- }
- ctrlElem.appendChild(inputElem);
- }
- else {
- // custom input element:
- let wrapperElem: HTMLElement | undefined;
- switch(type) {
- case "hotkey":
- wrapperElem = createHotkeyInput({
- initialValue: initialVal as HotkeyObj,
- onChange: (hotkey) => {
- confChanged(featKey as keyof FeatureConfig, initialVal, hotkey);
- },
- });
- break;
- }
- ctrlElem.appendChild(wrapperElem!);
- }
- ftConfElem.appendChild(ctrlElem);
- }
- featuresCont.appendChild(ftConfElem);
- }
- }
- //#SECTION set values of inputs on external change
- siteEvents.on("rebuildCfgMenu", (newConfig) => {
- for(const ftKey in featInfo) {
- const ftElem = document.querySelector<HTMLInputElement>(`#bytm-ftconf-${ftKey}-input`);
- const labelElem = document.querySelector<HTMLLabelElement>(`#bytm-ftconf-${ftKey}-label`);
- if(!ftElem)
- continue;
- const ftInfo = featInfo[ftKey as keyof typeof featInfo];
- const value = newConfig[ftKey as keyof FeatureConfig];
- if(ftInfo.type === "toggle")
- ftElem.checked = Boolean(value);
- else
- ftElem.value = String(value);
- if(!labelElem)
- continue;
-
- // @ts-ignore
- const unitTxt = typeof ftInfo.unit === "string" ? " " + ftInfo.unit : "";
- if(ftInfo.type === "slider")
- labelElem.textContent = fmtVal(Number(value)) + unitTxt;
- else if(ftInfo.type === "toggle")
- labelElem.textContent = toggleLabelText(Boolean(value)) + unitTxt;
- }
- info("Rebuilt config menu");
- });
- //#SECTION scroll indicator
- const scrollIndicator = document.createElement("img");
- scrollIndicator.id = "bytm-menu-scroll-indicator";
- scrollIndicator.src = await getResourceUrl("img-arrow_down");
- scrollIndicator.role = "button";
- scrollIndicator.ariaLabel = scrollIndicator.title = t("scroll_to_bottom");
- featuresCont.appendChild(scrollIndicator);
- scrollIndicator.addEventListener("click", () => {
- const bottomAnchor = document.querySelector("#bytm-menu-bottom-anchor");
- bottomAnchor?.scrollIntoView({
- behavior: "smooth",
- });
- });
- featuresCont.addEventListener("scroll", (evt: Event) => {
- const scrollPos = (evt.target as HTMLDivElement)?.scrollTop ?? 0;
- const scrollIndicator = document.querySelector<HTMLImageElement>("#bytm-menu-scroll-indicator");
- if(!scrollIndicator)
- return;
- if(scrollIndicatorEnabled && scrollPos > scrollIndicatorOffsetThreshold && !scrollIndicator.classList.contains("bytm-hidden")) {
- scrollIndicator.classList.add("bytm-hidden");
- }
- else if(scrollIndicatorEnabled && scrollPos <= scrollIndicatorOffsetThreshold && scrollIndicator.classList.contains("bytm-hidden")) {
- scrollIndicator.classList.remove("bytm-hidden");
- }
- });
- const bottomAnchor = document.createElement("div");
- bottomAnchor.id = "bytm-menu-bottom-anchor";
- featuresCont.appendChild(bottomAnchor);
- //#SECTION finalize
- menuContainer.appendChild(headerElem);
- menuContainer.appendChild(featuresCont);
- const versionElemCont = document.createElement("div");
- versionElemCont.id = "bytm-menu-version";
- const versionElem = document.createElement("a");
- versionElem.classList.add("bytm-link");
- versionElem.role = "button";
- versionElem.tabIndex = 0;
- versionElem.ariaLabel = versionElem.title = t("version_tooltip", scriptInfo.version, scriptInfo.buildNumber);
- versionElem.textContent = `v${scriptInfo.version} (${scriptInfo.buildNumber})`;
- const versionElemClicked = async (e: MouseEvent | KeyboardEvent) => {
- e.preventDefault();
- e.stopPropagation();
- await openChangelogMenu("cfgMenu");
- closeCfgMenu(undefined, false);
- };
- versionElem.addEventListener("click", versionElemClicked);
- versionElem.addEventListener("keydown", (e) => e.key === "Enter" && versionElemClicked(e));
- menuContainer.appendChild(footerCont);
- versionElemCont.appendChild(versionElem);
- titleElem.appendChild(versionElemCont);
- backgroundElem.appendChild(menuContainer);
- document.body.appendChild(backgroundElem);
- window.addEventListener("resize", debounce(checkToggleScrollIndicator, 150));
- log("Added menu element");
- // ensure stuff is reset if menu was opened before being added
- isCfgMenuOpen = false;
- document.body.classList.remove("bytm-disable-scroll");
- document.querySelector("ytmusic-app")?.removeAttribute("inert");
- backgroundElem.style.visibility = "hidden";
- backgroundElem.style.display = "none";
- }
- /** Closes the config menu if it is open. If a bubbling event is passed, its propagation will be prevented. */
- export function closeCfgMenu(evt?: MouseEvent | KeyboardEvent, enableScroll = true) {
- if(!isCfgMenuOpen)
- return;
- isCfgMenuOpen = false;
- evt?.bubbles && evt.stopPropagation();
- if(enableScroll) {
- document.body.classList.remove("bytm-disable-scroll");
- document.querySelector("ytmusic-app")?.removeAttribute("inert");
- }
- const menuBg = document.querySelector<HTMLElement>("#bytm-cfg-menu-bg");
- siteEvents.emit("cfgMenuClosed");
- if(!menuBg)
- return;
- menuBg.style.visibility = "hidden";
- menuBg.style.display = "none";
- }
- /** Opens the config menu if it is closed */
- export async function openCfgMenu() {
- if(!isCfgMenuAdded)
- await addCfgMenu();
- if(isCfgMenuOpen)
- return;
- isCfgMenuOpen = true;
- document.body.classList.add("bytm-disable-scroll");
- document.querySelector("ytmusic-app")?.setAttribute("inert", "true");
- const menuBg = document.querySelector<HTMLElement>("#bytm-cfg-menu-bg");
- if(!menuBg)
- return;
- menuBg.style.visibility = "visible";
- menuBg.style.display = "block";
- checkToggleScrollIndicator();
- }
- /** Checks if the features container is scrollable and toggles the scroll indicator accordingly */
- function checkToggleScrollIndicator() {
- const featuresCont = document.querySelector<HTMLElement>("#bytm-menu-opts");
- const scrollIndicator = document.querySelector<HTMLElement>("#bytm-menu-scroll-indicator");
-
- // disable scroll indicator if container doesn't scroll
- if(featuresCont && scrollIndicator) {
- const verticalScroll = isScrollable(featuresCont).vertical;
- /** If true, the indicator's threshold is under the available scrollable space and so it should be disabled */
- const underThreshold = featuresCont.scrollHeight - featuresCont.clientHeight <= scrollIndicatorOffsetThreshold;
- if(!underThreshold && verticalScroll && !scrollIndicatorEnabled) {
- scrollIndicatorEnabled = true;
- scrollIndicator.classList.remove("bytm-hidden");
- }
- if((!verticalScroll && scrollIndicatorEnabled) || underThreshold) {
- scrollIndicatorEnabled = false;
- scrollIndicator.classList.add("bytm-hidden");
- }
- }
- }
- //#MARKER help dialog
- let isHelpDialogOpen = false;
- /** Key of the feature currently loaded in the help dialog */
- let helpDialogCurFeature: FeatureKey | undefined;
- /** Opens the feature help dialog for the given feature */
- async function openHelpDialog(featureKey: FeatureKey) {
- if(isHelpDialogOpen)
- return;
- isHelpDialogOpen = true;
- let menuBgElem: HTMLElement;
- if(!helpDialogCurFeature) {
- // create menu
- const headerElem = document.createElement("div");
- headerElem.classList.add("bytm-menu-header", "small");
- const titleCont = document.createElement("div");
- titleCont.className = "bytm-menu-titlecont-no-title";
- titleCont.role = "heading";
- titleCont.ariaLevel = "1";
- const helpIconSvg = await resourceToHTMLString("img-help");
- if(helpIconSvg)
- titleCont.innerHTML = helpIconSvg;
- const closeElem = document.createElement("img");
- closeElem.classList.add("bytm-menu-close", "small");
- closeElem.role = "button";
- closeElem.tabIndex = 0;
- closeElem.src = await getResourceUrl("img-close");
- closeElem.ariaLabel = closeElem.title = t("close_menu_tooltip");
- closeElem.addEventListener("click", (e) => closeHelpDialog(e));
- closeElem.addEventListener("keydown", (e) => e.key === "Enter" && closeHelpDialog(e));
- headerElem.appendChild(titleCont);
- headerElem.appendChild(closeElem);
- menuBgElem = document.createElement("div");
- menuBgElem.id = "bytm-feat-help-menu-bg";
- menuBgElem.classList.add("bytm-menu-bg");
- menuBgElem.ariaLabel = menuBgElem.title = t("close_menu_tooltip");
- menuBgElem.style.visibility = "hidden";
- menuBgElem.style.display = "none";
- menuBgElem.addEventListener("click", (e) => {
- if(isHelpDialogOpen && (e.target as HTMLElement)?.id === "bytm-feat-help-menu-bg")
- closeHelpDialog(e);
- });
- document.body.addEventListener("keydown", (e) => {
- if(isHelpDialogOpen && e.key === "Escape")
- closeHelpDialog(e);
- });
- const menuContainer = document.createElement("div");
- menuContainer.ariaLabel = menuContainer.title = ""; // prevent bg title from propagating downwards
- menuContainer.classList.add("bytm-menu");
- menuContainer.id = "bytm-feat-help-menu";
- const featDescElem = document.createElement("h3");
- featDescElem.id = "bytm-feat-help-menu-desc";
- const helpTextElem = document.createElement("div");
- helpTextElem.id = "bytm-feat-help-menu-text";
- menuContainer.appendChild(headerElem);
- menuContainer.appendChild(featDescElem);
- menuContainer.appendChild(helpTextElem);
- menuBgElem.appendChild(menuContainer);
- document.body.appendChild(menuBgElem);
- }
- else
- menuBgElem = document.querySelector<HTMLElement>("#bytm-feat-help-menu-bg")!;
- if(helpDialogCurFeature !== featureKey) {
- // update help text
- const featDescElem = menuBgElem.querySelector<HTMLElement>("#bytm-feat-help-menu-desc")!;
- const helpTextElem = menuBgElem.querySelector<HTMLElement>("#bytm-feat-help-menu-text")!;
- featDescElem.textContent = t(`feature_desc_${featureKey}`);
- // @ts-ignore
- const helpText: string | undefined = featInfo[featureKey]?.helpText?.();
- helpTextElem.textContent = helpText ?? t(`feature_helptext_${featureKey}`);
- }
- // show menu
- const menuBg = document.querySelector<HTMLElement>("#bytm-feat-help-menu-bg");
- if(!menuBg)
- return warn("Couldn't find feature help dialog background element");
- helpDialogCurFeature = featureKey;
- menuBg.style.visibility = "visible";
- menuBg.style.display = "block";
- document.querySelector("#bytm-cfg-menu")?.setAttribute("inert", "true");
- }
- function closeHelpDialog(evt?: MouseEvent | KeyboardEvent) {
- if(!isHelpDialogOpen)
- return;
- isHelpDialogOpen = false;
- evt?.bubbles && evt.stopPropagation();
- const menuBg = document.querySelector<HTMLElement>("#bytm-feat-help-menu-bg");
- if(!menuBg)
- return warn("Couldn't find feature help dialog background element");
- menuBg.style.visibility = "hidden";
- menuBg.style.display = "none";
- document.querySelector("#bytm-cfg-menu")?.removeAttribute("inert");
- }
- //#MARKER export menu
- let isExportMenuAdded = false;
- let isExportMenuOpen = false;
- let copiedTxtTimeout: number | undefined = undefined;
- let lastUncompressedCfgString: string | undefined;
- /** Adds a menu to copy the current configuration as compressed (if supported) or uncompressed JSON (hidden by default) */
- async function addExportMenu() {
- const canCompress = await compressionSupported();
- const menuBgElem = document.createElement("div");
- menuBgElem.id = "bytm-export-menu-bg";
- menuBgElem.classList.add("bytm-menu-bg");
- menuBgElem.ariaLabel = menuBgElem.title = t("close_menu_tooltip");
- menuBgElem.style.visibility = "hidden";
- menuBgElem.style.display = "none";
- menuBgElem.addEventListener("click", (e) => {
- if(isExportMenuOpen && (e.target as HTMLElement)?.id === "bytm-export-menu-bg") {
- closeExportMenu(e);
- openCfgMenu();
- }
- });
- document.body.addEventListener("keydown", (e) => {
- if(isExportMenuOpen && e.key === "Escape") {
- closeExportMenu(e);
- openCfgMenu();
- }
- });
- const menuContainer = document.createElement("div");
- menuContainer.ariaLabel = menuContainer.title = ""; // prevent bg title from propagating downwards
- menuContainer.classList.add("bytm-menu");
- menuContainer.id = "bytm-export-menu";
- //#SECTION title bar
- const headerElem = document.createElement("div");
- headerElem.classList.add("bytm-menu-header");
- const titleCont = document.createElement("div");
- titleCont.className = "bytm-menu-titlecont";
- titleCont.role = "heading";
- titleCont.ariaLevel = "1";
- const titleElem = document.createElement("h2");
- titleElem.className = "bytm-menu-title";
- titleElem.textContent = t("export_menu_title", scriptInfo.name);
- const closeElem = document.createElement("img");
- closeElem.classList.add("bytm-menu-close");
- closeElem.role = "button";
- closeElem.tabIndex = 0;
- closeElem.src = await getResourceUrl("img-close");
- closeElem.ariaLabel = closeElem.title = t("close_menu_tooltip");
- const closeExportMenuClicked = (e: MouseEvent | KeyboardEvent) => {
- closeExportMenu(e);
- openCfgMenu();
- };
- closeElem.addEventListener("click", (e) => closeExportMenuClicked(e));
- closeElem.addEventListener("keydown", (e) => e.key === "Enter" && closeExportMenuClicked(e));
- titleCont.appendChild(titleElem);
- headerElem.appendChild(titleCont);
- headerElem.appendChild(closeElem);
- //#SECTION body
- const menuBodyElem = document.createElement("div");
- menuBodyElem.classList.add("bytm-menu-body");
- const textElem = document.createElement("div");
- textElem.id = "bytm-export-menu-text";
- textElem.textContent = t("export_hint");
- const textAreaElem = document.createElement("textarea");
- textAreaElem.id = "bytm-export-menu-textarea";
- textAreaElem.readOnly = true;
- const cfgString = JSON.stringify({ formatVersion, data: getFeatures() });
- lastUncompressedCfgString = JSON.stringify({ formatVersion, data: getFeatures() }, undefined, 2);
- textAreaElem.value = canCompress ? await compress(cfgString, compressionFormat, "string") : cfgString;
- siteEvents.on("configChanged", async (data) => {
- const textAreaElem = document.querySelector<HTMLTextAreaElement>("#bytm-export-menu-textarea");
- const cfgString = JSON.stringify({ formatVersion, data });
- lastUncompressedCfgString = JSON.stringify({ formatVersion, data }, undefined, 2);
- if(textAreaElem)
- textAreaElem.value = canCompress ? await compress(cfgString, compressionFormat, "string") : cfgString;
- });
- //#SECTION footer
- const footerElem = document.createElement("div");
- footerElem.classList.add("bytm-menu-footer-right");
- const copyBtnElem = document.createElement("button");
- copyBtnElem.classList.add("bytm-btn");
- copyBtnElem.textContent = t("copy_to_clipboard");
- copyBtnElem.ariaLabel = copyBtnElem.title = t("copy_config_tooltip");
- const copiedTextElem = document.createElement("span");
- copiedTextElem.id = "bytm-export-menu-copied-txt";
- copiedTextElem.classList.add("bytm-menu-footer-copied");
- copiedTextElem.textContent = t("copied_notice");
- copiedTextElem.style.display = "none";
- const copyBtnClicked = async (evt: MouseEvent | KeyboardEvent) => {
- evt?.bubbles && evt.stopPropagation();
- const textAreaElem = document.querySelector<HTMLTextAreaElement>("#bytm-export-menu-textarea");
- if(textAreaElem) {
- GM.setClipboard(String(evt?.shiftKey || evt?.ctrlKey ? lastUncompressedCfgString : textAreaElem.value));
- copiedTextElem.style.display = "inline-block";
- if(typeof copiedTxtTimeout === "undefined") {
- copiedTxtTimeout = setTimeout(() => {
- copiedTextElem.style.display = "none";
- copiedTxtTimeout = undefined;
- }, 3000) as unknown as number;
- }
- }
- };
- copyBtnElem.addEventListener("click", copyBtnClicked);
- copyBtnElem.addEventListener("keydown", (e) => e.key === "Enter" && copyBtnClicked(e));
- // flex-direction is row-reverse
- footerElem.appendChild(copyBtnElem);
- footerElem.appendChild(copiedTextElem);
- //#SECTION finalize
- menuBodyElem.appendChild(textElem);
- menuBodyElem.appendChild(textAreaElem);
- menuBodyElem.appendChild(footerElem);
- menuContainer.appendChild(headerElem);
- menuContainer.appendChild(menuBodyElem);
-
- menuBgElem.appendChild(menuContainer);
- document.body.appendChild(menuBgElem);
- }
- /** Closes the export menu if it is open. If a bubbling event is passed, its propagation will be prevented. */
- function closeExportMenu(evt: MouseEvent | KeyboardEvent) {
- if(!isExportMenuOpen)
- return;
- isExportMenuOpen = false;
- evt?.bubbles && evt.stopPropagation();
- const menuBg = document.querySelector<HTMLElement>("#bytm-export-menu-bg");
- if(!menuBg)
- return warn("Couldn't find export menu background element");
- menuBg.style.visibility = "hidden";
- menuBg.style.display = "none";
- const copiedTxt = document.querySelector<HTMLElement>("#bytm-export-menu-copied-txt");
- if(copiedTxt) {
- copiedTxt.style.display = "none";
- if(typeof copiedTxtTimeout === "number") {
- clearTimeout(copiedTxtTimeout);
- copiedTxtTimeout = undefined;
- }
- }
- }
- /** Opens the export menu if it is closed */
- async function openExportMenu() {
- if(!isExportMenuAdded)
- await addExportMenu();
- isExportMenuAdded = true;
- if(isExportMenuOpen)
- return;
- isExportMenuOpen = true;
- document.body.classList.add("bytm-disable-scroll");
- document.querySelector("ytmusic-app")?.setAttribute("inert", "true");
- const menuBg = document.querySelector<HTMLElement>("#bytm-export-menu-bg");
- if(!menuBg)
- return warn("Couldn't find export menu background element");
- menuBg.style.visibility = "visible";
- menuBg.style.display = "block";
- }
- //#MARKER import menu
- let isImportMenuAdded = false;
- let isImportMenuOpen = false;
- /** Adds a menu to import a configuration from compressed or uncompressed JSON (hidden by default) */
- async function addImportMenu() {
- const menuBgElem = document.createElement("div");
- menuBgElem.id = "bytm-import-menu-bg";
- menuBgElem.classList.add("bytm-menu-bg");
- menuBgElem.ariaLabel = menuBgElem.title = t("close_menu_tooltip");
- menuBgElem.style.visibility = "hidden";
- menuBgElem.style.display = "none";
- menuBgElem.addEventListener("click", (e) => {
- if(isImportMenuOpen && (e.target as HTMLElement)?.id === "bytm-import-menu-bg") {
- closeImportMenu(e);
- openCfgMenu();
- }
- });
- document.body.addEventListener("keydown", (e) => {
- if(isImportMenuOpen && e.key === "Escape") {
- closeImportMenu(e);
- openCfgMenu();
- }
- });
- const menuContainer = document.createElement("div");
- menuContainer.ariaLabel = menuContainer.title = ""; // prevent bg title from propagating downwards
- menuContainer.classList.add("bytm-menu");
- menuContainer.id = "bytm-import-menu";
- //#SECTION title bar
- const headerElem = document.createElement("div");
- headerElem.classList.add("bytm-menu-header");
- const titleCont = document.createElement("div");
- titleCont.className = "bytm-menu-titlecont";
- titleCont.role = "heading";
- titleCont.ariaLevel = "1";
- const titleElem = document.createElement("h2");
- titleElem.className = "bytm-menu-title";
- titleElem.textContent = t("import_menu_title", scriptInfo.name);
- const closeElem = document.createElement("img");
- closeElem.classList.add("bytm-menu-close");
- closeElem.role = "button";
- closeElem.tabIndex = 0;
- closeElem.src = await getResourceUrl("img-close");
- closeElem.ariaLabel = closeElem.title = t("close_menu_tooltip");
- const closeImportMenuClicked = (e: MouseEvent | KeyboardEvent) => {
- closeImportMenu(e);
- openCfgMenu();
- };
- closeElem.addEventListener("click", closeImportMenuClicked);
- closeElem.addEventListener("keydown", (e) => e.key === "Enter" && closeImportMenuClicked(e));
- titleCont.appendChild(titleElem);
- headerElem.appendChild(titleCont);
- headerElem.appendChild(closeElem);
- //#SECTION body
- const menuBodyElem = document.createElement("div");
- menuBodyElem.classList.add("bytm-menu-body");
- const textElem = document.createElement("div");
- textElem.id = "bytm-import-menu-text";
- textElem.textContent = t("import_hint");
- const textAreaElem = document.createElement("textarea");
- textAreaElem.id = "bytm-import-menu-textarea";
- //#SECTION footer
- const footerElem = document.createElement("div");
- footerElem.classList.add("bytm-menu-footer-right");
- const importBtnElem = document.createElement("button");
- importBtnElem.classList.add("bytm-btn");
- importBtnElem.textContent = t("import");
- importBtnElem.ariaLabel = importBtnElem.title = t("start_import_tooltip");
- importBtnElem.addEventListener("click", async (evt) => {
- evt?.bubbles && evt.stopPropagation();
- const textAreaElem = document.querySelector<HTMLTextAreaElement>("#bytm-import-menu-textarea");
- if(!textAreaElem)
- return warn("Couldn't find import menu textarea element");
- try {
- /** Tries to parse an uncompressed or compressed input string as a JSON object */
- const decode = async (input: string) => {
- try {
- return JSON.parse(input);
- }
- catch {
- try {
- return JSON.parse(await decompress(input, compressionFormat, "string"));
- }
- catch(err) {
- warn("Couldn't import configuration:", err);
- return null;
- }
- }
- };
- const parsed = await decode(textAreaElem.value.trim());
- if(typeof parsed !== "object")
- return alert(t("import_error_invalid"));
- if(typeof parsed.formatVersion !== "number")
- return alert(t("import_error_no_format_version"));
- if(typeof parsed.data !== "object" || parsed.data === null || Object.keys(parsed.data).length === 0)
- return alert(t("import_error_no_data"));
- if(parsed.formatVersion < formatVersion) {
- let newData = JSON.parse(JSON.stringify(parsed.data));
- const sortedMigrations = Object.entries(migrations)
- .sort(([a], [b]) => Number(a) - Number(b));
- let curFmtVer = Number(parsed.formatVersion);
- for(const [fmtVer, migrationFunc] of sortedMigrations) {
- const ver = Number(fmtVer);
- if(curFmtVer < formatVersion && curFmtVer < ver) {
- try {
- const migRes = JSON.parse(JSON.stringify(migrationFunc(newData)));
- newData = migRes instanceof Promise ? await migRes : migRes;
- curFmtVer = ver;
- }
- catch(err) {
- console.error(`Error while running migration function for format version ${fmtVer}:`, err);
- }
- }
- }
- parsed.formatVersion = curFmtVer;
- parsed.data = newData;
- }
- else if(parsed.formatVersion !== formatVersion)
- return alert(t("import_error_wrong_format_version", formatVersion, parsed.formatVersion));
- await saveFeatures({ ...getFeatures(), ...parsed.data });
- if(confirm(t("import_success_confirm_reload"))) {
- disableBeforeUnload();
- return location.reload();
- }
- emitSiteEvent("rebuildCfgMenu", parsed.data);
- closeImportMenu();
- openCfgMenu();
- }
- catch(err) {
- warn("Couldn't import configuration:", err);
- alert(t("import_error_invalid"));
- }
- });
- footerElem.appendChild(importBtnElem);
- //#SECTION finalize
- menuBodyElem.appendChild(textElem);
- menuBodyElem.appendChild(textAreaElem);
- menuBodyElem.appendChild(footerElem);
- menuContainer.appendChild(headerElem);
- menuContainer.appendChild(menuBodyElem);
-
- menuBgElem.appendChild(menuContainer);
- document.body.appendChild(menuBgElem);
- }
- /** Closes the import menu if it is open. If a bubbling event is passed, its propagation will be prevented. */
- function closeImportMenu(evt?: MouseEvent | KeyboardEvent) {
- if(!isImportMenuOpen)
- return;
- isImportMenuOpen = false;
- evt?.bubbles && evt.stopPropagation();
- const menuBg = document.querySelector<HTMLElement>("#bytm-import-menu-bg");
- const textAreaElem = document.querySelector<HTMLTextAreaElement>("#bytm-import-menu-textarea");
- if(textAreaElem)
- textAreaElem.value = "";
- if(!menuBg)
- return warn("Couldn't find import menu background element");
- menuBg.style.visibility = "hidden";
- menuBg.style.display = "none";
- }
- /** Opens the import menu if it is closed */
- async function openImportMenu() {
- if(!isImportMenuAdded)
- await addImportMenu();
- isImportMenuAdded = true;
- if(isImportMenuOpen)
- return;
- isImportMenuOpen = true;
- document.body.classList.add("bytm-disable-scroll");
- document.querySelector("ytmusic-app")?.setAttribute("inert", "true");
- const menuBg = document.querySelector<HTMLElement>("#bytm-import-menu-bg");
- if(!menuBg)
- return warn("Couldn't find import menu background element");
- menuBg.style.visibility = "visible";
- menuBg.style.display = "block";
- }
- //#MARKER changelog menu
- let isChangelogMenuAdded = false;
- let isChangelogMenuOpen = false;
- /** Adds a changelog menu (hidden by default) */
- async function addChangelogMenu() {
- const menuBgElem = document.createElement("div");
- menuBgElem.id = "bytm-changelog-menu-bg";
- menuBgElem.classList.add("bytm-menu-bg");
- menuBgElem.ariaLabel = menuBgElem.title = t("close_menu_tooltip");
- menuBgElem.style.visibility = "hidden";
- menuBgElem.style.display = "none";
- menuBgElem.addEventListener("click", (e) => {
- if(isChangelogMenuOpen && (e.target as HTMLElement)?.id === "bytm-changelog-menu-bg") {
- closeChangelogMenu(e);
- if(menuBgElem.dataset.returnTo === "cfgMenu")
- openCfgMenu();
- }
- });
- document.body.addEventListener("keydown", (e) => {
- if(isChangelogMenuOpen && e.key === "Escape") {
- closeChangelogMenu(e);
- if(menuBgElem.dataset.returnTo === "cfgMenu")
- openCfgMenu();
- }
- });
- const menuContainer = document.createElement("div");
- menuContainer.ariaLabel = menuContainer.title = ""; // prevent bg title from propagating downwards
- menuContainer.classList.add("bytm-menu");
- menuContainer.id = "bytm-changelog-menu";
- //#SECTION title bar
- const headerElem = document.createElement("div");
- headerElem.classList.add("bytm-menu-header");
- const titleCont = document.createElement("div");
- titleCont.className = "bytm-menu-titlecont";
- titleCont.role = "heading";
- titleCont.ariaLevel = "1";
- const titleElem = document.createElement("h2");
- titleElem.className = "bytm-menu-title";
- titleElem.textContent = t("changelog_menu_title", scriptInfo.name);
- const closeElem = document.createElement("img");
- closeElem.classList.add("bytm-menu-close");
- closeElem.role = "button";
- closeElem.tabIndex = 0;
- closeElem.src = await getResourceUrl("img-close");
- closeElem.ariaLabel = closeElem.title = t("close_menu_tooltip");
- const closeChangelogMenuClicked = (e: MouseEvent | KeyboardEvent) => {
- closeChangelogMenu(e);
- if(menuBgElem.dataset.returnTo === "cfgMenu")
- openCfgMenu();
- };
- closeElem.addEventListener("click", closeChangelogMenuClicked);
- closeElem.addEventListener("keydown", (e) => e.key === "Enter" && closeChangelogMenuClicked(e));
- titleCont.appendChild(titleElem);
- headerElem.appendChild(titleCont);
- headerElem.appendChild(closeElem);
- //#SECTION body
- const getChangelogHtml = (async () => {
- try {
- const changelogMd = await getChangelogMd();
- return await parseMarkdown(changelogMd);
- }
- catch(err) {
- return `Error: ${err}`;
- }
- });
- const menuBodyElem = document.createElement("div");
- menuBodyElem.id = "bytm-changelog-menu-body";
- menuBodyElem.classList.add("bytm-menu-body");
- const textElem = document.createElement("div");
- textElem.id = "bytm-changelog-menu-text";
- textElem.classList.add("bytm-markdown-container");
- textElem.innerHTML = await getChangelogHtml();
- //#SECTION finalize
- menuBodyElem.appendChild(textElem);
- menuContainer.appendChild(headerElem);
- menuContainer.appendChild(menuBodyElem);
-
- menuBgElem.appendChild(menuContainer);
- document.body.appendChild(menuBgElem);
- const anchors = document.querySelectorAll<HTMLAnchorElement>("#bytm-changelog-menu-text a");
- for(const anchor of anchors) {
- anchor.ariaLabel = anchor.title = anchor.href;
- anchor.target = "_blank";
- }
- }
- /** Closes the changelog menu if it is open. If a bubbling event is passed, its propagation will be prevented. */
- function closeChangelogMenu(evt?: MouseEvent | KeyboardEvent) {
- if(!isChangelogMenuOpen)
- return;
- isChangelogMenuOpen = false;
- evt?.bubbles && evt.stopPropagation();
- const menuBg = document.querySelector<HTMLElement>("#bytm-changelog-menu-bg");
- if(!menuBg)
- return warn("Couldn't find changelog menu background element");
- menuBg.style.visibility = "hidden";
- menuBg.style.display = "none";
- }
- /**
- * Opens the changelog menu if it is closed
- * @param returnTo What menu to open after the changelog menu is closed
- */
- export async function openChangelogMenu(returnTo: "cfgMenu" | "exit" = "cfgMenu") {
- if(!isChangelogMenuAdded)
- await addChangelogMenu();
- isChangelogMenuAdded = true;
- if(isChangelogMenuOpen)
- return;
- isChangelogMenuOpen = true;
- document.body.classList.add("bytm-disable-scroll");
- document.querySelector("ytmusic-app")?.setAttribute("inert", "true");
- const menuBg = document.querySelector<HTMLElement>("#bytm-changelog-menu-bg");
- if(!menuBg)
- return warn("Couldn't find changelog menu background element");
- menuBg.dataset.returnTo = returnTo;
- menuBg.style.visibility = "visible";
- menuBg.style.display = "block";
- }
|