ColaManga 瀏覽增強

隱藏廣告內容,提昇瀏覽體驗。自訂背景顏色,圖片大小調整。當圖片載入失敗時,自動重新載入圖片。提供熱鍵功能:[← 上一頁]、[下一頁 →]、[↑ 自動上滾動]、[↓ 自動下滾動]。當用戶滾動到頁面底部時,自動跳轉到下一頁。

// ==UserScript==
// @name         ColaManga 瀏覽增強
// @name:zh-TW   ColaManga 瀏覽增強
// @name:zh-CN   ColaManga 浏览增强
// @name:en      ColaManga Browsing Enhance
// @version      2025.09.21-Beta
// @author       Canaan HS
// @description       隱藏廣告內容,提昇瀏覽體驗。自訂背景顏色,圖片大小調整。當圖片載入失敗時,自動重新載入圖片。提供熱鍵功能:[← 上一頁]、[下一頁 →]、[↑ 自動上滾動]、[↓ 自動下滾動]。當用戶滾動到頁面底部時,自動跳轉到下一頁。
// @description:zh-TW 隱藏廣告內容,提昇瀏覽體驗。自訂背景顏色,圖片大小調整。當圖片載入失敗時,自動重新載入圖片。提供熱鍵功能:[← 上一頁]、[下一頁 →]、[↑ 自動上滾動]、[↓ 自動下滾動]。當用戶滾動到頁面底部時,自動跳轉到下一頁。
// @description:zh-CN 隐藏广告内容,提昇浏览体验。自定义背景颜色,调整图片大小。当图片载入失败时,自动重新载入图片。提供快捷键功能:[← 上一页]、[下一页 →]、[↑ 自动上滚动]、[↓ 自动下滚动]。当用户滚动到页面底部时,自动跳转到下一页。
// @description:en    Hide advertisement content, enhance browsing experience. Customize background color, adjust image size. Automatically reload images when they fail to load. Provide shortcut key functionalities: [← Previous Page], [Next Page →], [↑ Auto Scroll Up], [↓ Auto Scroll Down]. Automatically jump to the next page when users scroll to the bottom of the page.

// @match        *://www.colamanga.com/manga-*/
// @match        *://www.colamanga.com/manga-*/*/*.html
// @icon         https://www.colamanga.com/favicon.png

// @license      MPL-2.0
// @namespace    https://gf.qytechs.cn/users/989635
// @supportURL   https://github.com/Canaan-HS/MonkeyScript/issues

// @require      https://update.gf.qytechs.cn/scripts/487608/1661432/SyntaxLite_min.js

// @grant        GM_setValue
// @grant        GM_getValue

// @run-at       document-start
// ==/UserScript==

