Sleazy Fork is available in English.

Soul++

提升你的魂+使用体验

// ==UserScript==
// @name            Soul++
// @namespace       SoulPlusPlus
// @version         1.0.2
// @description     提升你的魂+使用体验
// @run-at          document-start
// @author          镜花水中捞月
// @homepage        https://github.com/FetchTheMoon
// @icon64          https://cdn.jsdelivr.net/gh/FetchTheMoon/UserScript/LOGO.png
// @supportURL      https://github.com/FetchTheMoon/UserScript/issues
// ----------------COPY START---------------------
// @match           https://*.spring-plus.net/*
// @match           https://*.summer-plus.net/*
// @match           https://*.soul-plus.net/*
// @match           https://*.south-plus.net/*
// @match           https://*.north-plus.net/*
// @match           https://*.snow-plus.net/*
// @match           https://*.level-plus.net/*
// @match           https://*.white-plus.net/*
// @match           https://*.imoutolove.me/*
// @match           https://*.south-plus.org/*
// @match           https://*.east-plus.net/*
// --------------------------------------------
// @match           https://spring-plus.net/*
// @match           https://summer-plus.net/*
// @match           https://soul-plus.net/*
// @match           https://south-plus.net/*
// @match           https://north-plus.net/*
// @match           https://snow-plus.net/*
// @match           https://level-plus.net/*
// @match           https://white-plus.net/*
// @match           https://imoutolove.me/*
// @match           https://south-plus.org/*
// @match           https://east-plus.net/*
// --------------------------------------------
// @grant           GM_getValue
// @grant           GM_setValue
// @grant           GM_listValues
// @grant           GM_addValueChangeListener
// @grant           GM_removeValueChangeListener
// @grant           GM_notification
// @grant           GM_deleteValue
// @grant           GM_addStyle
// @grant           GM_getResourceText
// @grant           unsafeWindow
// --------------------------------------------
// @require         https://cdn.jsdelivr.net/npm/toastify-js@1.11.2/src/toastify.min.js
// @resource        TOASTIFY_CSS https://cdn.jsdelivr.net/npm/toastify-js@1.11.2/src/toastify.css
// ----------------COPY END---------------------
// @license         GPL-3.0 License
// ==/UserScript==

'use strict';

const PageType = Object.freeze({
    THREADS_PAGE: Symbol("普通主题列表"),
    PIC_WALL_PAGE: Symbol("图墙区主题列表"),
    POSTS_PAGE: Symbol("帖子列表"),
    SEARCH_RESULT: Symbol("搜索结果")
});

const ToastType = Object.freeze({
    INFO: Symbol("信息"),
    SUCCESS: Symbol("成功"),
    DANGER: Symbol("危险,失败"),
    WARNING: Symbol("警告")
});

const FETCH_CONFIG = {
    credentials: 'include',
    mode: "no-cors"
};

function getElementByXpath(from, xpath) {
    return from.evaluate(xpath, from, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}

function waitForImageToLoad(imageElement) {
    return new Promise(resolve => {
        imageElement.onload = resolve
    })
}

function toast(info, toastType, time = 3000, close = true) {
    let t;
    switch (toastType) {
        case ToastType.INFO:
            t = "linear-gradient(109deg, #3da1e0, #004dc1)";
            break;
        case ToastType.SUCCESS:
            t = "linear-gradient(213deg, #5daa16, #05bb1b)";
            break;
        case ToastType.DANGER:
            t = "linear-gradient(18deg, #cb3131, #ac1415)";
            break;
        case ToastType.WARNING:
            t = "linear-gradient(180deg, #e98202, #fe5e00)";
            break;
        default:
            t = "linear-gradient(109deg, #3da1e0, #004dc1)";
    }
    Toastify({
        text: info,
        duration: time,
        close: close,
        gravity: "bottom", // `top` or `bottom`
        position: "right", // `left`, `center` or `right`
        stopOnFocus: true, // Prevents dismissing of toast on hover
        style: {
            background: t,
        },
        onClick: function () {
        } // Callback after click
    }).showToast();

}

function getTimeStamp() {
    return new Date().getTime();
}

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
}


