wnacg 觀看優化

只設置匹配 https://www.wnacg.com/ 漫畫自動載入所有頁面至同一頁, 純黑背景色, 圖像最大寬度縮小

2023-08-12 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

// ==UserScript==
// @name            wnacg 觀看優化
// @version         0.0.4
// @author          HentiSaru
// @description     只設置匹配 https://www.wnacg.com/  漫畫自動載入所有頁面至同一頁, 純黑背景色, 圖像最大寬度縮小

// @match           *://*.wnacg.com/photos-view-id-*.html
// @icon            https://www.wnacg.com/favicon.ico

// @license         MIT
// @namespace       https://greasyfork.org/users/989635

// @run-at          document-start
// @grant           GM_setValue
// @grant           GM_getValue
// @grant           GM_addStyle

// @require         https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js
// @require         https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js
// @require         https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js
// ==/UserScript==

var modal, set_value;
function GetSettings() {
    let Settings = GM_getValue("Settings", null);
    if (Settings === null) {
        Settings = [{"BC":"#000000", "ULS":"0rem", "BW":"100%", "MW":"60%", "BH":"auto", "MH":"auto"}];
    }
    return Settings[0];
}

(function () {
    ImportStyle();
    let photo_box, current, total, interval = setInterval(() => {
        photo_box = document.getElementById("photo_body");
        current = document.querySelector("span.newpagelabel b");
        total = document.querySelectorAll("select option");
        if (photo_box && current && total.length > 0) {
            document.body.classList.add("CustomBody");
            photo_box.classList.remove("photo_body");
            ImageGeneration(photo_box, current, total);
            AdReplace();
            clearInterval(interval);
        }
    }, 500);
    HotKey();
})();

async function ImageGeneration(box, current, total) {
    // 總頁數 = 總共頁數 - 當前頁數
    var total_pages = total.length - parseInt(current.textContent, 10);
    let NLink, img, dom, parser = new DOMParser();
    /* -------------------------------------------------------------- */
    /* 觀察者 */
    var observer = new IntersectionObserver(function (observed) {
        observed.forEach(function (entry) {
            if (entry.isIntersecting) {history.pushState(null, null, entry.target.alt)}
        });
    }, {threshold: 0.4});
    /* 圖像渲染 */
    function ReactRender({ OLink, src }) {
        return React.createElement("img", {
            className: "ImageOptimization",
            src: src,
            alt: OLink,
            ref: function (img) {
                if (img) {observer.observe(img)}
            }
        });
    }
    /* 獲取下一頁資訊 */
    async function NextPage(link) {
        if (total_pages > 1) {
            fetch(link)
                .then(response => response.text())
                .then(html => {
                    dom = parser.parseFromString(html, "text/html").getElementById("photo_body");
                    NLink = dom.querySelector("a").href;
                    img = dom.querySelector("img").src;
                    ReactDOM.render(React.createElement(ReactRender, { OLink: link, src: img }), box.appendChild(document.createElement("div")));
                    setTimeout(()=>{
                        NextPage(NLink);
                        total_pages--;
                    }, 600)
                })
        }
    }/* -------------------------------------------------------------- */
    document.title = document.title.split(" - ")[1]; // 變換 title 格式
    NLink = box.querySelector("a").href; // 獲取下一頁連結
    img = box.querySelector("img").src; // 獲取圖像連結
    /* 重新渲染第一章圖 */
    ReactDOM.render(React.createElement(ReactRender, { OLink: window.location.href, src: img }), box);
    document.querySelector("p.result").scrollIntoView(); // 回到最上方
    HeavyTypography(); // 調整排版樣式
    NextPage(NLink); // 請求下一頁
}

/* 廣告隱藏 */
async function AdReplace() {
    const breadHTML = { __html: document.querySelector(".png.bread").innerHTML };
    ReactDOM.render(
        React.createElement("div", { dangerouslySetInnerHTML: breadHTML }
        ),
        document.getElementById("bread")
    );
}

/* 重新調整排版 */
async function HeavyTypography() {
    // box.replaceChildren(); 刪除子節點方法
    try {
        document.getElementById("bodywrap").remove();
        document.querySelector("div.tocaowrap").classList.add("TailStyle");
        document.querySelector(".newpagewrap").remove();
        document.querySelector(".footer.wrap").remove();
    } catch { }
}

/* 熱鍵開啟菜單 */
async function HotKey() {
    document.addEventListener("keydown", function(event) {
        if (event.shiftKey && $('.modal-background').length < 1) {
            event.preventDefault();
            SettingInterface();
        }
    })
}

