Greasy Fork is available in English.

ESJ Zone: Auto Expand Collapsed Chapters

Expand the collapsed, last read chapters (alternatively, all chapters) automatically.

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name               ESJ Zone: Auto Expand Collapsed Chapters
// @name:zh-TW         ESJ Zone:自動展開摺疊章節
// @name:zh-CN         ESJ Zone:自动展开折叠章节
// @description        Expand the collapsed, last read chapters (alternatively, all chapters) automatically.
// @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.1.4
// @license            MIT
// @match              https://www.esjzone.cc/detail/*.html
// @match              https://www.esjzone.me/detail/*.html
// @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
// @supportURL         https://greasyfork.org/scripts/487306/feedback
// ==/UserScript==

const LL = (function()
{
    const translations =
    {
        "en": {
            COMMAND: {
                SETTING: "Change Expand Setting",
            },
            SETTING: {
                TITLE: "Expand Setting",
                EXPAND_BEHAVIOUR: "Expand Behaviour",
                EXPAND_ALL: "Expand All Chapters",
                LAST_READ: "Last Read Chapters Only",
                CANCEL: "Cancel",
                SAVE: "Save",
            },
        },
        "zh-TW": {
            COMMAND: {
                SETTING: "更改展開設定",
            },
            SETTING: {
                TITLE: "展開設定",
                EXPAND_BEHAVIOUR: "展開行為",
                EXPAND_ALL: "展開所有章節",
                LAST_READ: "展開最後閱讀章節",
                CANCEL: "取消",
                SAVE: "儲存",
            },
        },
        "zh-CN": {
            COMMAND: {
                SETTING: "更改展開设定",
            },
            SETTING: {
                TITLE: "展開设定",
                EXPAND_BEHAVIOUR: "展开行为",
                EXPAND_ALL: "展开所有章节",
                LAST_READ: "展开最后阅读章节",
                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 ExpandOptions =
{
    EXPAND_ALL: "expand_all",
    LAST_READ: "last_read",
};

GM.registerMenuCommand(LL.COMMAND.SETTING(), showExpandSetting);

(async () =>
{
    const setting = await getExpandSetting();

    const elements = document.querySelectorAll("#chapterList details");
    for (const element of elements)
    {
        if ((setting === ExpandOptions.EXPAND_ALL) || (element.querySelector("p.active") !== null))
        {
            element.open = true;
        }
    }
})();

function getExpandSetting()
{
    return GM.getValue("expand", ExpandOptions.LAST_READ);
}

let settingOpened = false;

function showExpandSetting()
{
    if (settingOpened) { return Promise.resolve(new Error("Setting was already opened.")); }

    return new Promise(async (resolve) =>
    {
        const setting = await getExpandSetting();

        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 GM.setValue("expand", settings.get("expand"));

            window.dispatchEvent(new CustomEvent(`${EVENT_KEY}:hideModal`, { detail: `#${form.id}` }));
        });
        form.addEventListener("hide.bs.modal", () => resolve());
        form.addEventListener("hidden.bs.modal", () =>
        {
            form.remove();
            settingOpened = 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.SETTING.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 expandFormGroup = document.createElement("div");
        expandFormGroup.classList.add("form-group");

        const expandSelect = document.createElement("select");
        expandSelect.id = "expand-select";
        expandSelect.classList.add("form-control");
        expandSelect.name = "expand";

        const expandAllOption = document.createElement("option");
        expandAllOption.value = ExpandOptions.EXPAND_ALL;
        expandAllOption.selected = (setting === ExpandOptions.EXPAND_ALL);
        expandAllOption.innerText = LL.SETTING.EXPAND_ALL();

        const lastReadOption = document.createElement("option");
        lastReadOption.value = ExpandOptions.LAST_READ;
        lastReadOption.selected = (setting === ExpandOptions.LAST_READ);
        lastReadOption.innerText = LL.SETTING.LAST_READ();

        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.SETTING.CANCEL();

        const saveButton = document.createElement("button");
        saveButton.classList.add("btn", "btn-primary");
        cancelButton.type = "submit";
        saveButton.innerText = LL.SETTING.SAVE();

        modalHeader.append(modalTitle, closeButton);
        expandSelect.append(expandAllOption, lastReadOption);
        expandFormGroup.append(expandSelect);
        modalBody.append(expandFormGroup);
        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}` }));
        settingOpened = true;
    });
}

const PageScript = ({ 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");
    });
};

const scriptWrapper = document.createElement("div");
scriptWrapper.style.display = "none";
const shadowRoot = scriptWrapper.attachShadow({ mode: "closed" });
const script = document.createElement("script");
script.textContent = `(${PageScript})(${JSON.stringify({ EVENT_KEY })}); //# sourceURL=userscript://page/${encodeURI(GM.info.script.name)}.js`;
shadowRoot.append(script);
(document.body ?? document.head ?? document.documentElement).append(scriptWrapper);