function getRandomInt(min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

async function fetchRetry(url, options, n = 1) {
    try {
        return await fetch(url, options)
    } catch (err) {
        if (n <= 1) throw err;
        return await fetchRetry(url, options, n - 1);
    }
}

async function getPage(url, dummy = false, retry = 3, toastPop = false) {
    return await fetchRetry(url, FETCH_CONFIG, retry)
        .then(resp => resp.text())
        .then(html => {
            if (dummy) {
                let dummy = document.createElement("html");
                dummy.insertAdjacentHTML('afterbegin', html);
                return dummy
            } else {
                return html
            }
        }).catch(e => {
                if (toastPop) {
                    toast(`访问 ${url} 失败\n${e}`, ToastType.WARNING);
                } else {
                    console.error(`访问 ${url} 失败\n${e}`);
                }
            }
        );
}

class GMK {

    static addStyle(css) {
        return GM_addStyle(css);
    }

    static setValue(key, value) {
        return GM_setValue(key, value)
    }

    static getValue(key) {
        return GM_getValue(key)
    }

    static getResourceText(key) {
        return GM_getResourceText(key);
    }

    static listValues() {
        return GM_listValues();
    }

    static deleteValue(_name) {
        return GM_deleteValue(_name);
    }

    // listener_id = GM_addValueChangeListener(name, function(name, old_value, new_value, remote) {})
    static addValueChangeListener(_name, callback) {
        return GM_addValueChangeListener(_name, callback);
    }

    static removeValueChangeListener(listener_id) {
        return GM_removeValueChangeListener(listener_id);
    }
}

class MppManager {
    static TASK_KEY = "Soul++:MppThreadsStatus";

    constructor() {
    }

    static isThreadExist(_tid) {
        return this.getMarkList().hasOwnProperty(_tid);
    }

    static getMarkList() {
        return GMK.getValue(this.TASK_KEY) || {}
    }

    static addMarkThread(_tid) {
        GMK.setValue(this.TASK_KEY, { ...this.getMarkList(), ...{ [_tid]: {} } })
    }

    static deleteMarkedThread(_tid) {
        let markList = this.getMarkList();
        delete markList[_tid];
        GMK.setValue(this.TASK_KEY, markList)
    }

    static isMarked(_tid) {
        return this.getMarkList().hasOwnProperty(_tid)
    }

    static getAllThreadStatus() {
        return this.getMarkList();
    }

    static setThreadStatus(_tid, threadStatus) {
        GMK.setValue(this.TASK_KEY, {
            ...this.getMarkList(),
            [_tid]: threadStatus
        });
    }

    static getLastFetchTime(_tid) {
        let res = Object.entries(GMK.getValue(this.TASK_KEY)).filter(e => e[0] === _tid);
        return res[0][1]["lastFetchTime"];
    };

    static isAllChecked() {
        let res = Object.entries(GMK.getValue(this.TASK_KEY)).filter(e => !e[1]["allPagesChecked"]);
        return res.length === 0;
    };
}

//##############################################################
// 功能
//##############################################################

function buyRefresh_free(target = document) {
    let buyButtons = target.querySelectorAll(".quote.jumbotron>.btn.btn-danger")
    buyButtons.forEach(button => {
        // 获取GET购买地址
        const urlRegex = /location\.href='(.+)'/
        const buyUrl = button.getAttribute("onclick");
        if (buyUrl === null) return;
        let m = buyUrl.match(urlRegex);
        if (m === null) return;
        let url = m[1];
        // 避免点击按钮的时候跳转,删掉这个属性
        button.setAttribute("onclick", "null");
        // 拿到帖子ID
        let postContainer = button.closest(".tpc_content .f14")
        let post_id = postContainer.getAttribute("id");
        // 添加点击事件,用fetch发送请求,然后读取页面再直接修改当前页面
        let customPurchase = (e => {
            e.stopPropagation();
            let btn = e.target;
            btn.setAttribute("value", "正在购买……请稍等………");
            try {
                fetch(url, FETCH_CONFIG)
                    .then(resp => resp.text())
                    .then(text => {
                        if (!text.includes("操作完成")) {
                            toast("购买失败!", ToastType.DANGER);
                            return;
                        }
                        let threadID = postContainer.getAttribute("tid");
                        let pg = postContainer.getAttribute("page");
                        let resultURL = `./read.php?tid=${threadID}&page=${pg}`;
                        fetch(resultURL, FETCH_CONFIG).then(resp => resp.text())
                            .then(html => {
                                let dummy = document.createElement("html");
                                dummy.innerHTML = html;
                                if (GMK.getValue("hidePostImage")) {
                                    hidePostImage(dummy);
                                }
                                let purchased = dummy.querySelector("#" + post_id);
                                let notPurchased = document.querySelector("#" + post_id);
                                notPurchased.parentNode.replaceChild(purchased, notPurchased);

                            });

                        btn.style.display = "none";

                    });

            } catch (error) {
                toast(`发送请求出错,购买失败!\n${error}`, ToastType.DANGER);
                console.log('Request Failed', error);
            }
        })

        button.addEventListener("click", customPurchase);
    });
}

function hideImg(img) {
    // 避免折叠论坛表情
    const emojiPathReg = /images\/post\/smile\//;
    if (img.getAttribute("src").match(emojiPathReg)) {
        return
    }
    // 避免折叠论坛自带的文件图标
    const fileIconPathReg = /images\/colorImagination\/file\//;
    if (img.getAttribute("src").match(fileIconPathReg)) {
        return
    }

    // 避免重复处理
    let p = img.parentNode;
    if (p.getAttribute("class") === "spp-img-mask") return;

    // 如果开启了按需加载
    if (GMK.getValue("loadImageOnDemand")) {
        img.dataset.src = img.getAttribute("src");
        img.setAttribute("src", "")
    }

    // 如果图片的父元素是A标签,去掉它
    if (img.parentNode.tagName === "A") img.parentNode.replaceWith(img);
    // 创建包裹元素
    let wrapper = document.createElement('div');
    wrapper.setAttribute("class", "spp-img-mask");
    wrapper.style.display = "grid";
    wrapper.style.gridTemplateRows = "auto auto";
    wrapper.style.justifyItems = "center";

    // 将父元素下的图片元素替换成包裹元素
    img.parentNode.replaceChild(wrapper, img);

    // 将图片元素当成子元素放入包裹元素
    wrapper.appendChild(img);

    img.style.width = "100%";

    // 添加类名
    img.setAttribute("class", "spp-thread-imgs spp-hide");

    // 包裹元素样式
    wrapper.style.borderStyle = "dashed";
    wrapper.style.width = "auto";
    wrapper.style.height = "20";
    wrapper.style.textAlign = "center";
    wrapper.style.verticalAlign = "center";
    wrapper.style.cursor = "pointer";

    // 创建遮罩小人儿表情
    let icon_hide = document.createElement("img");
    icon_hide.setAttribute("src", "images/post/smile/smallface/face106.gif");

    let icon_show = document.createElement("img");
    icon_show.setAttribute("src", "images/post/smile/smallface/face109.gif");


    // 创建遮罩文本
    let tip = document.createElement("span");
    let tip_text = document.createElement("span");
    tip_text.innerText = "看看是啥";

    // 凑一堆儿来
    tip.appendChild(icon_hide);
    tip.appendChild(icon_show);
    tip.appendChild(tip_text);

    // 添加类名
    icon_hide.setAttribute("class", "spp-img-mask-icon-hide");
    icon_show.setAttribute("class", "spp-img-mask-icon-show spp-hide");
    tip.setAttribute("class", "ssp-img-mask-text");

    // 插入元素
    wrapper.insertBefore(tip, img);
    // 防止点击图片打开新窗口
    document.querySelector(".spp-thread-imgs").addEventListener("click", e => e.preventDefault());
    // 事件监听
    wrapper.addEventListener("click", (e) => {
        e.stopPropagation();
        // console.log(e.target);
        // console.log(e.currentTarget);
        let img = e.currentTarget.querySelector(".spp-thread-imgs");
        img.classList.toggle("spp-hide");
        // 按需加载
        if (GMK.getValue("loadImageOnDemand") && !img.classList.contains("spp-hide")) {
            let loading = document.createElement("div");
            loading.innerHTML = `<div class="spp-loading-animation">
                <div class="dot1"></div>
                <div class="dot2"></div>
                <div class="dot3"></div>
            </div>`;
            loading = loading.firstChild;
            img.parentNode.append(loading);
            img.setAttribute("src", img.dataset.src);
            waitForImageToLoad(img).then(() => {
                loading.parentNode.removeChild(loading);
            });
        }
        e.currentTarget.querySelector(".spp-img-mask-icon-hide").classList.toggle("spp-hide");
        e.currentTarget.querySelector(".spp-img-mask-icon-show").classList.toggle("spp-hide");
    });

}

function hideAvatar(avatar) {
    let src = avatar.getAttribute("src");
    if (src === "images/face/none.gif") return;

    // 如果开启了按需加载
    if (GMK.getValue("loadImageOnDemand")) {
        avatar.dataset.src = avatar.getAttribute("src");
        avatar.setAttribute("src", "")
    }

    // 创建包裹元素
    let wrapper = document.createElement('div');
    wrapper.setAttribute("class", "spp-avatar-mask");
    wrapper.style.minWidth = "162px";
    wrapper.style.minHeight = "162px";
    wrapper.style.display = "grid";
    wrapper.style.justifyItems = "center";
    wrapper.style.alignItems = "center";

    // 创建一个假头像
    let fakeAvatarElement = document.createElement("img");
    fakeAvatarElement.setAttribute("src", "images/face/none.gif");
    fakeAvatarElement.style.borderStyle = "dashed";
    fakeAvatarElement.style.borderRadius = "3";
    fakeAvatarElement.style.borderWidth = "3px";
    fakeAvatarElement.style.borderColor = "Orange";

    // 替换包裹元素
    avatar.parentNode.replaceChild(wrapper, avatar);

    // 将假头像和真头像插到包裹元素中
    wrapper.appendChild(avatar);
    wrapper.appendChild(fakeAvatarElement);

    // 隐藏真头像
    avatar.classList.add("spp-hide");

    // 设置类名
    avatar.classList.add("spp-avatar-real");
    fakeAvatarElement.classList.add("spp-avatar-fake");

    // 事件监听
    wrapper.addEventListener("mouseenter", (e) => {
        e.stopPropagation();
        e.currentTarget.querySelector(".spp-avatar-fake").classList.add("spp-hide");
        e.currentTarget.querySelector(".spp-avatar-real").classList.remove("spp-hide");
        // 按需加载
        if (GMK.getValue("loadImageOnDemand") && !avatar.classList.contains("spp-hide")) {
            let loading = document.createElement("div");
            loading.innerHTML = `<div class="spp-loading-animation">
                <div class="dot1"></div>
                <div class="dot2"></div>
                <div class="dot3"></div>
            </div>`;
            loading = loading.firstChild;
            e.currentTarget.append(loading);
            avatar.setAttribute("src", avatar.dataset.src);
            waitForImageToLoad(avatar).then(() => {
                loading.parentNode.removeChild(loading);
            });
        }
    });

    wrapper.addEventListener("mouseleave", (e) => {
        e.stopPropagation();
        e.currentTarget.querySelector(".spp-avatar-fake").classList.remove("spp-hide");
        e.currentTarget.querySelector(".spp-avatar-real").classList.add("spp-hide");
        e.currentTarget.querySelectorAll(".spp-loading-animation").forEach(ele => ele.parentNode.removeChild(ele));
    });


}

function hidePostImage(target = document) {
    let thread_user_post_images = target.querySelectorAll(".t5.t2 .r_one img");

    thread_user_post_images.forEach(hideImg);
}

function hideUserAvatar(target = document) {
    let user_avatars = target.querySelectorAll(".user-pic img");
    user_avatars.forEach(hideAvatar);
}

function dynamicLoadingNextPage(pageType) {

    class NextPageLoader {

        constructor() {
            this.isFetching = false;
            this.nextPageDummy = null;
        }

        GetURLDummy(url) {
            this.nextPageDummy = document.createElement("html");
            this.isFetching = true;
            return fetch(url, FETCH_CONFIG)
                .then(response => response.text())

        }

        AppendNextPageItems(itemSelector, divider) {

            let postsFragment = document.createDocumentFragment();
            this.nextPageDummy.querySelectorAll(itemSelector).forEach(ele => postsFragment.appendChild(ele));
            // 追加下一页的所有子项追加到分割线下面
            divider.parentNode.appendChild(postsFragment);
        }

        UpdatePageList() {
            // 主动更新帖子列表上下方的当前页码数
            let pagesOld = document.querySelectorAll(".pages");
            let pagesNew = this.nextPageDummy.querySelectorAll(".pages");
            for (let i = 0; i < pagesOld.length; i++) {
                pagesOld[i].parentNode.replaceChild(pagesNew[i], pagesOld[i]);
            }
        }

    }


    function getNextPageUrl() {
        let pageSeq = document.querySelector(".pages b");
        if (!pageSeq) return null;
        let pageNum = pageSeq.parentNode;
        let url = pageNum.nextSibling.firstChild.getAttribute("href");
        if (pageNum.nextSibling.nextSibling.classList.contains("pagesone")) return null;
        if (document.URL.includes(url)) return null;
        return url;
    }


    function makeDivider(itemsSelector, dividerMaker) {
        let divider = dividerMaker();
        let allItem = document.querySelectorAll(itemsSelector);
        let lastItem = allItem[allItem.length - 1];
        lastItem.parentNode.appendChild(divider);
        return divider;
    }

    let nextPageLoader;
    let nextPageURL;
    nextPageLoader = nextPageLoader || new NextPageLoader()
    // 处理搜索结果页面
    if (pageType === PageType.SEARCH_RESULT) {
        document.addEventListener('wheel', (e) => {
            e.stopPropagation();
            const itemListSelector = ".tr3.tac";
            if (e.deltaY < 0 || nextPageLoader.isFetching) return;
            if (!nextPageLoader.nextPageDummy) {
                nextPageURL = getNextPageUrl();
                if (!nextPageURL) return;
                let divider = makeDivider(itemListSelector, () => {
                    let divider = document.createElement("tr");
                    let dividerContent = document.createElement("td");
                    divider.setAttribute("class", "tr2 spp-next-page-loader-divider")
                    divider.appendChild(dividerContent);
                    dividerContent.colSpan = 7;
                    dividerContent.style.textAlign = "center";
                    dividerContent.style.fontWeight = "bold";
                    dividerContent.innerText = "...";
                    return divider;
                });
                divider.firstChild.innerText = "正在获取下一页的帖子......";
                let p = nextPageLoader.GetURLDummy(nextPageURL);
                p
                    .then(html => {
                        nextPageLoader.nextPageDummy.innerHTML = html
                        if (GMK.getValue("blockAdforumSearchResult")) blockAdforumSearchResult(nextPageLoader.nextPageDummy);
                    })
                    .catch(err => {
                        console.error(err);
                        divider.firstChild.innerText = "获取下一页的帖子出错,请手动刷新";
                    })
                    .finally(() => {
                        nextPageLoader.isFetching = false;
                        divider.firstChild.innerText = "滚动条到底后继续向下滚动将会加载下一页的帖子";
                    });

            }
            // 否则判断一下是否到底了,到底了就追加下一页的内容
            else if (Math.abs(document.documentElement.scrollHeight - (window.pageYOffset + window.innerHeight)) < 20) {
                let divider = getElementByXpath(document, "//tr[@class='tr2 spp-next-page-loader-divider'][last()]");
                nextPageLoader.AppendNextPageItems(itemListSelector, divider);
                nextPageLoader.UpdatePageList();

                divider.firstChild.innerText = `以下是第${nextPageURL.match(/page-(\d+)/)[1]}页`;
                window.history.pushState({}, 0, nextPageURL); // 将地址栏也改变了
                nextPageLoader.nextPageDummy = null;
            }

        })
    }
    // 处理主题列表页面
    if (pageType === PageType.THREADS_PAGE) {
        document.addEventListener('wheel', (e) => {
            e.stopPropagation();
            const itemListSelector = ".tr3.t_one";
            if (e.deltaY < 0 || nextPageLoader.isFetching) return;
            if (!nextPageLoader.nextPageDummy) {
                nextPageURL = getNextPageUrl();
                if (!nextPageURL) return;
                let divider = makeDivider(itemListSelector, () => {
                    let divider = document.createElement("tr");
                    let dividerContent = document.createElement("td");
                    divider.setAttribute("class", "tr2 spp-next-page-loader-divider")
                    divider.appendChild(dividerContent);
                    dividerContent.colSpan = 5;
                    dividerContent.style.textAlign = "center";
                    dividerContent.style.fontWeight = "bold";
                    dividerContent.innerText = "...";
                    return divider;
                });
                divider.firstChild.innerText = "正在获取下一页的帖子......";
                let p = nextPageLoader.GetURLDummy(nextPageURL);
                p
                    .then(html => {
                        nextPageLoader.nextPageDummy.innerHTML = html;
                        threadAddAnchorAttribute(nextPageLoader.nextPageDummy, page + 1, fid);
                        if (GMK.getValue("highlightViewedThread")) highlightViewedThread(nextPageLoader.nextPageDummy);
                    })
                    .catch(err => {
                        console.error(err);
                        divider.firstChild.innerText = "获取下一页的帖子出错,请手动刷新";
                    })
                    .finally(() => {
                        nextPageLoader.isFetching = false;
                        divider.firstChild.innerText = "滚动条到底后继续向下滚动将会加载下一页的帖子";
                    });

            }
            // 否则判断一下是否到底了,到底了就追加下一页的内容
            else if (Math.abs(document.documentElement.scrollHeight - (window.pageYOffset + window.innerHeight)) < 20) {
                let divider = getElementByXpath(document, "//tr[@class='tr2 spp-next-page-loader-divider'][last()]");
                nextPageLoader.AppendNextPageItems(itemListSelector, divider);
                nextPageLoader.UpdatePageList();
                divider.firstChild.innerText = `以下是第${page + 1}页`;
                window.history.pushState({}, 0, nextPageURL); // 将地址栏也改变了
                page += 1;
                nextPageLoader.nextPageDummy = null;
            }

        })
    }
    // 处理楼层列表页面
    if (pageType === PageType.POSTS_PAGE) {
        document.addEventListener('wheel', (e) => {
            e.stopPropagation();
            const itemListSelector = ".t5.t2";
            if (e.deltaY < 0 || nextPageLoader.isFetching) return;
            if (!nextPageLoader.nextPageDummy) {
                nextPageURL = getNextPageUrl();
                if (!nextPageURL) return;
                let divider = makeDivider(itemListSelector, () => {
                    let divider = document.createElement("div");
                    let dividerContent = document.createElement("span");
                    divider.setAttribute("class", "t5 t2 spp-next-page-loader-divider")
                    divider.appendChild(dividerContent);
                    divider.style.textAlign = "center";
                    divider.style.fontWeight = "bold";
                    divider.style.fontSize = "14px";
                    divider.innerText = "...";
                    return divider;
                });
                divider.innerText = "加载中..";
                nextPageLoader.GetURLDummy(nextPageURL)
                    .then(html => {
                        nextPageLoader.nextPageDummy.innerHTML = html
                        postAddAnchorAttribute(nextPageLoader.nextPageDummy, page + 1, tid);
                        if (GMK.getValue("buyRefresh_free")) buyRefresh_free(nextPageLoader.nextPageDummy);
                        if (GMK.getValue("hidePostImage")) hidePostImage(nextPageLoader.nextPageDummy);
                        if (GMK.getValue("hideUserAvatar")) hideUserAvatar(nextPageLoader.nextPageDummy);
                        if (GMK.getValue("hoistingResourcePost")) hoistingResourcePost(nextPageLoader.nextPageDummy);
                    })
                    .catch(err => {
                        console.error(err);
                        divider.innerText = "获取下一页的帖子出错,请手动刷新";
                    })
                    .finally(() => {
                        nextPageLoader.isFetching = false;
                        divider.innerText = "滚动条到底后继续向下滚动将会加载下一页的帖子";
                    });

            }
            // 否则判断一下是否到底了,到底了就追加下一页的内容
            else if (Math.abs(document.documentElement.scrollHeight - (window.pageYOffset + window.innerHeight)) < 20) {
                let divider = getElementByXpath(document, "//div[@class='t5 t2 spp-next-page-loader-divider'][last()]");
                nextPageLoader.AppendNextPageItems(itemListSelector, divider);
                nextPageLoader.UpdatePageList();
                divider.innerText = `以下是第${page + 1}页`;
                // window.history.pushState({}, 0, nextPageURL); // 将地址栏也改变了
                page += 1;
                nextPageLoader.nextPageDummy = null;
            }

        })
    }
    // 处理图墙区主题列表页面
    if (pageType === PageType.PIC_WALL_PAGE) {
        document.addEventListener('wheel', (e) => {
            e.stopPropagation();
            const itemListSelector = ".dcsns-li.dcsns-rss.dcsns-feed-0";
            if (e.deltaY < 0 || nextPageLoader.isFetching) return;
            if (!nextPageLoader.nextPageDummy) {
                nextPageURL = getNextPageUrl();
                if (!nextPageURL) return;
                let divider = makeDivider(itemListSelector, () => {
                    let divider = document.createElement("tr");
                    let dividerContent = document.createElement("td");
                    divider.setAttribute("class", "tr2 spp-next-page-loader-divider")
                    divider.appendChild(dividerContent);
                    dividerContent.colSpan = 5;
                    dividerContent.style.textAlign = "center";
                    dividerContent.style.fontWeight = "bold";
                    dividerContent.innerText = "...";
                    return divider;
                });
                divider.firstChild.innerText = "正在获取下一页的帖子......";
                let p = nextPageLoader.GetURLDummy(nextPageURL);
                p
                    .then(html => {
                        nextPageLoader.nextPageDummy.innerHTML = html;
                        nextPageLoader.nextPageDummy.querySelectorAll(".dcsns-li.dcsns-rss.dcsns-feed-0 .lazy").forEach(ele => {
                            ele.setAttribute("loading", "lazy");
                            ele.setAttribute("class", "");
                            ele.setAttribute("src", ele.getAttribute("data-original"));
                            ele.setAttribute("data-original", "");
                            ele.style.display = "inline";
                        });

                    })
                    .catch(err => {
                        console.error(err);
                        divider.firstChild.innerText = "获取下一页的帖子出错,请手动刷新";
                    })
                    .finally(() => {
                        nextPageLoader.isFetching = false;
                        divider.firstChild.innerText = "滚动条到底后继续向下滚动将会加载下一页的帖子";
                    });

            }
            // 否则判断一下是否到底了,到底了就追加下一页的内容
            else if (Math.abs(document.documentElement.scrollHeight - (window.pageYOffset + window.innerHeight)) < 20) {
                let divider = getElementByXpath(document, "//tr[@class='tr2 spp-next-page-loader-divider'][last()]");
                nextPageLoader.AppendNextPageItems(itemListSelector, divider);
                nextPageLoader.UpdatePageList();
                divider.firstChild.innerText = `以下是第${page + 1}页`;
                window.history.pushState({}, 0, nextPageURL); // 将地址栏也改变了
                page += 1;
                nextPageLoader.nextPageDummy = null;
            }

        })
    }
}

async function automaticTaskCollection() {

    function setUIDsValue(uid, value) {
        let tmp = GMK.getValue("LastAutomaticTaskCollectionDate") || {};
        tmp[uid] = value;
        GMK.setValue("LastAutomaticTaskCollectionDate", tmp);
    }

    if (document.querySelector("#login_0")) {
        console.log(`尚未登录,不接任务`);
        return
    }

    let uid = document.querySelector("#menu_profile .ul2").innerHTML.match(/u\.php\?action-show-uid-(\d+)\.html/)[1];
    let uname = document.querySelector("#user-login a").innerText;

    console.log(GMK.getValue("LastAutomaticTaskCollectionDate"));
    let lastTime = GMK.getValue("LastAutomaticTaskCollectionDate") ?
        (parseInt(GMK.getValue("LastAutomaticTaskCollectionDate")[uid]) || 0) : 0;
    console.log(`${uname}[${uid}] 上次:${new Date(lastTime).toLocaleDateString()} ${new Date(lastTime).toLocaleTimeString()}`);


    if (new Date().getTime() - lastTime < (3600 * 1000)) {
        console.log("再等等……");
        return;
    }


    async function forumTask(pageURL, selector, jobType) {
        let dummy = await fetch(
            pageURL,
            FETCH_CONFIG)
            .then(response => response.text())
            .then(html => {
                let dummy = document.createElement("html");
                dummy.innerHTML = html;
                return dummy
            })
            .catch(err => console.error(err));

        async function t(task) {
            let job = task.getAttribute("onclick");
            let r = job.match(/startjob\('(\d+)'\);/);
            let jobID = r[1];
            let taskURL = `/plugin.php?H_name=tasks&action=ajax&actions=${jobType}&cid=${jobID}&nowtime=${new Date().getTime()}&verify=${verifyhash}`;


            await fetch(taskURL, FETCH_CONFIG)
                .then(response => response.text())
                .then(html => {
                        console.log(html);
                        if (html.includes("success\t")) toast(html.match(/!\[CDATA\[success\t(.+)]]>/)[1], ToastType.SUCCESS);
                    }
                )
                .catch(err => console.error(err));
        }

        await dummy.querySelectorAll(selector).forEach(t);

        console.log(`${pageURL} done, ${new Date().getTime()}`)
    }

    for (let i = 0; i < 2; i++) {
        forumTask(
            "/plugin.php?H_name-tasks.html",
            "a[title=按这申请此任务]",
            "job"
        ).catch(err => console.error(err));
        await sleep(3000);
        forumTask(
            "/plugin.php?H_name-tasks-actions-newtasks.html.html",
            "a[title=领取此奖励]",
            "job2"
        ).catch(err => console.error(err));
    }
    console.log(`${uname}[${uid}], 本次领取时间:${new Date().getTime()}`);
    setUIDsValue(uid, new Date().getTime());

}

function blockAdforumSearchResult(target = document) {
    target.querySelectorAll(".tr3.tac").forEach(ele => {
        let forum = ele.childNodes[2];
        if (forum.firstChild.getAttribute("href").match(/fid-17[1-4]/)) {
            ele.style.display = "none";
        }
    });
}

function createFloatDraggableButton(text, GMKey, style) {
    let btn = document.createElement("button");
    let main = document.getElementById("main");
    main.appendChild(btn);

    btn.innerText = text;
    btn.setAttribute("id", "spp-float-draggable-button");
    btn.setAttribute("draggable", "true");
    btn.style.display = "block";
    btn.style.position = "fixed";
    btn.style.background = "#efefef";
    btn.style.zIndex = "99";
    btn.style.width = "30px";
    btn.style.padding = "10";
    btn.style.borderRadius = "1px";

    if (style) {
        for (const k in style) {
            btn.style[k] = style[k];
        }
    }

    let GM_style;
    if (GMKey) GM_style = GMK.getValue(GMKey);
    if (GM_style) {
        for (const k in GM_style) {
            btn.style[k] = GM_style[k];
        }
    }

    return btn;

}

function mark() {
    if (document.location.href.includes("/read.php")) {

        const GREY = "linear-gradient(to top, rgb(184 184 184), rgb(188 188 188))";
        const BLACK = "linear-gradient(to top, #313131,#000000)";
        const GMKey = "Style_markPlusPlus";

        let markButton = createFloatDraggableButton(
            MppManager.isMarked(tid) ? "MARKED" : "MARK",
            GMKey,
            {
                left: "calc(50vw + 470px)",
                top: "234px",
                background: MppManager.isMarked(tid) ? GREY : BLACK,
                color: "white",
                fontWeight: "bold",
                outline: "none",
                border: "none",
                borderRadius: "3px",
                width: "30px",
                opacity: MppManager.isMarked(tid) ? "0.4" : "0.8",
                cursor: "pointer",
            },
        );

        let dragStart = {};
        let dragEnd = {};
        // 防止拖到视口以外了
        AddIntersectionObserver(([entry]) => {
            if (!entry.isIntersecting) {
                // console.log('LEAVE');
                markButton.style.left = dragStart['saved']['left'];
                markButton.style.top = dragStart['saved']['top'];
            }
        }, markButton)

        markButton.addEventListener("click", async evt => {
            evt.stopPropagation();

            if (MppManager.isMarked(tid)) {
                MppManager.deleteMarkedThread(tid);
                evt.target.style.background = BLACK;
                evt.target.innerText = "MARK";
                evt.target.style.opacity = "0.8";
            } else {
                MppManager.addMarkThread(tid);
                evt.target.style.background = GREY;
                evt.target.innerText = "MARKED";
                evt.target.style.opacity = "0.4";
                // 第一次mark就先把基本内容给收录了
                let threadStatus = {};

                threadStatus['page'] = 1;
                threadStatus['lastFetchTime'] = 0;
                threadStatus['maxPage'] = totalpage;
                threadStatus['title'] = document.querySelector('.crumbs-item.current strong>a').textContent;
                threadStatus["markTime"] = new Date().toLocaleDateString();
                MppManager.setThreadStatus(tid, threadStatus);
                console.log(MppManager.getMarkList());
            }


        });

        markButton.addEventListener("contextmenu", openStatus);

        markButton.addEventListener("dragstart", (e) => {
            e.stopPropagation();
            dragStart = {
                clientX: e.clientX,
                clientY: e.clientY,
                saved: {
                    left: e.target.style.left,
                    top: e.target.style.top,
                }
            }
        });

        markButton.addEventListener("dragend", (e) => {
            e.stopPropagation();
            // 获得丅的交叉点坐标
            let startX = window.innerWidth / 2;
            let startY = 0;
            dragEnd = {
                clientX: e.clientX,
                clientY: e.clientY,
            }
            let newLeft = parseFloat(e.target.style.left.match(/(-?\d+)px/)[1]) + (dragEnd.clientX - dragStart.clientX)
            let newTop = parseFloat(e.target.style.top.match(/(-?\d+)px/)[1]) + (dragEnd.clientY - dragStart.clientY);


            e.target.style.left = `calc(50vw + ${newLeft}px)`;
            e.target.style.top = `${newTop}px`;

            let tmp = {
                ...GMK.getValue(GMKey),
                ...{
                    left: `calc(50vw + ${newLeft}px)`,
                    top: `${newTop}px`,
                }
            };
            GMK.setValue(GMKey, tmp);
        });
    }
    let menuButton = document.createElement("li");
    let a = document.createElement("a");
    a.innerText = "我的MARK";
    a.style.cursor = "pointer";
    a.classList.add("mpp-status");
    menuButton.appendChild(a);
    document.querySelector("#main").insertAdjacentHTML("afterbegin", `
   <style>
        .mpp{
            position: fixed;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            background: white;
            z-index: 2000000;
        }
        .mpp-mask{
            position: fixed;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background: black;
            opacity: 0.5;
            user-select: none;
            z-index: 1000000;
        }
        .mpp-container{
            background: #eeeeee;
            display:grid;
            grid-template-areas:
            "title"
            "main";
            grid-template-rows: 28px auto;
            grid-gap: 10px;
            min-height: 80vh;
            max-height: 80vh;
            width: 900px;
            overflow-y: hidden;
        }
        .mpp-title{
            grid-area: title;
            background: #111111;
            border-left: black;
            border-right: black;
            text-align: center;
            font-weight: bold;
            height: 100%;
            color: white;
            padding-top: 5px;
            margin: 0;
        }

        .mpp-main{
            grid-area: main;
            height: 75vh;
            overflow-y: scroll;
            overflow-x: hidden;

        }


        .spp-hide{
            display: none;
        }

        .mpp-accordion{
            width: 100%; 
            border: none;
            outline: none;
            background-color: whitesmoke;
            text-align: left;
            padding: 10px 10px;
            font-size: 12px;
            /*font-weight: bold;*/
            color: #444;
            cursor: pointer;
            transition: background-color 0.2s linear;
            display: inline-grid;
            grid-template-columns:1fr 4fr 1fr 1fr 1fr 1fr 1fr;
            align-items: center;
            justify-items: center;
            margin-bottom: 4px;
            box-shadow: 1px 2px 2px #AAAAAA;
            
        }
        .mpp-header{
            width: 100%;
            display: inline-grid;
            grid-template-columns:1fr 4fr 1fr 1fr 1fr 1fr 1fr; 
            padding: 10px 10px;
            font-size: 12px;
            align-items: center;
            justify-items: left;
        }

        /*.mpp-accordion-plus{ */
        /*    font-size: 14px;*/
        /*    float: right;*/
        /*}*/

        button.mpp-accordion:before{
            content: '无';
            font-size: 10px;
            color: gray;
        }
        button.mpp-accordion.have-content:before{
            content: '+';
            font-size: 14px;
            font-weight: bold;
            color: black;
        }
        button.mpp-accordion.have-content.mpp-accordion-is-open:before{
            content: '-';
            font-size: 14px;
            font-weight: bold;
            color: black;
        }
        button.mpp-accordion:hover, button.mpp-accordion.mpp-accordion-is-open{
            background-color: #ddd;
        }

        .mpp-accordion-content{
            background: #eeeeee;
            border-left: 1px solid whitesmoke;
            border-right: 1px solid whitesmoke;
            padding: 0 20px;
            margin-bottom: 1px;
            max-height: 0;
            overflow: hidden;
            font-size: 10px;
        }

        .mpp-accordion-content.mpp-accordion-is-open{
            max-height: fit-content;
        }

        .mpp-sticky{
            position: sticky;
            top: 0;
        }

        .mpp-accordion-op{
            display: flex;

            justify-content: end;
            align-content: center;
        }
        .mpp-accordion-op a{
            padding: 5px;
            margin-left: 20px;
            font-size: 12px;
            cursor: pointer;
        }
        a.mpp-delete{
            text-align: right;
            color: brown;
        }
        
        a.mpp-sell{
            color: blueviolet;
            font-weight: bold;
        }
        
        a.mpp-hyperlink{
            color: blue;
        }
        a.mpp-hash{
            color: forestgreen;
        }
        
        .mpp-content-cell.mpp-content-title,
        .mpp-content-cell.mpp-content-last-fetch-time{
            justify-self: left; 
        }
        .mpp-content-cell.mpp-content-last-fetch-time{
            padding-left: 1em;
        }
        a.mpp-status{
            /*font-weight: bold;*/
            color: dodgerblue;
            cursor:pointer;
        }
        span.mpp-content-result{
            justify-self: center;
        }

    </style> 
    <div class="mpp-mask spp-hide"></div> 
    <div class="mpp spp-hide">
            <div class="mpp-container">
                <p class="mpp-title">我的Mark(保持此窗口开启才会运行)</p>
                <div class="mpp-main">
                    <div class="mpp-accordion-op mpp-sticky">
                        <a class="mpp-accordion-expand-all">全部展开</a>
                        <a class="mpp-accordion-collapse-all">全部折叠</a>  
                    </div>
                    <div class="mpp-header" >
                        <span class="mpp-header-cell mpp-content-result"></span> 
                        <span class="mpp-header-cell mpp-content-title" >帖子标题</span> 
                        <span class="mpp-header-cell">页数</span> 
                        <span class="mpp-header-cell mpp-content-last-fetch-time">检查时间</span> 
                        <span class="mpp-header-cell">悬赏状态</span> 
                        <span class="mpp-header-cell">MARK时间</span> 
                        <a class="mpp-delete mpp-content-cell" data-tid="1274464"></a>
                    </div>
                    <div class="mpp-content-container">
                        
                    </div>
                </div> 
            </div> 
        </div>
    
`);
    document.querySelector("#guide").prepend(menuButton);
    // document.querySelector('.fl>.gray2>.fl:first-child').insertAdjacentText("beforeend",
    //     `, `);
    // document.querySelector('.fl>.gray2>.fl:first-child').insertAdjacentHTML("beforeend",
    //     `<a class="mpp-status">我的MARK</a>`);
    document.querySelector(".mpp-accordion-expand-all").addEventListener("click", evt => {

        document.querySelectorAll(".mpp-accordion").forEach(ele => {
            if (!ele.nextElementSibling.querySelectorAll("p>a").length) return;
            if (!ele.classList.contains(" mpp-accordion-is-open")) ele.classList.add("mpp-accordion-is-open");
        });
        document.querySelectorAll(".mpp-accordion-content").forEach(ele => {
            if (!ele.querySelectorAll("p>a").length) return;
            if (!ele.classList.contains("mpp-accordion-is-open")) ele.classList.add("mpp-accordion-is-open");
            ele.style.maxHeight = ele.scrollHeight + 'px';
        });
    });
    document.querySelector(".mpp-accordion-collapse-all").addEventListener("click", evt => {
        document.querySelectorAll(".mpp-accordion").forEach(ele => {
            if (ele.classList.contains("mpp-accordion-is-open")) ele.classList.remove("mpp-accordion-is-open")
        });
        document.querySelectorAll(".mpp-accordion-content").forEach(ele => {
            if (ele.classList.contains("mpp-accordion-is-open")) ele.classList.remove("mpp-accordion-is-open")
            ele.style.maxHeight = null;
        });
    });

    // 用于tab之间广播通讯,只允许一个tab运行mark++
    const bc = new BroadcastChannel("Soul++:MppTaskStart");


    let refreshID;


    // 自己不会接到
    bc.onmessage = async msg => {
        console.log('BroadcastChannel:', msg.data);
        if (msg.data.includes("mppTaskStart")) {
            closeMenu(null);
            // toast("由于你在别的标签打开了“我的MARK”,此标签的“我的MARK”被关闭了",ToastType.WARNING, 99999 * 1000);
        }
    };


    function insertDataHTML() {
        let container = document.querySelector(".mpp-content-container");
        let threadsStatus = MppManager.getAllThreadStatus()
        let insertHTML = ``;
        for (const [_tid, status] of Object.entries(threadsStatus)) {
            let posts = "";
            if (status['sell']) status["sell"].forEach(ele => posts += `<p><a class="mpp-sell" href="${ele}" target="_blank">[出售]${ele}</a></p>`);
            if (status['hyperlink']) status["hyperlink"].forEach(ele => posts += `<p><a class="mpp-hyperlink" href="${ele}" target="_blank">[超链]${ele}</a></p>`);
            if (status["magnetOrMiaochuan"]) status["magnetOrMiaochuan"].forEach(ele => posts += `<p><a class="mpp-hash" href="${ele}" target="_blank">[磁力或秒传]${ele}</a></p>`);
            let button = container.querySelector(`button.mpp-accordion[data-tid="${_tid}"`);
            if (button) button.classList.remove("have-content");
            let content = container.querySelector(`div.mpp-accordion-content[data-tid="${_tid}"`);
            // console.log(button ? button.classList.toString() : "mpp-accordion");
            // console.log(content ? content.classList.toString() : "mpp-accordion-content");
            // <span class="mpp-content-cell mpp-accordion-plus">${posts === "" ? "" : button.classList.contains("") ? "-" : "+"}</span>
            insertHTML += `
            <button type="button" class="${button ? button.classList.toString() : "mpp-accordion"} ${posts ? "have-content" : ""}" data-tid="${_tid}">
                <a 
                class="mpp-content-cell mpp-content-title"  
                href="/read.php?tid=${_tid}" 
                target="_blank"
                >${status["title"].slice(0, 20)}${status["title"].length > 20 ? "..." : ""}</a> 
                <span class="mpp-content-cell">${status["page"] || 0} / ${status["maxPage"]}</span> 
                <span class="mpp-content-cell mpp-content-last-fetch-time">${status["lastFetchTime"] ? `${Math.round((getTimeStamp() - parseInt(status["lastFetchTime"])) / 1000 / 60)} 分钟之前` : '尚未检查'}</span> 
                <span class="mpp-content-cell">${status["offerState"]}</span> 
                <span class="mpp-content-cell">${status["markTime"]}</span> 
                <a class="mpp-delete mpp-content-cell" data-tid="${_tid}">删除</a>
            </button>

            <div class="${content ? content.classList.toString() : "mpp-accordion-content"}" data-tid="${_tid}">
                ` + posts + `
            </div>
            `;

        }
        container.innerHTML = insertHTML;
        document.querySelectorAll("button.mpp-accordion").forEach(ele => {
            ele.addEventListener("click", evt => {
                evt.stopPropagation();
                if (!evt.currentTarget.nextElementSibling.querySelectorAll("p>a").length) return;
                let btn = evt.currentTarget;
                let content = btn.nextElementSibling;
                btn.classList.toggle("mpp-accordion-is-open");
                content.classList.toggle("mpp-accordion-is-open");
                content.style.maxHeight = content.classList.contains("mpp-accordion-is-open") ? content.scrollHeight + 'px' : null;
                // evt.currentTarget.querySelector(".mpp-accordion-plus").textContent = content.classList.contains("mpp-accordion-is-open") ? "-" : "+";
            });

        });
        document.querySelectorAll(".mpp-delete.mpp-content-cell").forEach(ele => {
            ele.addEventListener("click", evt => {
                evt.stopPropagation();
                evt.preventDefault();
                if (!confirm("删除后无法撤销,确定删除?")) return;
                const parentButton = evt.currentTarget.closest("button")
                const content = parentButton.nextElementSibling;
                parentButton.remove();
                content.remove();
                MppManager.deleteMarkedThread(evt.currentTarget.dataset.tid);

            })
        });
    }

    function openStatus(evt) {
        evt.stopPropagation();
        evt.preventDefault();
        // 显示数据
        insertDataHTML();

        // 显示菜单
        let sppMenu = document.querySelector(".mpp");
        sppMenu.classList.remove("spp-hide");
        // 显示遮罩
        let sppMenuMask = document.querySelector(".mpp-mask");
        sppMenuMask.classList.remove("spp-hide");
        // 防止滚动到菜单后面的页面
        document.body.style.overflow = "hidden";

        bc.postMessage('mppTaskStart');
        setTimeout(mppTask, 5000);
        sessionStorage.setItem("Soul++:MppTaskID", 'start');
        refreshID = setInterval(insertDataHTML, 1000);
    }

    function closeMenu(evt) {
        if (evt) evt.stopPropagation();
        document.body.style.overflow = null;
        let sppMenu = document.querySelector(".mpp");
        sppMenu.classList.add("spp-hide");
        let sppMenuMask = document.querySelector(".mpp-mask");
        sppMenuMask.classList.add("spp-hide");
        // clearInterval(parseInt(sessionStorage.getItem("Soul++:MppTaskID")));
        sessionStorage.setItem("Soul++:MppTaskID", 'stop');
        clearInterval(refreshID);
    }

    document.querySelector("a.mpp-status").addEventListener("click", openStatus)

    document.querySelector(".mpp-mask").addEventListener("click", closeMenu);


}

function backToTop() {

    const GMKey = "Style_backToTop";
    let backToTopButton = createFloatDraggableButton(
        "回到顶部",
        GMKey,
        {
            left: "calc(50vw + 470px)",
            bottom: "40px",
            background: "linear-gradient(to top, #eeeeee,#ffffff)",
            color: "black",
            fontWeight: "bold",
            outline: "none",
            border: "none",
            borderRadius: "3px",
            width: "30px",
            opacity: "80%",
            cursor: "pointer",
        }
    );

    let dragStart = {};
    let dragEnd = {};

    // 防止拖到视口以外了
    AddIntersectionObserver(([entry]) => {
        if (!entry.isIntersecting) {
            console.log('LEAVE');
            backToTopButton.style.left = dragStart.saved.left;
            backToTopButton.style.bottom = dragStart.saved.bottom;
        }
    }, backToTopButton)

    backToTopButton.addEventListener("click", (e) => {
        e.stopPropagation();
        window.scrollTo({ top: 0, behavior: "smooth" });
    });

    backToTopButton.addEventListener("dragstart", (e) => {
        e.stopPropagation();

        dragStart = {
            clientX: e.clientX,
            clientY: e.clientY,
            saved: {
                left: e.target.style.left,
                bottom: e.target.style.bottom,
            }
        }
    });

    backToTopButton.addEventListener("dragend", (e) => {
        e.stopPropagation();
        // 获得丅的交叉点坐标
        dragEnd = {
            clientX: e.clientX,
            clientY: e.clientY,
        }
        let newLeft = parseFloat(e.target.style.left.match(/(-?\d+)px/)[1]) + (dragEnd.clientX - dragStart.clientX);
        // bottom从下往上算的,所以要减
        let newBottom = parseFloat(e.target.style.bottom.match(/(-?\d+)px/)[1]) - (dragEnd.clientY - dragStart.clientY);


        e.target.style.left = `calc(50vw + ${newLeft}px)`;
        e.target.style.bottom = `${newBottom}px`;

        let tmp = {
            ...GMK.getValue(GMKey),
            ...{
                left: `calc(50vw + ${newLeft}px)`,
                bottom: `${newBottom}px`,
            }
        };
        GMK.setValue(GMKey, tmp);


    });

}

function postAddAnchorAttribute(target, pg, threadID) {
    target.querySelectorAll(".tpc_content .f14").forEach(ele => {
        ele.setAttribute("page", pg);
        ele.setAttribute("tid", threadID);
        let pid = ele.previousElementSibling.getAttribute("name");
        ele.setAttribute("pid", pid);

    });
}

function threadAddAnchorAttribute(target, pg, forumID) {
    target.querySelectorAll(".tr3.t_one").forEach(ele => {
        ele.setAttribute("page", pg);
        ele.setAttribute("fid", forumID);
        let tid_m = ele.querySelector("a").getAttribute("href").match(/tid-(\d+)/);
        if (!tid_m) return;
        let tid = tid_m[1];
        ele.setAttribute("tid", tid);

    });
}

function highlightViewedThread() {

    function removeCurrent() {
        let prev = document.querySelector(".spp-last-viewed-thread");
        if (prev) prev.classList.remove("spp-last-viewed-thread");
    }

    function setLastViewed() {
        let tmp = GMK.getValue("Soul++:lastViewedThread") || {};
        tmp[fid] = tid;
        GMK.setValue("Soul++:lastViewedThread", tmp);
    }

    function setViewed() {
        let tmp = GMK.getValue("Soul++:viewedThreads") || {};
        tmp[fid] = tmp[fid] || [];
        if (!tmp[fid].includes(tid)) tmp[fid].push(tid);
        GMK.setValue("Soul++:viewedThreads", tmp);
    }

    // 帖子阅读页面处理
    if ((document.location.href.includes("/read.php"))) {
        // 直接打开页面的话不会触发visibilitychange事件
        if (!document.hidden) setViewed();
        // 当visibilitychange触发时,hidden代表用户关闭或者离开了当前页面
        document.addEventListener("visibilitychange", () => {
            if (document.hidden) {
                setLastViewed();
            } else {
                setViewed();
            }
        });
    }
    // 帖子列表页面处理
    else if ((document.location.href.includes("/thread.php") || document.location.href.includes("/thread_new.php"))) {
        // 在帖子列表页面会主动滚动到最后浏览的帖子的位置
        document.addEventListener('readystatechange', (event) => {
            if (document.readyState === "complete") {
                history.scrollRestoration = "manual";
                let ele = document.querySelector(".spp-last-viewed-thread");
                if (!ele) return;
                ele.scrollIntoView({ behavior: "auto", block: "center" });
            }
        });

        // 主动更新帖子列表页
        // GM_addValueChangeListener(name, function(name, old_value, new_value, remote) {})
        GMK.addValueChangeListener("Soul++:viewedThreads", (_name, oldVal, newVal, remote) => {
            console.log(`本版块已阅帖:${newVal[fid]}`);
            document.querySelectorAll(".tr3.t_one").forEach(ele => {
                if (newVal[fid].includes(ele.getAttribute("tid"))) {
                    ele.querySelector("h3 a").classList.add("spp-viewed-thread");
                }
            });
        })
        GMK.addValueChangeListener("Soul++:lastViewedThread", (_name, oldVal, newVal, remote) => {
            // 新记录中当前fid下正在阅读的tid和DOM树中一致的话则返回
            if (document.querySelector(".spp-last-viewed-thread").getAttribute("tid") === newVal[fid]) return;
            console.log(`正在本版块阅读新帖:${newVal[fid]}`);
            removeCurrent();
            document.querySelectorAll(".tr3.t_one").forEach(ele => {
                if (ele.getAttribute("tid") === newVal[fid]) {
                    ele.classList.add("spp-last-viewed-thread");
                    ele.scrollIntoView({ behavior: "auto", block: "center" });
                }
            });
        })

        // 将已经阅读过的帖子改成灰色
        let readedThreads = GMK.getValue("Soul++:viewedThreads") || {};
        let thisForumReadedThreads = readedThreads[fid] || [];
        // console.log(`当前版块已读帖子:${thisForumReadedThreads}`);
        document.querySelectorAll("h3 a").forEach(ele => {
            let container = ele.closest(".tr3.t_one");
            if (!container) return;

            if (thisForumReadedThreads.includes(container.getAttribute("tid"))) {
                console.log(`${container.getAttribute("tid")} 已读`);
                ele.classList.add("spp-viewed-thread");
            }
            if (ele.getAttribute("id") === `a_ajax_${GMK.getValue("Soul++:lastViewedThread")[fid]}`) {
                container.classList.add("spp-last-viewed-thread");
            }
            ele.addEventListener("click", e => {
                e.stopPropagation();
                removeCurrent();
                e.target.closest(".tr3.t_one").classList.add("spp-last-viewed-thread");
            });
        });
    }
}

function createSettingMenu() {
    let menuBox = document.createElement("div");
    document.querySelector("#main").prepend(menuBox);

    let menuButton = document.createElement("li");
    let a = document.createElement("a");
    a.innerText = "Soul++";
    a.style.cursor = "pointer";
    menuButton.appendChild(a);
    a.addEventListener("click", e => {
        e.stopPropagation();
        // 读取数据更显选项显示
        document.querySelectorAll(".spp-accordion-content").forEach(ele => {
            ele.querySelectorAll(".spp-menu-checkbox").forEach(ele => {
                let checkbox = ele.querySelector("input");
                if (checkbox) {
                    let key = checkbox.dataset.funckey;
                    checkbox.checked = GMK.getValue(key);
                }
            })
        });

        // 显示菜单
        let sppMenu = document.querySelector(".spp-menu");
        sppMenu.classList.remove("spp-hide");
        // 显示遮罩
        let sppMenuMask = document.querySelector(".spp-menu-mask");
        sppMenuMask.classList.remove("spp-hide");
        // 防止滚动到菜单后面的页面
        document.body.style.overflow = "hidden";

    });
    document.querySelector("#guide").prepend(menuButton);

    menuBox.outerHTML = `
<div class="spp-menu-mask spp-hide"></div>
<div class="spp-menu spp-hide">
    <div class="spp-menu-container">
        <p class="spp-menu-title">Soul++ 设置</p>
        <div class="spp-menu-main">
            <div class="spp-menu-accordion-op spp-sticky">
                <a class="spp-menu-accordion-support-me" style="grid-column-start: 1">支持作者</a>
                <a class="spp-menu-accordion-expand-all" style="grid-column-start: 4">全部展开</a>
                <a class="spp-menu-accordion-collapse-all" style="grid-column-start: 5">全部折叠</a>
            </div>
            <button type="button" class="spp-accordion spp-accordion-is-open">🔄 免刷新</button>
            <div class="spp-accordion-content spp-accordion-is-open">
                <div class="spp-menu-checkbox"><label><input data-funcKey="buyRefresh_free" type="checkbox" id="buy-refresh-free">购买免刷新</label></div>
            </div>


            <button type="button" class="spp-accordion spp-accordion-is-open">♾️ 无缝加载</button>
            <div class="spp-accordion-content spp-accordion-is-open">
                <div class="spp-menu-checkbox"><label><input data-funcKey="dynamicLoadingThreads" type="checkbox" id="dynamic-load-posts">无缝加载板块帖子列表</label></div>
                <div class="spp-menu-checkbox"><label><input data-funcKey="dynamicLoadingPosts" type="checkbox" id="dynamic-load-threads">无缝加载贴内楼层列表</label></div>
                <div class="spp-menu-checkbox"><label><input data-funcKey="dynamicLoadingSearchResult" type="checkbox" id="dynamic-load-search-result">无缝加载搜索页结果</label></div>
                <div class="spp-menu-checkbox"><label><input data-funcKey="dynamicLoadingPicWall" type="checkbox" id="dynamic-load-pic-wall">无缝加载图墙模式帖子</label></div>
            </div>

            <button type="button" class="spp-accordion spp-accordion-is-open">🛑 屏蔽</button>
            <div class="spp-accordion-content spp-accordion-is-open">
                <div class="spp-menu-checkbox"><label><input data-funcKey="blockAdforumSearchResult" type="checkbox" id="block-adforum-search-result">屏蔽网赚区搜索结果</label></div>
            </div>

            <button type="button" class="spp-accordion spp-accordion-is-open">🔞 SFW安全模式</button>
            <div class="spp-accordion-content spp-accordion-is-open">
                <div class="spp-menu-checkbox"><label><input data-funcKey="hidePostImage" type="checkbox" id="hide-post-image">折叠贴内图片(点击虚线框 展开/折叠 图片)</label></div>
                <div class="spp-menu-checkbox spp-menu-sub-item"><label><input data-funcKey="loadImageOnDemand" type="checkbox" id="load-image-on-demand">按需加载头像、图片(展开后才开始加载)</label></div>
                <div class="spp-menu-checkbox"><label><input data-funcKey="hideForumRules" type="checkbox" id="hide-chaguan-poster">折叠板块公告(其实板块公告右边有个小箭头,我只是帮你们点了一下)</label></div>
                <div class="spp-menu-checkbox"><label><input data-funcKey="hideUserAvatar" type="checkbox" id="hide-user-avatar">替换用户头像为默认(鼠标滑入查看)</label></div>
            </div>
            <button type="button" class="spp-accordion spp-accordion-is-open">🔖 mark++</button>
            <div class="spp-accordion-content spp-accordion-is-open">
                <div class="spp-menu-checkbox"><label><input data-funcKey="markPlusPlus" type="checkbox" id="mark-plus-plus">开启MARK++</label></div>
                <div class="spp-menu-checkbox"><label class="spp-menu-description">- 打开后查看帖子页面右边会出现MARK按钮(可拖到任意位置)</label></div>
                <div class="spp-menu-checkbox"><label class="spp-menu-description">- 点击MARK之后,当前帖子会加入到“我的MARK”列表里</label></div>
                <div class="spp-menu-checkbox"><label class="spp-menu-description">- 在导航栏可以找到“我的MARK”入口,右键点击MARK按钮也可以打开“我的MARK”</label></div>
                <div class="spp-menu-checkbox"><label class="spp-menu-description" style="color: brown">- 保持打开“我的MARK”窗口,脚本会以5秒/帖的频率检查MARK列表</label></div>
                <div class="spp-menu-checkbox"><label class="spp-menu-description" style="color: brown">- 同一时间只允许一个浏览器标签打开“我的MARK”</label></div>
            </div>
            <button type="button" class="spp-accordion spp-accordion-is-open">💠 其它</button>
            <div class="spp-accordion-content spp-accordion-is-open">
                <div class="spp-menu-checkbox"><label><input data-funcKey="automaticTaskCollection" type="checkbox" id="automatic-task-collection">自动领取和完成论坛任务</label></div>
                <div class="spp-menu-checkbox"><label><input data-funcKey="hoistingResourcePost" type="checkbox" id="hoisting-resource-post">将当前页包含[购买/秒传/磁力链/超链]的楼层提升到前面</label></div>
                <div class="spp-menu-checkbox"><label><input data-funcKey="replaceAllDomainToTheSame" type="checkbox" id="replace-all-plus-to-the-same">统一替换所有plus链接为当前正在使用的域名</label></div>
                <div class="spp-menu-checkbox"><label><input data-funcKey="highlightViewedThread" type="checkbox" id="highlight-viewed-threads">标记已阅读过的帖子</label></div>
                <div class="spp-menu-checkbox"><label><input data-funcKey="linkToReplyAndQuote" type="checkbox" id="link-to-reply-and-quote">给[回复第X楼/引用第X楼]增加跳转到该楼层的链接</label></div>
            </div>
            <button type="button" class="spp-accordion spp-danger">❗</button>
            <div class="spp-accordion-content spp-danger-content">
                <button class="spp-btn-danger" data-funcKey="resetAll" id="spp-reset-all">清空所有设置</button>
            </div>

        </div>
        <div class="spp-menu-op-zone">
            <button id="spp-menu-close"><img alt="" src="images/post/smile/smallface/face099.jpg"/> 我好了</button>
        </div>
    </div>
</div>
 
`;

    (function () {
        function closeMenu(evt, saveAndRefresh) {
            evt.stopPropagation();
            document.body.style.overflow = null;
            let sppMenu = document.querySelector(".spp-menu");
            sppMenu.classList.add("spp-hide");
            let sppMenuMask = document.querySelector(".spp-menu-mask");
            sppMenuMask.classList.add("spp-hide");
            if (saveAndRefresh) {
                changes.forEach(e => GMK.setValue(e[0], e[1]))
                document.location.reload();
            }
            changes = [];

        }

        window.addEventListener("keydown", evt => {
            if (evt.key === "Escape") closeMenu(evt, false);
        });
        let changes = [];
        document.querySelectorAll(".spp-menu-checkbox input").forEach(ele => {
            ele.addEventListener("change", evt => {
                changes.push([evt.currentTarget.dataset.funckey, evt.currentTarget.checked]);
            });
        });

        document.querySelector("#spp-menu-close").addEventListener("click", evt => closeMenu(evt, true));
        document.querySelector(".spp-menu-mask").addEventListener("click", evt => closeMenu(evt, false));
        document.querySelector("#spp-reset-all").addEventListener("click", evt => {
            evt.stopPropagation();

            if (confirm(
                `
                【警告】
                这将会清空你在所有的数据和设置,包括:
                - 已读的帖子
                - 可拖放按钮的位置
                - 已经MARK过的帖子
                
                你确定要这样做?`
            )) {
                GMK.listValues().forEach(vName => {
                    console.log(vName);
                    GMK.deleteValue(vName);
                });
                console.log(GMK.listValues());
                document.location.reload();
            }
        });
        document.querySelector(".spp-menu-accordion-support-me").addEventListener("click", evt => {
            evt.stopPropagation();
            let toastTip = Toastify({
                text: "点我或者点击图片即可关闭",
                duration: 15000,
                close: true,
                gravity: "top", // `top` or `bottom`
                position: "center", // `left`, `center` or `right`
                stopOnFocus: true, // Prevents dismissing of toast on hover
                style: {},
                onClick: function () {
                    toastTip.hideToast();
                    toastRedEnvelop.hideToast();
                }
            })
            toastTip.showToast();
            let img = document.createElement("img");
            img.style.background = "none";
            img.style.boxShadow = "none";
            img.style.borderRadius = "10px";
            img.style.width = "300px";
            img.style.height = "435px";
            img.src = 'https://cdn.jsdelivr.net/gh/FetchTheMoon/UserScript/images/RedEnvelope.jpg';
            let toastRedEnvelop = Toastify({
                node: img,
                duration: 9999999,
                close: false,
                gravity: "top", // `top` or `bottom`
                position: "center", // `left`, `center` or `right`
                stopOnFocus: true, // Prevents dismissing of toast on hover
                style: {
                    background: "none",
                    boxShadow: "none",
                },
                onClick: function () {
                    toastTip.hideToast();
                    toastRedEnvelop.hideToast();
                }
            });
            toastRedEnvelop.showToast();
        });
        document.querySelector(".spp-menu-accordion-expand-all").addEventListener("click", evt => {

            document.querySelectorAll(".spp-accordion").forEach(ele => {
                if (ele.classList.contains("spp-danger")) return;
                if (!ele.classList.contains(" spp-accordion-is-open")) ele.classList.add("spp-accordion-is-open");
            });
            document.querySelectorAll(".spp-accordion-content").forEach(ele => {
                if (ele.classList.contains("spp-danger-content")) return;
                if (!ele.classList.contains("spp-accordion-is-open")) ele.classList.add("spp-accordion-is-open");
                ele.style.maxHeight = ele.scrollHeight + 'px';
            });
        });
        document.querySelector(".spp-menu-accordion-collapse-all").addEventListener("click", evt => {
            document.querySelectorAll(".spp-accordion").forEach(ele => {
                if (ele.classList.contains("spp-accordion-is-open")) ele.classList.remove("spp-accordion-is-open")
            });
            document.querySelectorAll(".spp-accordion-content").forEach(ele => {
                if (ele.classList.contains("spp-accordion-is-open")) ele.classList.remove("spp-accordion-is-open")
                ele.style.maxHeight = null;
            });
        });
        document.querySelectorAll("button.spp-accordion").forEach(ele => {
            ele.addEventListener("click", evt => {
                evt.stopPropagation();
                let btn = evt.target;
                let content = btn.nextElementSibling;
                btn.classList.toggle("spp-accordion-is-open");
                content.classList.toggle("spp-accordion-is-open");
                content.style.maxHeight = content.classList.contains("spp-accordion-is-open") ? content.scrollHeight + 'px' : null;
            });

        });
    }());
}

function replaceAllDomainToTheSame() {


    const arr = [...document.querySelectorAll("a")];
    const domains = [
        "spring-plus.net",
        "summer-plus.net",
        "soul-plus.net",
        "south-plus.net",
        "north-plus.net",
        "snow-plus.net",
        "level-plus.net",
        "white-plus.net",
        "imoutolove.me",
        "south-plus.org",
    ];
    const checker = value => domains.some(element => value.href.includes(element));

    arr.filter(checker).filter(ele => !ele.href.includes(window.location.hostname)).forEach(ele => {
        console.log("替换链接:", ele);
        let newURL = new URL(ele.href);
        newURL.hostname = window.location.hostname;
        ele.href = newURL.href;
    });


}

function MutationObserverProcess() {
    function callback(mutationList, observer) {
        mutationList.forEach((mutation) => {
            mutation.addedNodes.forEach(ele => {

                if (ele.tagName === "IMG") {
                    if (ele.classList.contains("spp-mutation-processed")) return

                    function hide(confirmSelector, handler) {
                        const postContainer = ele.closest(confirmSelector);
                        if (!postContainer) return
                        handler(ele);
                        ele.classList.add("spp-mutation-processed");
                    }

                    if (document.location.href.includes("/read.php")) {
                        ele.setAttribute("loading", "lazy");
                        if (GMK.getValue("hideUserAvatar")) hide(".user-pic", hideAvatar)
                        if (GMK.getValue("hidePostImage")) hide(".t5.t2 .r_one", hideImg)
                    }
                }
                if (document.location.href.includes("/read.php") && GMK.getValue("linkToReplyAndQuote")) {
                    if (ele.title === "复制此楼地址") {
                        ele.insertAdjacentHTML("beforebegin", `<a name="SPP-${ele.innerText}" id="SPP-${ele.innerText}"></a>`);
                    }
                }
            });
        });


    }

    let observerOptions = {
        childList: true,  // 观察目标子节点的变化,是否有添加或者删除
        attributes: true, // 观察属性变动
        subtree: true     // 观察后代节点,默认为 false
    }

    let observer = new MutationObserver(callback);
    observer.observe(document.documentElement, observerOptions);
}

async function mppTask() {
    let taskInterval = 5000;
    await (async function () {

        // 先查询页数没到最后一页的
        let markedList = Object.entries(MppManager.getMarkList()).filter(e => {
            return e[1]['maxPage'] - e[1]['page'] > 0
        });
        // 如果都到最后一页了,就按上次查询时间距今最远的来
        if (!markedList.length) {
            markedList = Object.entries(MppManager.getMarkList())
                .sort((f, s) => {
                    return f[1]['lastFetchTime'] - s[1]['lastFetchTime']
                });
        }
        console.log(markedList);
        if (!markedList.length) return;
        if (MppManager.isAllChecked()) taskInterval = 10 * 1000;
        let [_tid, threadStatus] = markedList[0];
        let maxPage = threadStatus['maxPage'] || 1;
        let currentPage = threadStatus["page"] || 1;
        const dummy = await getPage(`/read.php?tid=${_tid}&page=${currentPage}`, true);
        const m = dummy.innerHTML.match(/var totalpage = parseInt\('(\d+)'\)/);
        if (m) maxPage = parseInt(m[1]);
        const allPosts = [...dummy.querySelectorAll(".t5.t2")];

        function getOfferState(dummy) {
            const ele = dummy.querySelector(".tips .s3");
            if (!ele) {
                console.log("没找到悬赏状态", ele, dummy);
                return;
            }
            const state = ele.textContent;
            if (state.includes("剩余时间:已结束")) {
                return "悬赏超时";
            } else if (state.includes("悬赏结束")) {
                return `<a class="mpp-sell" href='/read.php?tid=${_tid}' target="_blank" style="color: #73a5ff">有答案了</a>`;
            } else if (state.includes("悬赏中")) {
                return `剩余${state.match(/(\d+)小时/)[1]}小时`;
            }
        }

        if (currentPage === 1) {
            threadStatus["offerState"] = getOfferState(dummy);
            allPosts.shift();
        }
        // 在第一页获取一下帖子的状态,看看到底有没有结贴
        if (threadStatus["allPagesChecked"]) {
            await sleep(5000); // 否则会遇到提示刷新小于1秒;
            const page1dummy = await getPage(`/read.php?tid=${_tid}&page=1`, true);
            threadStatus["offerState"] = getOfferState(page1dummy);
        }
        threadStatus["sell"] = threadStatus["sell"] || [];
        threadStatus["hyperlink"] = threadStatus["hyperlink"] || [];
        threadStatus["magnetOrMiaochuan"] = threadStatus["magnetOrMiaochuan"] || [];
        threadStatus['title'] = dummy.querySelector('.crumbs-item.current strong>a').textContent;
        threadStatus['lastFetchTime'] = getTimeStamp();
        threadStatus['maxPage'] = maxPage;
        threadStatus["page"] += currentPage < maxPage ? 1 : 0;
        const getPid = (post) => {
            return post.previousSibling.getAttribute("name");
        }
        allPosts.forEach(post => {

            let u = `/read.php?tid=${_tid}&page=${currentPage}#${getPid(post)}`;
            if (post.querySelector(".quote.jumbotron")) {
                if (!threadStatus["sell"].includes(u)) threadStatus["sell"].push(u)
            } else if (post.querySelector(".tpc_content a")) {
                if (!threadStatus["hyperlink"].includes(u)) threadStatus["hyperlink"].push(u);
            } else if (post.querySelector(".tpc_content").textContent.match(/^[0-9a-fA-F]{20,}/mg)) {
                if (!threadStatus["magnetOrMiaochuan"].includes(u)) threadStatus["magnetOrMiaochuan"].push(u);
            }
        });


        threadStatus["allPagesChecked"] = currentPage === maxPage;
        if (MppManager.isThreadExist(_tid)) MppManager.setThreadStatus(_tid, threadStatus);
        console.log(threadStatus);

    })();
    if (sessionStorage.getItem("Soul++:MppTaskID") === 'start') setTimeout(mppTask, taskInterval);

}

function linkToReplyAndQuote(target = document) {
    if (window.location.hash.includes("#SPP-")) {
        const ele = document.querySelector(window.location.hash);
        if (ele) ele.scrollIntoView();
    }

    let allPosts = [...target.querySelectorAll(".t5.t2")];
    allPosts.forEach(ele => {
        const quote = ele.querySelector("h6.quote2+div");
        if (quote) {
            const floor = parseInt(quote.firstChild.textContent.match(/引用第(\d+)楼/)[1]);
            const page = Math.ceil(floor / 30);
            const text = quote.firstChild.textContent.replace(/引用(第\d+楼)(.+)/, `引用<a style="color: dodgerblue" href="/read.php?tid=${tid}&page=${page}#SPP-B${floor}F">$1</a>$2`);
            quote.removeChild(quote.firstChild);
            quote.insertAdjacentHTML("afterbegin", text);

        }
        const reply = ele.querySelector(".h1.fl>.fl");
        if (reply) {
            const m = reply.firstChild.textContent.match(/回 (\d+)楼/);
            if(!m) return
            const floor = parseInt(m[1]);
            const page = Math.ceil(floor / 30);
            const text = reply.innerText.replace(/回 (\d+)楼(.+)/, `回 <a style="color: dodgerblue" href="/read.php?tid=${tid}&page=${page}#SPP-B${floor}F">$1楼</a>$2`);
            reply.innerText = "";
            reply.insertAdjacentHTML("afterbegin", text);

        }
    });
}

function hoistingResourcePost(target = document) {
    if (document.location.href.includes("#")) return;
    let allPosts = [...target.querySelectorAll(".t5.t2")];

    // 是否拿出顶楼
    const insertPosition = page === 1 ? allPosts.shift() : target.querySelector("input[name=tid]");
    const resourcePosts = allPosts.filter(post =>
        post.querySelector(".quote.jumbotron")
        || post.querySelector(".tpc_content a")
        || post.querySelector(".tpc_content").textContent.match(/^[0-9a-fA-F]{20,}/mg)
    ).reverse();
    resourcePosts.forEach(ele => insertPosition.after(ele));

}

function AddIntersectionObserver(callback, element) {
    const obs = new window.IntersectionObserver(callback, {
        root: null,
        threshold: 0.5,
    })
    obs.observe(element);
}

//##############################################################
// 执行入口
//##############################################################
(function () {

    GMK.addStyle(`<style>
    .spp-last-viewed-thread{
        background: #deeeff;
    }
    .spp-viewed-thread{
        color: #bbbbbb;
    }
    .spp-hide{
        display: none;
    }
    
    
        
    .spp-menu{
        position: fixed;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        background: white; 
        z-index: 2000000; 
    }
    .spp-menu-mask{
        position: fixed;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        background: black;
        opacity: 0.5;
        user-select: none;
        z-index: 1000000; 
    }
    .spp-menu-container{
        background: #eeeeee;
        display:grid;
        grid-template-areas:
            "title"
            "main"
            "op";
        grid-template-rows: 28px auto 40px;
        grid-gap: 10px;
        min-height: 80vh;
        max-height: 80vh;
        width: 600px; 
    }
    .spp-menu-title{
        grid-area: title;
        background: url(images/colorImagination/bg_topnav.gif) repeat-x #000000;
        border-left: black;
        border-right: black;
        text-align: center;
        font-weight: bold;
        height: 100%;
        color: white;
        padding-top: 5px;
        margin: 0;
    }
    
    .spp-menu-main{
        grid-area: main;
        height: 70vh;
        overflow-y: scroll;
    }
    
    /*.spp-menu-op-zone{*/
    /*    grid-area: op;*/
    /*    display: grid;*/
    /*    grid-template-columns: 50% 50%;*/
    /*}*/
    
    .spp-hide{
        display: none;
    }
    
    button.spp-accordion{
        width: 100%;
        border: none;
        outline: none;
        background-color: whitesmoke;
        text-align: left;
        padding: 10px 10px;
        font-size: 14px;
        font-weight: bold;
        color: #444;
        cursor: pointer;
        transition: background-color 0.2s linear;
    }
    
    button.spp-accordion:after{
        content:'+';
        font-size: 14px;
        float: right;
    }
    
    button.spp-accordion.spp-accordion-is-open:after{
        content: '-';
    }
    
    button.spp-accordion:hover, button.spp-accordion.spp-accordion-is-open{
        background-color: #ddd;
    }
    
    .spp-accordion-content{
        background: #eeeeee;
        border-left: 1px solid whitesmoke;
        border-right: 1px solid whitesmoke;
        padding: 0 20px;
        margin-bottom: 3px;
        max-height: 0;
        overflow: hidden;
        /*transition: all 0.2s ease-in-out;*/
        font-size: 14px;
    }
    
    .spp-accordion-content.spp-accordion-is-open{
        max-height: fit-content;
        /*transition: all 0.2s ease-in-out;*/
    }
    
    .spp-sticky{
        position: sticky;
        top: 0;
    }
    
    .spp-menu-accordion-op{
        display: grid;
        grid-template-columns: repeat(5,1fr);
        justify-content: end;
        align-content: center;
    }
    .spp-menu-accordion-op a{
        padding: 5px;
        margin-left: 20px;
        font-size: 12px;
        cursor: pointer;
    }
    
    .spp-menu-checkbox{
        margin: 10px;
    }
    .spp-menu-checkbox input[type="checkbox"]{
        margin-bottom: 5px;
    }
    /*.spp-menu-op-zone{*/
    
    /*    display: flex;*/
    /*    justify-content: space-around;*/
    /*    align-content: center;*/
    /*}*/
    
    .spp-menu-op-zone button{
        display: block;
        width: 100%;
        height: 100%;
        background: black;
        color: white;
        border: 1px solid black;
        font-size: 16px;
        font-family: 宋体,"sans-serif";
        font-weight: bold;
        cursor: pointer;
        
    }
    
    #spp-menu-close{
       background:linear-gradient(to bottom, #3a3a3a,#000000);
    }
     
    .spp-menu-description{
        padding-left: 20px;
        font-size: 12px;
    }
    
    .spp-danger-content{
        padding: 0;
    }
    
    .spp-btn-danger{
        display: block;
        background: crimson;
        color: whitesmoke;
        width: 100%;
        height: 40px;
        border: none;
        outline: none;
        cursor: pointer;
    }
    .spp-menu-sub-item{
        padding-left: 20px;
    }
        .spp-loading-animation{
            width:150px;
            margin:50px auto;
            text-align: center;
        }
        .spp-loading-animation >div{
            width: 18px;
            height: 18px;
            border-radius: 100%;
            display:inline-block;
            background-color: #af0909;
            -webkit-animation: dot 1.4s infinite ease-in-out;
            animation: dot 1.4s infinite ease-in-out;
            -webkit-animation-fill-mode: both;
            animation-fill-mode: both;
        }
        .spp-loading-animation .dot1{
            -webkit-animation-delay: -0.30s;
            animation-delay: -0.30s;
        }
        .spp-loading-animation .dot2{
            -webkit-animation-delay: -0.15s;
            animation-delay: -0.15s;
        }
        @-webkit-keyframes dot {
            0%, 80%, 100% {-webkit-transform: scale(0.0) }
            40% { -webkit-transform: scale(1.0) }
        }
        @keyframes dot {
            0%, 80%, 100% {-webkit-transform: scale(0.0) }
            40% { -webkit-transform: scale(1.0) }
        }
    </style>`.replace(/<\/?style>/gm, ""));
    GMK.addStyle(GMK.getResourceText("TOASTIFY_CSS"));

    console.log(`=======================================
            Soul++ 已经启动
=======================================`);
    // 给所有图片增加懒加载,以及处理图片隐藏
    MutationObserverProcess();

    // toast("Soul++ 已启动,可以在论坛导航栏进行设置", ToastType.WARNING, 5000);
    // toast("Soul++ 已启动,可以在论坛导航栏进行设置", ToastType.INFO, 5000);
    // toast("Soul++ 已启动,可以在论坛导航栏进行设置", ToastType.SUCCESS, 5000);
    // toast("Soul++ 已启动,可以在论坛导航栏进行设置", ToastType.DANGER, 5000);

    document.addEventListener("readystatechange", evt => {
        if (!(document.readyState === "interactive")) return;
        createSettingMenu();
        backToTop();

        if (document.location.href.includes("/read.php")) {
            postAddAnchorAttribute(document, page, tid);
            if (GMK.getValue("hoistingResourcePost")) {
                hoistingResourcePost();
            }
            if (GMK.getValue("linkToReplyAndQuote")) {
                linkToReplyAndQuote();
            }
        }
        if (document.location.href.includes("/thread.php")) {
            threadAddAnchorAttribute(document, page, fid);
            if (GMK.getValue("hideForumRules")) {
                document.cookie = "deploy=%09thread%09%0A;"
                document.querySelector("#cate_thread").style.display = "none";
            } else {
                document.cookie = "deploy=;"
                document.querySelector("#cate_thread").style.display = null;
            }
        }

        if (GMK.getValue("buyRefresh_free") && document.location.href.includes("/read.php")) {
            buyRefresh_free();
        }
        if (GMK.getValue("dynamicLoadingThreads") && (document.location.href.includes("/thread.php"))) {
            dynamicLoadingNextPage(PageType.THREADS_PAGE);
        }
        if (GMK.getValue("dynamicLoadingPicWall") && document.location.href.includes("/thread_new.php")) {
            dynamicLoadingNextPage(PageType.PIC_WALL_PAGE);
        }
        if (GMK.getValue("dynamicLoadingPosts") && document.location.href.includes("/read.php")) {
            dynamicLoadingNextPage(PageType.POSTS_PAGE);
        }
        if (GMK.getValue("dynamicLoadingSearchResult") && document.location.href.includes("/search.php")) {
            dynamicLoadingNextPage(PageType.SEARCH_RESULT);
        }
        if (GMK.getValue("blockAdforumSearchResult") && document.location.href.includes("/search.php")) {
            blockAdforumSearchResult();
        }
        if (GMK.getValue("automaticTaskCollection")) {
            automaticTaskCollection();
        }
        if (GMK.getValue("highlightViewedThread")) {
            highlightViewedThread();
        }
        if (GMK.getValue("replaceAllDomainToTheSame")) {
            replaceAllDomainToTheSame();
        }
        if (GMK.getValue("markPlusPlus")) {
            mark();
        }

    });


})();