/* 數據解析 */
function Analyze(value) {
    if (value === "auto") {
        return [9, "auto"];
    } else if (value.endsWith("rem") || value.endsWith("%")) {
        return [parseInt(value), value];
    } else {
        return [value, "color"];
    }
}
/* 設定介面 */
async function SettingInterface() {
    let array = [], save = {}, showElement, id, value;
    set = GetSettings(); // 用這種笨方式避免順序出錯
    for (const result of [set.BC, set.ULS, set.BW, set.MW, set.BH, set.MH]) {
        value = Analyze(result);
        array.push({"key":value[0], "value":value[1]});
    }
    modal = `
        <div class="modal-background">
            <div class="modal-interface">
                <h1 style="margin-bottom: 1rem; font-size: 1.3rem;">圖像設置</h1>
                <p>
                    <Cins>上下間距</Cins><input type="range" id="ULS" class="slider" min="0" max="100" value="${array[1].key}" step="1">
                    <span class="Cshow">${array[1].value}</span>
                </p>
                <br>
                <p>
                    <Cins>基本寬度</Cins><input type="range" id="BW" class="slider" min="9" max="100" value="${array[2].key}" step="1">
                    <span class="Cshow">${array[2].value}</span>
                </p>
                <br>
                <p>
                    <Cins>最大寬度</Cins><input type="range" id="MW" class="slider" min="9" max="100" value="${array[3].key}" step="1">
                    <span class="Cshow">${array[3].value}</span>
                </p>
                <br>
                <p>
                    <Cins>基本高度</Cins><input type="range" id="BH" class="slider" min="9" max="100" value="${array[4].key}" step="1">
                    <span class="Cshow">${array[4].value}</span>
                </p>
                <br>
                <p>
                    <Cins>最大高度</Cins><input type="range" id="MH" class="slider" min="9" max="100" value="${array[5].key}" step="1">
                    <span class="Cshow">${array[5].value}</span>
                </p>
                <br>
                <p>
                    <Cins>背景顏色</Cins><input type="color" id="BC" class="color" value="${array[0].key}">
                    <span style="margin-right: 17.9rem;"></span><button id="save_set" class="button-sty">保存設置</button>
                </p>
            </div>
        </div>
    `
    $(document.body).append(modal);
    $("#BC").on("input", event => {
        value = $(event.target).val();
        id = event.target.id;
        StyleChange(id, value);
    });
    $("#ULS").on("input", event => {
        showElement = $(event.target).next(".Cshow");
        value = $(event.target).val();
        id = event.target.id;
        StyleChange(id, `${value}rem`);
        showElement.text(`${value}rem`);
    });
    $("#BW, #MW").on("input", event => {
        showElement = $(event.target).next(".Cshow");
        value = $(event.target).val();
        id = event.target.id;

        if (value === "9") {
            StyleChange(id, "auto");
            showElement.text("auto");
        } else {
            StyleChange(id, `${value}%`);
            showElement.text(`${value}%`);
        }
    });
    $("#BH, #MH").on("input", event => {
        showElement = $(event.target).next(".Cshow");
        value = $(event.target).val();
        id = event.target.id;

        if (value === "9") {
            StyleChange(id, "auto");
            showElement.text("auto");
        }  else {
            StyleChange(id, `${value}rem`);
            showElement.text(`${value}rem`);
        }
    });
    $('#save_set').on("click", function() {
        $(".modal-interface").find("input").each(function() {
            id = $(this).attr('id');
            value = $(this).val();
            if (id === "ULS") {
                save[id] = `${value}rem`;
            } else if (id === "BW" || id === "MW") {
                if (value === "9") {save[id] = "auto"} else {save[id] = `${value}%`}
            } else if (id === "BH" || id === "MH") {
                if (value === "9") {save[id] = "auto"} else {save[id] = `${value}rem`}
            } else {save[id] = value}
        });
        array = [save]
        GM_setValue("Settings", array);
        $('.modal-background').remove();
    });
}

/* css 樣式導入 */
async function ImportStyle() {
    set = GetSettings();
    GM_addStyle(`
        .ImageOptimization {
            display: block;
            margin: ${set.ULS} auto;
            width: ${set.BW};
            height: ${set.BH};
            max-width: ${set.MW};
            max-height: ${set.MH};
        }
        .CustomBody {
            overflow-x: visible !important;
            background: ${set.BC};
        }
        .TailStyle {
            width: auto;
            max-width: ${set.MW};
        }
        .modal-background {
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            display: flex;
            z-index: 9999;
            position: fixed;
            overflow: auto;
            pointer-events: none;
            background-color: rgba(0, 0, 0, 0.1);
        }
        .modal-interface {
            margin: auto;
            color: #3d8fe7;
            padding: 1% 2%;
            border-radius: 5px;
            background-color: #f3f3f3;
            border: 2px solid #c0d8fc;
            pointer-events: auto;
        }
        .slider {
            width: 21rem;
            cursor: pointer;
        }
        .color {
            width: 4rem;
            cursor: pointer;
        }
        .Cshow {
            font-size: 1.25rem;
            margin: auto 1rem;
            font-weight: bold;
        }
        .button-sty {
            color: #ffffff;
            font-size: 1rem;
            padding: 0.3rem;
            font-weight: bold;
            border-radius: 5px;
            background-color: #3d8fe7;
            border: 2px solid #f3f3f3;
        }
        .button-sty:hover,
        .button-sty:focus {
            color: #c0d8fc;
            cursor: pointer;
            text-decoration: none;
        }
        p {
            display: flex;
            align-items: center;
            white-space: nowrap;
        }
        Cins {
            font-size: 1.2rem;
            font-weight: bold;
            padding: 1rem;
            margin-right: 1rem;
        }
    `);
}

/* 變更樣式 (這是個爛方式) */
const styleRules = {
    ULS: value => `.ImageOptimization {margin: ${value} auto;}`,
    BW: value => `.ImageOptimization {width: ${value}}`,
    BH: value => `.ImageOptimization {height: ${value}}`,
    MW: value => `.ImageOptimization {max-width: ${value}}`,
    MH: value => `.ImageOptimization {max-height: ${value}}`,
    BC: value => `.CustomBody {background: ${value}}`
};
async function StyleChange(id, value) {
    GM_addStyle(styleRules[id](value));
}