Greasy Fork

Greasy Fork is available in English.

S1论坛帖子已读样式

已读标题变灰,帖子的url根据最新回复数会每次刷新

当前为 2020-07-24 提交的版本,查看 最新版本

// ==UserScript==
// @name         S1论坛帖子已读样式
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  已读标题变灰,帖子的url根据最新回复数会每次刷新
// @author       You
// @match        *.saraba1st.com/2b/forum*
// @grant        none
// @run-at       document-body
// ==/UserScript==

function get(data, key) {
    const keys = key.replace(/\[(\d+)\]/g, ".$1").split(".");
    let result = data;
    while (keys.length && result) {
        result = result[keys.shift()];
    }
    return result;
}

// function isThread() {
//     return location.pathname.includes("thread");
// }

// function isThreadList() {
//     return location.pathname.includes("forum");
// }

function asyncRun(callback) {
    const timer = setInterval(() => {
        const ret = callback();
        if (ret) {
            clearInterval(timer);
        }
    }, 100);
}

function updateThreadReplyNum(id, num) {
    localStorage.setItem(id, num);
}

function getThreadLastReplyNum(id) {
    const num = localStorage.getItem(id);
    return num ? +num : null;
}

function appendStyle() {
    const style = document.createElement("style");
    style.type = "text/css";
    style.appendChild(
        document.createTextNode(
            ".tl th a:visited, .tl td.fn a:visited {color: #818588}"
        )
    );
    style.appendChild(
        document.createTextNode(
            ".tl th a:hover, .tl td.fn a:hover {color: #022C80}"
        )
    );
    document.body.appendChild(style);
}

(function () {
    "use strict";
    // 修改帖子样式
    appendStyle();

    // 动态修改url
    const hasReplacedIds = new Set();
    let table = null;
    let lastThreadNum = 0;
    let observer = null;

    function observeThreads() {
        const threads = Array.from(
            document.querySelectorAll("#threadlisttableid tbody tr")
        ).filter((element) => !hasReplacedIds.has(element.parentNode.id));
        threads.forEach((t) => observer.observe(t));
    }

    function initObserver() {
        observer = new IntersectionObserver(
            (entities) => {
                entities.forEach((entity) => {
                    if (entity.isIntersecting) {
                        const thread = entity.target;
                        observer.unobserve(thread);

                        const id = thread.parentNode.id;
                        if (hasReplacedIds.has(id)) {
                            return;
                        }
                        hasReplacedIds.add(id);

                        // url
                        const replyNum = get(thread.querySelector('.num a'), 'textContent');
                        const title = thread.querySelector('.xst');
                        if (title && replyNum) {
                            title.href += `#${replyNum}`;
                        }

                        // reply num
                        const tid = get(id.match(/\d+/), "0");
                        const lastReplyNum = +getThreadLastReplyNum(tid);
                        if (lastReplyNum && replyNum && replyNum - lastReplyNum > 0) {
                            title.textContent = `+${replyNum - lastReplyNum} ${
                                title.textContent
                                }`;
                        }
                    }
                });
            },
            {
                rootMargin: "100px",
            }
        );
    }

    function onThreadClick() {
        asyncRun(() => {
            if (table) {
                table.addEventListener('click', (e) => {
                    let element = e.target;
                    const validClick = (element.tagName === 'A' && element.href.includes('thread-'))
                        || (element.tagName === 'IMG' && element.src === 'https://static.saraba1st.com/image/s1/folder_common.gif');
                    if (!validClick) {
                        return;
                    }
                    while (element.parentNode) {
                        if (element.tagName === 'TBODY') {
                            const replyNum = get(element.querySelector('.num a'), 'textContent');
                            const tid = get(element.id.match(/\d+/), "0");
                            if (replyNum && tid) {
                                updateThreadReplyNum(tid, replyNum);
                            }
                            break;
                        }
                        if (element === table) {
                            break;
                        }
                        element = element.parentNode;
                    }
                });
                return true;
            }
        })
    }

    initObserver();
    onThreadClick();

    asyncRun(() => {
        table = document.getElementById("threadlisttableid");
        // 实际发现只检测table并不能保证帖子全加载了
        const nextPageBtn = document.querySelector("#autopbn");
        if (table && nextPageBtn) {
            lastThreadNum = table.childElementCount;
            observeThreads();
            return true;
        }
    });

    // 监控下一页
    asyncRun(() => {
        const nextPageBtn = document.querySelector("#autopbn");
        if (nextPageBtn) {
            nextPageBtn.addEventListener("click", () => {
                asyncRun(() => {
                    if (table.childElementCount > lastThreadNum) {
                        lastThreadNum = table.childElementCount;
                        observeThreads();
                        return true;
                    }
                });
            });
            return true;
        }
    });
})();