mike's lib

快速處理畫面操作

Bu script direkt olarak kurulamaz. Başka scriptler için bir kütüphanedir ve meta yönergeleri içerir // @require https://update.greasyfork.org/scripts/570074/1777131/mike%27s%20lib.js

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         mike's lib
// @namespace    小說
// @version      2026-03-18
// @description  工具函式庫
// @author       mike
// @match        http*://*/*
// @grant        none
// ==/UserScript==

(function (global) {
    'use strict';

    // 等待物件 func
    const waitElement = (selector, callback) => {
        const interval = setInterval(() => {
            const element = document.querySelector(selector);
            if (element) {
                clearInterval(interval);
                callback(element);
            }
        }, 100);
    };

    // 移除物件
    const removeElement = (baseObj, removeTarget) => {
        let beRemoveObj = baseObj.querySelector(removeTarget);
        if (beRemoveObj) {
            beRemoveObj.remove();
        }
    };
    
    // 等並刪除
    const waitAndKillElement = (selector) => {
        waitElement(selector, () => {
            removeElement(document, selector);
        });
    };

    // 重置寬高
    const resetMarginPadding = (element) => {
        element.style.margin = '0px';
        element.style.padding = '0px';
    };
    
    // 取得 平台名稱
    const getPlatform = (platformList) => {
        const pathname = window.location.pathname // 用網址查
        const findPlatform = platformList.find(item => {
            const [key, value] = Object.entries(item)[0];
            return pathname.includes(key);
        });
        return Object.values(findPlatform)[0]
    }

    // 製作快速跳轉浮窗
    // initChapterNavigator({selectors: ['.entry-content', 'p'], chapterStartsWith: ['[', '【']});
    const initChapterNavigator = ({
        selectors,
        chapterStartsWith
    }) => {

        if (
            !Array.isArray(selectors) ||
            selectors.length === 0 ||
            selectors.some(s => typeof s !== 'string' || !s.trim())
        ) {
            console.log('[ChapterNavigator] selectors 必須為非空字串陣列');
            return;
        }

        if (
            !Array.isArray(chapterStartsWith) ||
            chapterStartsWith.length === 0 ||
            chapterStartsWith.some(s => typeof s !== 'string' || !s)
        ) {
            console.log('[ChapterNavigator] chapterStartsWith 必須為非空字串陣列');
            return;
        }

        const queryBySelectors = () => {
            let current = document;

            for (let i = 0; i < selectors.length; i++) {
                if (!current) return [];

                if (i === selectors.length - 1) {
                    return Array.from(current.querySelectorAll(selectors[i]));
                }

                current = current.querySelector(selectors[i]);
            }

            return [];
        };

        const areaKeyList = () => {
            return queryBySelectors().filter(el => {
                const text = el.textContent.trim();
                return chapterStartsWith.some(prefix =>
                    text.startsWith(prefix)
                );
            });
        };

        const gotoText = (findText) => {
            areaKeyList().forEach(el => {
                if (el.textContent.includes(findText)) {
                    el.scrollIntoView({
                        behavior: 'smooth',
                        block: 'start'
                    });
                }
            });
        };

        const getChapterList = () =>
            areaKeyList().map(el => el.textContent);

        const createFloatingBox = (chapterList) => {
            if (document.getElementById('floatingBox')) {
                return;
            }

            const floatingBox = document.createElement('div');
            floatingBox.id = 'floatingBox';

            Object.assign(floatingBox.style, {
                position: 'fixed',
                right: '10px',
                bottom: '10px',
                width: '150px',
                height: '450px',
                overflow: 'auto',
                padding: '10px',
                backgroundColor: '#333',
                color: 'white',
                borderRadius: '8px',
                zIndex: '1000'
            });

            chapterList.forEach(text => {
                const btn = document.createElement('button');

                Object.assign(btn.style, {
                    width: '100%',
                    margin: '2px 0',
                    padding: '10px 5px',
                    backgroundColor: '#f44336',
                    color: 'white',
                    border: 'none',
                    borderRadius: '4px',
                    cursor: 'pointer'
                });

                btn.textContent = text;
                btn.onclick = () => gotoText(text);

                floatingBox.appendChild(btn);
            });

            document.body.appendChild(floatingBox);
        };

        const chapterList = getChapterList();

        if (!chapterList.length) {
            console.log('[ChapterNavigator] 未找到任何符合規則的章節');
            return;
        }

        createFloatingBox(chapterList);
    };

    // ⭐ 關鍵:對外輸出
    global.mikeLib = {
        waitElement,
        removeElement,
        waitAndKillElement,
        resetMarginPadding,
        getPlatform,
        initChapterNavigator
    };

})(window);