- // ==UserScript==
- // @name Auto Dark Mode for ESJ Zone
- // @name:zh-TW ESJ Zone 自動黑暗模式
- // @name:zh-CN ESJ Zone 自动黑暗模式
- // @description Automatically switch the theme between light and dark, based on the browser’s color scheme preference.
- // @description:zh-TW 根據瀏覽器的佈景主題設定,自動從明亮和黑暗模式間切換。
- // @description:zh-CN 根据浏览器的布景主题设定,自动从明亮和黑暗模式间切换。
- // @icon https://icons.duckduckgo.com/ip3/www.esjzone.cc.ico
- // @author Jason Kwok
- // @namespace https://jasonhk.dev/
- // @version 1.0.9
- // @license MIT
- // @match https://www.esjzone.cc/*
- // @match https://www.esjzone.me/*
- // @run-at document-end
- // @grant GM.getValue
- // @grant GM.setValue
- // @grant GM.registerMenuCommand
- // @require https://unpkg.com/typesafe-i18n@5.26.2/dist/i18n.object.min.js
- // @require https://unpkg.com/uuid-random@1.3.2/uuid-random.min.js
- // @require https://update.greasyfork.org/scripts/494512/1373878/gm-inject.js
- // @supportURL https://greasyfork.org/scripts/488026/feedback
- // ==/UserScript==
-
- const LL = (function()
- {
- const translations =
- {
- "en": {
- COMMAND: {
- SETTINGS: "Change Theme Settings",
- },
- SETTINGS: {
- TITLE: "Theme Settings",
- LIGHT_THEME: "Light Theme",
- WHITE: "White",
- BLUE: "Blue",
- GREEN: "Green",
- GRAY: "Pink",
- LIGHT_GRAY: "Light Gray",
- DARK_THEME: "Dark Theme",
- BLACK: "Black",
- DARK_GRAY: "Dark Gray",
- CANCEL: "Cancel",
- SAVE: "Save",
- },
- },
- "zh-TW": {
- COMMAND: {
- SETTINGS: "更改主題設定",
- },
- SETTINGS: {
- TITLE: "主題設定",
- LIGHT_THEME: "明亮主題",
- WHITE: "白色",
- BLUE: "藍色",
- GREEN: "綠色",
- PINK: "粉紅色",
- LIGHT_GRAY: "淺灰色",
- DARK_THEME: "黑暗主題",
- BLACK: "黑色",
- DARK_GRAY: "深灰色",
- CANCEL: "取消",
- SAVE: "儲存",
- },
- },
- "zh-CN": {
- COMMAND: {
- SETTINGS: "更改主题设定",
- },
- SETTINGS: {
- TITLE: "主题设定",
- LIGHT_THEME: "明亮主题",
- WHITE: "白色",
- BLUE: "蓝色",
- GREEN: "绿色",
- PINK: "粉红色",
- LIGHT_GRAY: "浅灰色",
- DARK_THEME: "黑暗主题",
- BLACK: "黑色",
- DARK_GRAY: "深灰色",
- CANCEL: "取消",
- SAVE: "储存",
- },
- },
- };
-
- let locale = "en";
- for (let _locale of navigator.languages.map((language) => new Intl.Locale(language)))
- {
- if (_locale.language === "zh")
- {
- _locale = new Intl.Locale("zh", { region: _locale.maximize().region });
- }
- ;
- if (_locale.baseName in translations)
- {
- locale = _locale.baseName;
- break;
- }
- }
-
- return i18nObject(locale, translations[locale]);
- })();
-
- const EVENT_KEY = uuid();
-
- const query = matchMedia("(prefers-color-scheme: dark)");
-
- GM.registerMenuCommand(LL.COMMAND.SETTINGS(), async () =>
- {
- await showThemeSettings();
- updateTheme(query);
- });
-
- query.addEventListener("change", updateTheme);
- updateTheme(query);
-
- GM.injectPageScript(
- ({ EVENT_KEY }) =>
- {
- window.addEventListener(`${EVENT_KEY}:showModal`, ({ detail: selector }) =>
- {
- $(selector)
- .on("hide.bs.modal", (event) =>
- {
- event.target.dispatchEvent(new CustomEvent("hide.bs.modal", { ...event }));
- })
- .on("hidden.bs.modal", (event) =>
- {
- event.target.dispatchEvent(new CustomEvent("hidden.bs.modal", { ...event }));
- })
- .modal("show");
- });
-
- window.addEventListener(`${EVENT_KEY}:hideModal`, ({ detail: selector }) =>
- {
- $(selector)
- .modal("hide");
- });
- },
- { EVENT_KEY });
-
- function getLightTheme()
- {
- return GM.getValue("light_theme", "mycolor-0");
- }
-
- function getDarkTheme()
- {
- return GM.getValue("dark_theme", "mycolor-1");
- }
-
- function setThemeSettings(lightTheme, darkTheme)
- {
- return Promise.all([GM.setValue("light_theme", lightTheme), GM.setValue("dark_theme", darkTheme)]);
- }
-
- function getExpectedTheme(isDarkMode)
- {
- return isDarkMode ? getDarkTheme() : getLightTheme();
- }
-
- function getCurrentTheme()
- {
- return document.querySelector(".customizer-color-switch [id^=mycolor-].active")?.id ?? "mycolor-0";
- }
-
- function setTheme(name)
- {
- document.querySelector(`.customizer-color-switch #${name}`).click();
- }
-
- async function updateTheme({ matches: isDarkMode })
- {
- const expectedTheme = await getExpectedTheme(isDarkMode);
- if (getCurrentTheme() !== expectedTheme)
- {
- setTheme(expectedTheme);
- }
- }
-
- let settingsOpened = false;
-
- function showThemeSettings()
- {
- if (settingsOpened) { return Promise.reject(new Error("Settings was already opened.")); }
-
- return new Promise(async (resolve) =>
- {
- const [lightTheme, darkTheme] = await Promise.all([getLightTheme(), getDarkTheme()]);
-
- const form = document.createElement("form");
- form.id = uuid();
- form.classList.add("modal", "fade");
- form.addEventListener("submit", async (event) =>
- {
- event.preventDefault();
-
- const settings = new FormData(form);
- await setThemeSettings(settings.get("light_theme"), settings.get("dark_theme"));
-
- window.dispatchEvent(new CustomEvent(`${EVENT_KEY}:hideModal`, { detail: `#${form.id}` }));
- });
- form.addEventListener("hide.bs.modal", () => resolve());
- form.addEventListener("hidden.bs.modal", () =>
- {
- form.remove();
- settingsOpened = false;
- });
-
- const modalDialog = document.createElement("div");
- modalDialog.classList.add("modal-dialog");
-
- const modalContent = document.createElement("div");
- modalContent.classList.add("modal-content");
-
- const modalHeader = document.createElement("div");
- modalHeader.classList.add("modal-header");
-
- const modalTitle = document.createElement("h4");
- modalTitle.classList.add("modal-title");
- modalTitle.innerText = LL.SETTINGS.TITLE();
-
- const closeButton = document.createElement("button");
- closeButton.classList.add("close");
- closeButton.type = "button";
- closeButton.dataset.dismiss = "modal";
- closeButton.innerHTML = `<span aria-hidden="true">×</span>`;
-
- const modalBody = document.createElement("div");
- modalBody.classList.add("modal-body");
-
- const lightThemeFormGroup = document.createElement("div");
- lightThemeFormGroup.classList.add("form-group");
-
- const lightThemeLabel = document.createElement("label");
- lightThemeLabel.htmlFor = "light-theme-select";
- lightThemeLabel.innerText = LL.SETTINGS.LIGHT_THEME();
-
- const lightThemeSelect = document.createElement("select");
- lightThemeSelect.id = "light-theme-select";
- lightThemeSelect.classList.add("form-control");
- lightThemeSelect.name = "light_theme";
-
- const whiteThemeOption = document.createElement("option");
- whiteThemeOption.value = "mycolor-0";
- whiteThemeOption.selected = (lightTheme === "mycolor-0");
- whiteThemeOption.innerText = LL.SETTINGS.WHITE();
-
- const blueThemeOption = document.createElement("option");
- blueThemeOption.value = "mycolor-2";
- blueThemeOption.selected = (lightTheme === "mycolor-2");
- blueThemeOption.innerText = LL.SETTINGS.BLUE();
-
- const greenThemeOption = document.createElement("option");
- greenThemeOption.value = "mycolor-3";
- greenThemeOption.selected = (lightTheme === "mycolor-3");
- greenThemeOption.innerText = LL.SETTINGS.GREEN();
-
- const pinkThemeOption = document.createElement("option");
- pinkThemeOption.value = "mycolor-4";
- pinkThemeOption.selected = (lightTheme === "mycolor-4");
- pinkThemeOption.innerText = LL.SETTINGS.PINK();
-
- const lightGrayThemeOption = document.createElement("option");
- lightGrayThemeOption.value = "mycolor-5";
- lightGrayThemeOption.selected = (lightTheme === "mycolor-5");
- lightGrayThemeOption.innerText = LL.SETTINGS.LIGHT_GRAY();
-
- const darkThemeFormGroup = document.createElement("div");
- darkThemeFormGroup.classList.add("form-group");
-
- const darkThemeLabel = document.createElement("label");
- darkThemeLabel.htmlFor = "dark-theme-select";
- darkThemeLabel.innerText = LL.SETTINGS.DARK_THEME();
-
- const darkThemeSelect = document.createElement("select");
- darkThemeSelect.id = "dark-theme-select";
- darkThemeSelect.classList.add("form-control");
- darkThemeSelect.name = "dark_theme";
-
- const blackThemeOption = document.createElement("option");
- blackThemeOption.value = "mycolor-1";
- blackThemeOption.selected = (darkTheme === "mycolor-1");
- blackThemeOption.innerText = LL.SETTINGS.BLACK();
-
- const darkGrayThemeOption = document.createElement("option");
- darkGrayThemeOption.value = "mycolor-6";
- darkGrayThemeOption.selected = (darkTheme === "mycolor-6");
- darkGrayThemeOption.innerText = LL.SETTINGS.DARK_GRAY();
-
- const modalFooter = document.createElement("div");
- modalFooter.classList.add("modal-footer");
-
- const cancelButton = document.createElement("button");
- cancelButton.classList.add("btn", "btn-default");
- cancelButton.type = "button";
- cancelButton.dataset.dismiss = "modal";
- cancelButton.innerText = LL.SETTINGS.CANCEL();
-
- const saveButton = document.createElement("button");
- saveButton.classList.add("btn", "btn-primary");
- cancelButton.type = "submit";
- saveButton.innerText = LL.SETTINGS.SAVE();
-
- modalHeader.append(modalTitle, closeButton);
- lightThemeSelect.append(whiteThemeOption, blueThemeOption, greenThemeOption, pinkThemeOption, lightGrayThemeOption);
- lightThemeFormGroup.append(lightThemeLabel, lightThemeSelect);
- darkThemeSelect.append(blackThemeOption, darkGrayThemeOption);
- darkThemeFormGroup.append(darkThemeLabel, darkThemeSelect);
- modalBody.append(lightThemeFormGroup, darkThemeFormGroup);
- modalFooter.append(cancelButton, saveButton);
- modalContent.append(modalHeader, modalBody, modalFooter);
- modalDialog.append(modalContent);
- form.append(modalDialog);
- document.body.append(form);
-
- window.dispatchEvent(new CustomEvent(`${EVENT_KEY}:showModal`, { detail: `#${form.id}` }));
- settingsOpened = true;
- });
- }