(function () {
    /* 臨時的自定義 (當 Enable = false 時, 其餘的設置將無效) */
    const Config = {
        BGColor: {
            Enable: true,
            Color: "#595959",
        },
        AutoTurnPage: { // 自動翻頁
            Enable: true,
            Mode: 3, // 1 = 快速 | 2 = 一般無盡 | 3 = 優化無盡
        },
        RegisterHotkey: { // 快捷功能
            Enable: true,
            Function: { // 移動端不適用以下配置
                TurnPage: true, // 翻頁
                AutoScroll: true, // 自動滾動
                KeepScroll: true, // 換頁繼續滾動
                ManualScroll: false, // 手動滾動啟用時, 將會變成點擊一次, 根據視點翻一頁 且 自動滾動會無效
            }
        }
    };
    const Control = {
        ScrollPixels: 2,
        WaitPicture: 1e3,
        BlockListener: new Set(["auxclick", "mousedown", "pointerup", "pointerdown", "dState", "touchstart", "unhandledrejection"]),
        IdList: {
            Title: "CME_Title",
            Iframe: "CME_Iframe",
            Block: "CME_Block-Ads",
            Menu: "CME_Menu-Style",
            Image: "CME_Image-Style",
            Scroll: "CME_Scroll-Hidden",
            ChildS: "CME_Child-Scroll-Hidden"
        }
    };
    const Param = {
        Body: null,
        ContentsPage: null,
        HomePage: null,
        PreviousLink: null,
        NextLink: null,
        MangaList: null,
        BottomStrip: null,
        Up_scroll: false,
        Down_scroll: false,
        IsFinalPage: false,
        IsMangaPage: Lib.$url.endsWith("html"),
        IsMainPage: window.self === window.parent
    };
    (async () => {
        if (!Param.IsMangaPage) return;
        Lib.addStyle(`
        html {pointer-events: none !important;}
        div[style*='position'] {display: none !important;}
        .mh_wrap a,
        .mh_readend a,
        span.mh_btn:not(.contact),
        #${Control.IdList.Iframe} {
            pointer-events: auto !important;
        }
    `, Control.IdList.Block);
        const OriginListener = EventTarget.prototype.addEventListener;
        const Block = Control.BlockListener;
        EventTarget.prototype.addEventListener = new Proxy(OriginListener, {
            apply(target, thisArg, args) {
                const [type, listener, options] = args;
                if (Block.has(type)) return;
                return target.apply(thisArg, args);
            }
        });
        const iframe = `iframe:not(#${Control.IdList.Iframe})`;
        const AdCleanup = () => {
            Lib.$qa(iframe).forEach(ad => ad.remove());
            Lib.body?.$qa("script").forEach(ad => ad.remove());
            requestIdleCallback(AdCleanup, {
                timeout: 300
            });
        };
        AdCleanup();
    })();
    const Tools = (() => {
        const idWhiteList = new Set(Object.values(Control.IdList));
        const storage = (key, value = null) => {
            return value != null ? Lib.session(key, {
                value: value
            }) : Lib.session(key);
        };
        const topDetected = Lib.$throttle(() => {
            Param.Up_scroll = Lib.sY == 0 ? (storage("scroll", false), false) : true;
        }, 1e3);
        const isTheBottom = () => Lib.sY + Lib.iH >= document.documentElement.scrollHeight;
        const detectSkip = Config.RegisterHotkey.Function.KeepScroll && Config.AutoTurnPage.Mode === 1;
        const bottomDetected = Lib.$throttle(() => {
            if (detectSkip) return;
            Param.Down_scroll = isTheBottom() ? (storage("scroll", false), false) : true;
        }, 1e3);
        return {
            storage: storage,
            getSet: () => {
                return Lib.getV("Style", {
                    BG_Color: "#595959",
                    Img_Bw: "auto",
                    Img_Mw: "100%"
                });
            },
            getNodes(root) {
                const nodes = [];
                function task(root2) {
                    const tree = document.createTreeWalker(root2, NodeFilter.SHOW_ELEMENT, {
                        acceptNode: node => {
                            if (idWhiteList.has(node.id)) {
                                return NodeFilter.FILTER_REJECT;
                            }
                            return NodeFilter.FILTER_ACCEPT;
                        }
                    });
                    while (tree.nextNode()) {
                        nodes.push(tree.currentNode);
                    }
                }
                task(root.head);
                task(root.body);
                return nodes;
            },
            autoScroll(move) {
                requestAnimationFrame(() => {
                    if (Param.Up_scroll && move < 0) {
                        window.scrollBy(0, move);
                        topDetected();
                        this.autoScroll(move);
                    } else if (Param.Down_scroll && move > 0) {
                        window.scrollBy(0, move);
                        bottomDetected();
                        this.autoScroll(move);
                    }
                });
            },
            manualScroll(move) {
                window.scrollBy({
                    left: 0,
                    top: move,
                    behavior: "smooth"
                });
            },
            isFinalPage(link) {
                Param.IsFinalPage = link.startsWith("javascript");
                return Param.IsFinalPage;
            },
            visibleObjects: object => object.filter(img => img.height > 0 || img.src),
            lastObject: object => {
                const len = object.length;
                if (len <= 5) return object[0];
                if (len <= 10) return object.at(-2) ?? object[0];
                return object.at(-3) ?? object[0];
            },
            detectionValue(object) {
                return this.visibleObjects(object).length >= Math.floor(object.length * .5);
            }
        };
    })();
    const Style = (() => {
        const $Set = Tools.getSet();
        return {
            async backgroundStyle(Color = Config.BGColor.Color) {
                Param.Body.style.cssText = `
                background: ${Color} !important;
            `;
                document.documentElement.style.cssText = `
                overflow: visible !important;
            `;
            },
            async pictureStyle() {
                if (Lib.platform === "Desktop") {
                    Lib.addStyle(`
                    .mh_comicpic img {
                        margin: auto;
                        display: block;
                        cursor: pointer;
                        vertical-align: top;
                        width: ${$Set.Img_Bw};
                        max-width: ${$Set.Img_Mw};
                    }
                `, Control.IdList.Image);
                }
                setTimeout(() => {
                    const click = new MouseEvent("click", {
                        bubbles: true,
                        cancelable: true
                    });
                    const observer = new IntersectionObserver(observed => {
                        observed.forEach(entry => {
                            if (entry.isIntersecting) {
                                entry.target.dispatchEvent(click);
                            }
                        });
                    }, {
                        threshold: .3
                    });
                    Param.MangaList.$qa("span.mh_btn:not(.contact):not(.read_page_link)").forEach(reloadBtn => observer.observe(reloadBtn));
                }, Control.WaitPicture);
            },
            async menuStyle() { }
        };
    })();
    const Hotkey = async () => {
        let jumpState = false;
        if (Lib.platform === "Desktop") {
            const {
                TurnPage,
                AutoScroll,
                KeepScroll,
                ManualScroll
            } = Config.RegisterHotkey.Function;
            if (Param.IsMainPage && KeepScroll && AutoScroll && !ManualScroll) {
                Param.Down_scroll = Tools.storage("scroll");
                Param.Down_scroll && Tools.autoScroll(Control.ScrollPixels);
            }
            const UP_ScrollSpeed = -Control.ScrollPixels;
            const CanScroll = AutoScroll || ManualScroll;
            Lib.onEvent(window, "keydown", event => {
                const key = event.key;
                if (key === "ArrowLeft" && TurnPage && !jumpState) {
                    event.stopImmediatePropagation();
                    jumpState = !Tools.isFinalPage(Param.PreviousLink);
                    location.assign(Param.PreviousLink);
                } else if (key === "ArrowRight" && TurnPage && !jumpState) {
                    event.stopImmediatePropagation();
                    jumpState = !Tools.isFinalPage(Param.NextLink);
                    location.assign(Param.NextLink);
                } else if (key === "ArrowUp" && CanScroll) {
                    event.stopImmediatePropagation();
                    event.preventDefault();
                    if (ManualScroll) {
                        Tools.manualScroll(-Lib.iH);
                    } else {
                        if (Param.Up_scroll) {
                            Param.Up_scroll = false;
                        } else if (!Param.Up_scroll || Param.Down_scroll) {
                            Param.Down_scroll = false;
                            Param.Up_scroll = true;
                            Tools.autoScroll(UP_ScrollSpeed);
                        }
                    }
                } else if (key === "ArrowDown" && CanScroll) {
                    event.stopImmediatePropagation();
                    event.preventDefault();
                    if (ManualScroll) {
                        Tools.manualScroll(Lib.iH);
                    } else {
                        if (Param.Down_scroll) {
                            Param.Down_scroll = false;
                            Tools.storage("scroll", false);
                        } else if (Param.Up_scroll || !Param.Down_scroll) {
                            Param.Up_scroll = false;
                            Param.Down_scroll = true;
                            Tools.storage("scroll", true);
                            Tools.autoScroll(Control.ScrollPixels);
                        }
                    }
                }
            }, {
                capture: true
            });
        } else if (Lib.platform === "Mobile") {
            let startX, startY, moveX, moveY;
            const sidelineX = Lib.iW * .3;
            const sidelineY = Lib.iH / 4 * .3;
            Lib.onEvent(window, "touchstart", event => {
                startX = event.touches[0].clientX;
                startY = event.touches[0].clientY;
            }, {
                passive: true
            });
            Lib.onEvent(window, "touchmove", Lib.$debounce(event => {
                moveY = event.touches[0].clientY - startY;
                if (Math.abs(moveY) < sidelineY) {
                    moveX = event.touches[0].clientX - startX;
                    if (moveX > sidelineX && !jumpState) {
                        jumpState = !Tools.isFinalPage(Param.PreviousLink);
                        location.assign(Param.PreviousLink);
                    } else if (moveX < -sidelineX && !jumpState) {
                        jumpState = !Tools.isFinalPage(Param.NextLink);
                        location.assign(Param.NextLink);
                    }
                }
            }, 60), {
                passive: true
            });
        }
    };
    const PageTurn = async () => {
        const turnMode = Config.AutoTurnPage.Mode;
        const optimized = turnMode === 3;
        async function unlimited() {
            Lib.addStyle(`
            .mh_wrap, .mh_readend, .mh_footpager,
            .fed-foot-info, #imgvalidation2022 {display: none;}
            body {
                margin: 0;
                padding: 0;
            }
            #${Control.IdList.Iframe} {
                margin: 0;
                padding: 0;
                width: 100%;
                height: 110vh;
                border: none;
            }
        `, Control.IdList.Scroll);
            const stylelRules = Lib.$q(`#${Control.IdList.Scroll}`).sheet.cssRules;
            if (Param.IsMainPage) {
                let size = 0;
                Lib.onEvent(window, "message", event => {
                    const data = event.data;
                    if (data && data.length > 0) {
                        const {
                            Title,
                            PreviousUrl,
                            CurrentUrl,
                            NextUrl,
                            Resize,
                            SizeSet,
                            SizeRecord
                        } = data[0];
                        if (Resize) {
                            if (size > SizeRecord) size -= SizeRecord;
                            size += Resize;
                            stylelRules[2].style.height = `${size}px`;
                        } else if (SizeSet) stylelRules[2].style.height = `${SizeSet}px`; else if (Title && NextUrl && PreviousUrl && CurrentUrl) {
                            document.title = Title;
                            Param.NextLink = NextUrl;
                            Param.PreviousLink = PreviousUrl;
                            history.pushState(null, null, CurrentUrl);
                        }
                    }
                });
            } else {
                Lib.addStyle(`
                html {
                    overflow: hidden !important;
                    overflow-x: hidden !important;
                    scrollbar-width: none !important;
                    -ms-overflow-style: none !important;
                }
                html::-webkit-scrollbar {
                    display: none !important;
                }
            `, Control.IdList.ChildS);
                let mainWindow = window;
                Lib.onEvent(window, "message", event => {
                    while (mainWindow.parent !== mainWindow) {
                        mainWindow = mainWindow.parent;
                    }
                    mainWindow.postMessage(event.data, Lib.$origin);
                });
            }
            const iframe = Lib.createElement("iframe", {
                id: Control.IdList.Iframe,
                src: Param.NextLink
            });
            (async () => {
                let img, Observer, quantity = 0;
                const observerNext = new IntersectionObserver(observed => {
                    observed.forEach(entry => {
                        const rect = entry.boundingClientRect;
                        const isPastTarget = rect.bottom < 0;
                        const isIntersecting = entry.isIntersecting;
                        if ((isIntersecting || isPastTarget) && Tools.detectionValue(img)) {
                            observerNext.disconnect();
                            Observer.disconnect();
                            turnPage();
                        }
                    });
                }, {
                    threshold: [0, .1, .5],
                    rootMargin: "0px 0px 200px 0px"
                });
                setTimeout(() => {
                    img = Param.MangaList.$qa("img");
                    if (img.length <= 5) {
                        turnPage();
                        return;
                    }
                    const lastImg = Tools.lastObject(Tools.visibleObjects(img));
                    lastImg instanceof Element && observerNext.observe(lastImg);
                    Lib.$observer(Param.MangaList, () => {
                        const visible = Tools.visibleObjects(img);
                        const vlen = visible.length;
                        if (vlen > quantity) {
                            quantity = vlen;
                            const lastImg2 = Tools.lastObject(visible);
                            if (lastImg2 instanceof Element) {
                                observerNext.disconnect();
                                observerNext.observe(lastImg2);
                            }
                        }
                    }, {
                        debounce: 100,
                        attributeFilter: ["src"]
                    }, observer => {
                        Observer = observer.ob;
                    });
                }, Control.WaitPicture);
            })();
            let turned = false;
            function turnPage() {
                if (turned) return;
                turned = true;
                let currentHeight = 0;
                const resizeObserver = new ResizeObserver(() => {
                    if (!Param.MangaList.isConnected) {
                        resizeObserver.disconnect();
                        return;
                    }
                    const newHeight = Param.MangaList.offsetHeight;
                    if (newHeight > currentHeight) {
                        window.parent.postMessage([{
                            Resize: newHeight,
                            SizeRecord: currentHeight
                        }], Lib.$origin);
                        currentHeight = newHeight;
                    }
                });
                if (Tools.isFinalPage(Param.NextLink)) {
                    if (optimized) {
                        window.parent.postMessage([{
                            SizeSet: Param.MangaList.offsetHeight + 245
                        }], Lib.$origin);
                    }
                    stylelRules[0].style.display = "block";
                    return;
                }
                waitLoad();
                Param.Body.appendChild(iframe);
                resizeObserver.observe(Param.MangaList);
                function waitLoad() {
                    let iframeWindow, currentUrl, content, allImg;
                    const failed = () => {
                        iframe.offAll();
                        iframe.src = "";
                        setTimeout(() => {
                            iframe.src = Param.NextLink;
                            waitLoad();
                        });
                    };
                    const success = () => {
                        iframe.offAll();
                        iframeWindow = iframe.contentWindow;
                        currentUrl = iframeWindow.location.href;
                        if (currentUrl !== Param.NextLink) {
                            failed();
                            return;
                        }
                        content = iframeWindow.document;
                        content.body.style.overflow = "hidden";
                        Lib.log(currentUrl, {
                            group: "無盡翻頁"
                        });
                        allImg = content.$qa("#mangalist img");
                        const urlUpdate = new IntersectionObserver(observed => {
                            observed.forEach(entry => {
                                if (entry.isIntersecting) {
                                    urlUpdate.disconnect();
                                    const PageLink = content.body.$qa("div.mh_readend ul a");
                                    window.parent.postMessage([{
                                        Title: content.title,
                                        CurrentUrl: currentUrl,
                                        PreviousUrl: PageLink[0]?.href,
                                        NextUrl: PageLink[2]?.href
                                    }], Lib.$origin);
                                }
                            });
                        }, {
                            threshold: 0
                        });
                        allImg.forEach(img => urlUpdate.observe(img));
                        if (optimized) {
                            Lib.$q("title").id = Control.IdList.Title;
                            const adapt = Lib.platform === "Desktop" ? .5 : .7;
                            const releaseMemory = new IntersectionObserver(observed => {
                                observed.forEach(entry => {
                                    if (entry.isIntersecting) {
                                        const targetImg = entry.target;
                                        const ratio = Math.min(adapt, Lib.iH * adapt / targetImg.clientHeight);
                                        if (entry.intersectionRatio >= ratio) {
                                            releaseMemory.disconnect();
                                            Tools.getNodes(document).forEach(node => {
                                                node.remove();
                                            });
                                            targetImg.scrollIntoView();
                                        }
                                    }
                                });
                            }, {
                                threshold: [0, .5, 1]
                            });
                            allImg.forEach(img => releaseMemory.observe(img));
                        }
                    };
                    iframe.on("load", success);
                    iframe.on("error", failed);
                }
            }
        }
        switch (turnMode) {
            case 2:
            case 3:
                unlimited();
                break;

            default:
                setTimeout(() => {
                    const img = Param.MangaList.$qa("img");
                    if (!Tools.isFinalPage(Param.NextLink)) {
                        const observerNext = new IntersectionObserver(observed => {
                            observed.forEach(entry => {
                                if (entry.isIntersecting && Tools.detectionValue(img)) {
                                    observerNext.disconnect();
                                    location.assign(Param.NextLink);
                                }
                            });
                        }, {
                            threshold: 1
                        });
                        observerNext.observe(Param.BottomStrip);
                    }
                }, Control.WaitPicture);
        }
    };
    function Main(raf = void 0) {
        async function mangaPageInit(callback) {
            Lib.waitEl(["body", "div.mh_readtitle", "div.mh_headpager", "div.mh_readend", "#mangalist"], null, {
                raf: raf,
                throttle: 30,
                timeout: 10,
                visibility: Param.IsMainPage,
                timeoutResult: true
            }).then(([Body, Title, HeadPager, Readend, Manga]) => {
                Param.Body = Body;
                const HomeLink = Title.$qa("a");
                Param.ContentsPage = HomeLink[0].href;
                Param.HomePage = HomeLink[1].href;
                try {
                    const PageLink = Readend.$qa("ul a");
                    Param.PreviousLink = PageLink[0].href;
                    Param.NextLink = PageLink[2].href;
                } catch {
                    const PageLink = HeadPager.$qa("a.mh_btn:not(.mh_bgcolor)");
                    Param.PreviousLink = PageLink[0].href;
                    Param.NextLink = PageLink[1].href;
                }
                Param.MangaList = Manga;
                Param.BottomStrip = Readend.$q(".endtip2");
                if ([Param.Body, Param.ContentsPage, Param.HomePage, Param.PreviousLink, Param.NextLink, Param.MangaList, Param.BottomStrip].every(Check => Check)) callback(true); else callback(false);
            });
        }
        async function contentsPageInit() {
            Lib.waitEl([".all_data_list", ".website-display-all"], ([list, display]) => {
                if (list.style.height === "auto") return;
                display.click();
            }, {
                raf: raf
            });
        }
        try {
            if (Param.IsMangaPage) {
                mangaPageInit(state => {
                    if (state) {
                        Style.pictureStyle();
                        Config.BGColor.Enable && Style.backgroundStyle();
                        Config.AutoTurnPage.Enable && PageTurn();
                        Config.RegisterHotkey.Enable && Hotkey();
                    } else {
                        Lib.log("Manga Page Init Error").error;
                        setTimeout(() => Main(true), 1e3);
                    }
                });
            } else contentsPageInit();
        } catch (error) {
            Lib.log(error).error;
        }
    }
    Main();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址