Greasy Fork

Greasy Fork is available in English.

Auto Dark Mode for Penana Mobile

Automatically switch the theme between light and dark, based on the browser’s color scheme preference.

当前为 2024-02-22 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name               Auto Dark Mode for Penana Mobile
// @name:zh-TW         Penana 手機版自動黑暗模式
// @description        Automatically switch the theme between light and dark, based on the browser’s color scheme preference.
// @description:zh-TW  根據瀏覽器的佈景主題設定,自動從明亮和黑暗模式間切換。
// @icon               https://wsrv.nl/?url=https://static2.penana.com/img/mobile/app-icon/ios/128.png
// @author             Jason Kwok
// @namespace          https://jasonhk.dev/
// @version            1.0.0
// @license            MIT
// @match              https://m.penana.com/*
// @match              https://android.penana.com/*
// @run-at             document-idle
// @inject-into        page
// @grant              GM.getValue
// @grant              GM.setValue
// @grant              GM.registerMenuCommand
// @require            https://unpkg.com/[email protected]/dist/i18n.object.min.js
// ==/UserScript==

const LL = (function()
{
    const translations =
    {
        "en": {
            COMMAND: {
                SETTING: "Change Light Theme Setting",
            },
            SETTING: {
                SUBTITLE: "Light Theme Setting",
                LIGHT: "Light",
                WHITE: "White",
                BROWN: "Brown",
                SAVE: "Save",
            },
        },
        "zh-TW": {
            COMMAND: {
                SETTING: "更改明亮主題設定",
            },
            SETTING: {
                SUBTITLE: "明亮主題設定",
                LIGHT: "明亮",
                WHITE: "白色",
                BROWN: "褐色",
                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 isGreasemonkey = (GM.info.scriptHandler === "Greasemonkey");

const interval = setInterval(() =>
{
    if (!window.eval(`"setTheme" in window`)) { return; }
    clearInterval(interval);

    if (isGreasemonkey)
    {
        window.getCookie = window.eval("getCookie");
        window.setCookie = window.eval("setCookie");
        window.setTheme = window.eval("setTheme");
    }

    const query = matchMedia("(prefers-color-scheme: dark)");

    query.addEventListener("change", updateTheme);
    updateTheme(query);

    GM.registerMenuCommand(LL.COMMAND.SETTING(), async () =>
    {
        await showLightThemeSetting();
        updateTheme(query);
    });

    function getLightTheme()
    {
        return GM.getValue("light_theme", "lighttheme");
    }

    async function getExpectedTheme(isDarkMode)
    {
        return isDarkMode ? "darktheme" : getLightTheme();
    }

    function getCurrentTheme()
    {
        switch (true)
        {
            case document.body.classList.contains("darktheme"):
                return "darktheme";
            case document.body.classList.contains("whitetheme"):
                return "whitetheme";
            case document.body.classList.contains("browntheme"):
                return "browntheme";
            default:
                return "lighttheme";
        }
    }

    async function updateTheme({ matches: isDarkMode })
    {
        const expectedTheme = await getExpectedTheme(isDarkMode);
        if (getCurrentTheme() !== expectedTheme)
        {
            setTheme(expectedTheme);
            setCookie("darktheme", expectedTheme, 9999);
        }
    }

    function showLightThemeSetting()
    {
        return new Promise(async (resolve) =>
        {
            const popupWrapper = document.createElement("div");
            popupWrapper.classList.add("popupwrap", "narrowpopup", "scroll");

            const popupMask = document.createElement("div");
            popupMask.classList.add("mask", "pseudoclose");

            const popup = document.createElement("div");
            popup.classList.add("popup", "logregpopup", "popupbox", "invitebox", "scroll");

            const closeButton = document.createElement("a");
            closeButton.classList.add("close", "ignorelink", "newclose", "nonsticky", "floatright");
            closeButton.innerText = "×";

            const contents = document.createElement("div");
            contents.classList.add("padding10px");

            const subtitle = document.createElement("span");
            subtitle.classList.add("popup_subtitle", "font15px");
            subtitle.innerText = LL.SETTING.SUBTITLE();

            const form = document.createElement("form");
            form.style = "margin-top: 20px;";
            form.addEventListener("submit", async (event) =>
            {
                event.preventDefault();

                const options = new FormData(form);
                await GM.setValue("light_theme", options.get("light_theme"));

                popupWrapper.style = "display: none;";
            });

            const lightThemeLabelWrapper = document.createElement("p");

            const lightThemeLabel = document.createElement("label");

            const lightThemeRadio = document.createElement("input");
            lightThemeRadio.classList.add("with-gap");
            lightThemeRadio.name = "light_theme";
            lightThemeRadio.type = "radio";
            lightThemeRadio.required = true;
            lightThemeRadio.value = "lighttheme";

            const lightThemeText = document.createElement("span");
            lightThemeText.innerText = LL.SETTING.LIGHT();

            const whiteThemeLabelWrapper = document.createElement("p");

            const whiteThemeLabel = document.createElement("label");

            const whiteThemeRadio = document.createElement("input");
            whiteThemeRadio.classList.add("with-gap");
            whiteThemeRadio.name = "light_theme";
            whiteThemeRadio.type = "radio";
            whiteThemeRadio.required = true;
            whiteThemeRadio.value = "whitetheme";

            const whiteThemeText = document.createElement("span");
            whiteThemeText.innerText = LL.SETTING.WHITE();

            const brownThemeLabelWrapper = document.createElement("p");

            const brownThemeLabel = document.createElement("label");

            const brownThemeRadio = document.createElement("input");
            brownThemeRadio.classList.add("with-gap");
            brownThemeRadio.name = "light_theme";
            brownThemeRadio.type = "radio";
            brownThemeRadio.required = true;
            brownThemeRadio.value = "browntheme";

            const brownThemeText = document.createElement("span");
            brownThemeText.innerText = LL.SETTING.BROWN();

            const saveButton = document.createElement("button");
            saveButton.classList.add("btn", "waves-effect", "waves-light");
            saveButton.innerHTML = `${LL.SETTING.SAVE()} <i class="material-icons right">save</i>`;

            lightThemeLabel.append(lightThemeRadio, lightThemeText);
            lightThemeLabelWrapper.append(lightThemeLabel);
            whiteThemeLabel.append(whiteThemeRadio, whiteThemeText);
            whiteThemeLabelWrapper.append(whiteThemeLabel);
            brownThemeLabel.append(brownThemeRadio, brownThemeText);
            brownThemeLabelWrapper.append(brownThemeLabel);
            form.append(lightThemeLabelWrapper, whiteThemeLabelWrapper, brownThemeLabelWrapper, saveButton);
            contents.append(subtitle, form);
            popup.append(closeButton, contents);
            popupWrapper.append(popupMask, popup);
            document.body.append(popupWrapper);

            const theme = await getLightTheme();
            switch (theme)
            {
                case "lighttheme":
                    lightThemeRadio.checked = true;
                    break;
                case "whitetheme":
                    whiteThemeRadio.checked = true;
                    break;
                case "browntheme":
                    brownThemeRadio.checked = true;
                    break;
            }

            const observer = new MutationObserver((records) =>
            {
                observer.disconnect();

                resolve();
                popupWrapper.remove();
            });

            observer.observe(popupWrapper, { attributes: true, attributeFilter: ["style"] });
        });
    }
}, 100);