98搜索增强

在搜索页进行搜索结果过滤、自动翻页、帖子预览,划词搜索

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==UserScript==
// @name         98搜索增强
// @version      1.2.4
// @description  在搜索页进行搜索结果过滤、自动翻页、帖子预览,划词搜索
// @author       etai2019
// @license      GPL-3.0 License
// @match        https://*.sehuatang.net/*
// @match        https://*.sehuatang.org/*
// @match        https://*.30fjp.com/*
// @match        https://*.18stm.cn/*
// @match        https://*.jq2t4.com/*
// @match        https://*.xo6c5.com/search.php?*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_registerMenuCommand
// @run-at       document-end
// @require      https://update.greasyfork.org/scripts/447533/1214813/findAndReplaceDOMText%20v%20046.js
// @namespace    etai2019
// ==/UserScript==

const clearCfg = false,
      clearCfgDebug = false;

// 初始化参数
if (clearCfgDebug || (clearCfg && GM_getValue('version') != GM_info.script.version) ){
    for (let i of GM_listValues()) {
        GM_deleteValue(i)
    }

    GM_setValue('version', GM_info.script.version)
}

// 初始化 setting
var default_settings = {
    "AutoScroll": {
        "Description": "自动翻页",
        "Enable": true,
    },
    "Includes": {
        "Description": "只看关键词",
        "Keywords": ["综合讨论区","高清中文字幕"],
    },
    "Excludes": {
        "Description": "排除关键词",
        "Keywords": ["资源出售","求片问答","内容隐藏","搬运","SHA1"],
    },
    "Banned": {
        "Description": "关键词黑名单,作用相当于排除关键词,但不会出现在搜索结果的可选框里",
        "Keywords": [],
    },
    "SelectSearch": {
        "Description": "划词搜索",
        "Enable": true,
    },
    "PreviewImage" : {
        "Enable": false,
        "Width": "90vw",
        "Height": "50vh",
    },
    "ScaleImageByWheel": {
        "Description": "滑鼠标缩放",
        "Unit": 0.3,
        "Enable": true
    },
    "Highlight": {
        "Description": "高亮关键词",
        "Keywords": "明里",
        "Enable": false,
    }
};
const settings = Object.assign({}, default_settings, GM_getValue("_98settings") || {});

// 初始化 脚本内部setting
var default_settings2 = {
    "ExcludedValues": [],
    "IncludedValues": [],
}
const settings2 = Object.assign({}, default_settings2, GM_getValue("_98settings2") || {});

// 添加样式
GM_addStyle(`
    .highlight {
        font-size: 1.2em;
        font-weight: bolder;
        background-color: yellow;
    }
    .slst {
        width: 1200px;
        margin-left: 30px;
    }
    .s2-sav-menu{
        font-family: Microsoft YaHei,sans-serif;
        position:fixed;
        display: block;
        text-align: left;
        color: #000;
        background:rgba(255,255,255,.8);
        backdrop-filter: blur(5px);
        border-radius: 4px;
        padding:6px 12px 10px 9px;
        /* margin-top: -2px; */
        z-index: 99999;
        font-size: 14px;
        max-width: 600px;
        box-shadow: 4px 4px 12px #ccc, -1px -1px 5px #eee;
        border-top: 2px solid #fff;
        border-left: 2px solid #fff;
        transform:scale(1);
        transition:0.2s;
        transition-timing-function: ease-out;
        animation: savOpenAnim 0.15s;
    }
    .savlink{
        margin: 4px 4px 4px 4px;
        border-radius: 4px;
        padding: 3px 5px;
        background: #fff;
        display: inline-block;
        transition: 0.2s;
        transition-timing-function: ease-out;
        box-shadow: -2px -2px 4px rgb(240 240 240), 2px 2px 4px rgb(70 70 70 / 50%);
        cursor: pointer;
        user-select: none;
    }
    .savlink:not(.RPdisabled):hover{
        background: aliceblue;
        box-shadow: -2px -2px 6px rgb(255 255 255 / 50%), 1px 1px 2px rgb(70 70 70 / 50%), inset -2px -2px 6px rgb(255 255 255 / 50%),inset 2px 2px 6px rgb(100 100 100 / 50%);
    }
    .s2-sav-menu .savlink a{
        color:#459df5;
        text-decoration:none;
        transition:0.4s;
    }
    .s2-sav-menu .savlink:not(.RPdisabled):hover a {
        color: #039cff;
        text-shadow: 0 0 #7cfb80;
    }

    .settings-container {
        font-family: Arial;
        font-size: 16px;
        color: #333;
        border: 1px solid #ccc;
        padding: 2px;
        background-color: #fff;
        display: flex;
        justify-content: space-between;
    }

    .checkbox-group {
        display: flex;
        gap:.5em;
        font-family: Arial;
        font-size: 16px;
    }

    .checkbox-group label {
        display: block;
    }

    .like-popup {
        width: 30%;
        position: fixed;
        top: 30%;
        left: 50%;
        transform: translate(-50%, -50%);
        z-index: 999;
        background-color: #ddd;
        border: 1px solid;
        padding: 5px;
    }

    .like-popup-content{
        font-size: 2em;
        margin-bottom: 1.5em;
    }

    .like-popup h5{
        font-size: 1.5em;
    }

    .preview-img {
        max-width: 49%;
        max-height: 100%;
        border: 1px solid;
        position: relative;
    }
`);

(function() {
    'use strict';

    // 设置菜单
    GM_registerMenuCommand("设置", function() {
        var editbox = document.createElement("div");
        editbox.id = "sav-editCodeBox";
        editbox.style.cssText = "position:fixed;" +
            "z-index:99999;" +
            "top:50%;left:50%;" +
            "transform:translate(-50%,-50%);" +
            "background:#ccc;" +
            "border-radius:4px;" +
            "padding:10px 20px;" ;
        editbox.innerHTML = " "+
            "<textarea wrap='off' cols='66' rows='20' style='overflow:auto;border-radius:4px;'>" + JSON.stringify(settings, false, 4) + "</textarea>" +
            "<br>" +
            "<button id='savDebug'>还原默认设置</button> &nbsp;&nbsp;&nbsp;" +
            "<button id='savEditBoxCloase' >关闭</button> &nbsp;&nbsp;&nbsp;" +
            "<button id='savEditBoxSave' >保存</button>" +
            "";

        // 还原默认设置
        editbox.querySelector("#savDebug").addEventListener("click", function() {
            document.querySelector("#sav-editCodeBox textarea").value = JSON.stringify(default_settings, false, 4);
        })

        // 关闭设置
        editbox.querySelector("#savEditBoxCloase").addEventListener("click", function() {
            editbox.parentNode.removeChild(editbox);
        });

        // 保存设置
        editbox.querySelector("#savEditBoxSave").addEventListener("click", function() {
            var codevalue = document.querySelector("#sav-editCodeBox textarea").value;
            var new_settings;
            try {
                new_settings = JSON.parse(codevalue);
            } catch(err){
                alert("保存失败,请按照下方提示修改后重新保存\n"+err);
                return;
            }

            GM_setValue("_98settings", new_settings);
            editbox.parentNode.removeChild(editbox);
            setTimeout(function(){
                window.location.reload();
            }, 100);
        })

        document.body.appendChild(editbox);
    })

    // 高亮关键词
    if (settings.Highlight.Enable) {
        HighlightKeywords();
    }


    // 划词搜索
    if (settings.SelectSearch.Enable) {
        document.onmouseup = SelectSearch;
    }

    // 搜索增强
    var curSite = {}
    const isSearchResultPage = window.location.href.includes('search.php');
    if (isSearchResultPage) {
        SetupNewSearch();
    }

    // 函数区域

    // 搜索增强
    function SetupNewSearch() {
        // 自动翻页
        if (settings.AutoScroll.Enable) {
            curSite = {
                SiteTypeID: 641,
                pageurl: "",
                pager: {
                    forceHTTPS: true,
                    interval: 500,
                    nextL: "a.nxt:not([href^=\"javascript\"]) ,a.next:not([href^=\"javascript\"])",
                    pageE: "#threadlist > ul",
                    replaceE: ".pg, .pages",
                    scrollD: 2000,
                    type: 1
                },
                pausePage: false,
                pageNum: { now: 1, _now: 1 },
            }

            RegisterAutoScroll();

            // 分页放在最上面
            const pgDiv = document.querySelector('.pgs');
            if (pgDiv != null) {
                document.querySelector('.sttl').insertAdjacentElement('afterend', pgDiv);
            }
        }


        // 推广弹窗
        const likePopup = document.createElement('div');
        likePopup.className = 'like-popup';
        likePopup.style.setProperty('display', 'none')
        document.body.appendChild(likePopup);
        likePopup.innerHTML = `
            <div class='like-popup-content'>
                <p>好用请给作者评分点赞吧~</p>
                <p>(版本大更新,弹窗一次,望理解)</p>
            </div>
            <h5><a href="forum.php?mod=viewthread&tid=1508541" target="_blank">好的,打开帖子</a></h5>
            <h5><a href="javascript:document.querySelector('.like-popup').style.setProperty('display', 'none')">关闭</a></h5>
        `;

        // 创建多选框组
        const exclusionCheckboxGroup = document.createElement('div');
        exclusionCheckboxGroup.className = 'checkbox-group';
        exclusionCheckboxGroup.appendChild(ele('<span>排除</span>'));
        for (const keyword of settings.Excludes.Keywords) {
            exclusionCheckboxGroup.appendChild(ele(`<label><input type="checkbox" class="exclusion" value="${keyword}"/>${keyword}</label>`));
        }

        const inclusionCheckboxGroup = document.createElement('div');
        inclusionCheckboxGroup.className = 'checkbox-group';
        inclusionCheckboxGroup.appendChild(ele('<span>只看</span>'));
        for (const keyword of settings.Includes.Keywords) {
            inclusionCheckboxGroup.appendChild(ele(`<label><input type="checkbox" value="${keyword}"/>${keyword}</label>`));
        }

        // 当多选框选项更改时,将所选值保存到脚本中
        exclusionCheckboxGroup.querySelectorAll('input[type="checkbox"]').forEach((checkbox) => {
            checkbox.addEventListener('change', (event) => {
                const excludedValues = Array.from(exclusionCheckboxGroup.querySelectorAll('input[type="checkbox"]:checked')).map((checked) => checked.value);
                settings2.ExcludedValues = excludedValues;
                SaveSettings2();

                // 移除搜索结果
                RemoveSearchResutls();
            });
        });

        inclusionCheckboxGroup.querySelectorAll('input[type="checkbox"]').forEach((checkbox) => {
            checkbox.addEventListener('change', (event) => {
                const includedValues = Array.from(inclusionCheckboxGroup.querySelectorAll('input[type="checkbox"]:checked')).map((checked) => checked.value);
                settings2.IncludedValues = includedValues;
                SaveSettings2();

                // 移除搜索结果
                RemoveSearchResutls();
            });
        });

        // 读取所选值
        const excludedValues = settings2.ExcludedValues;
        const includedValues = settings2.IncludedValues;

        // 恢复之前的选择
        excludedValues.forEach((selectedValue) => {
            const checkboxElement = exclusionCheckboxGroup.querySelector(`input[value="${selectedValue}"]`);
            if (checkboxElement) {
                checkboxElement.checked = true;
            }
        });

        includedValues.forEach((selectedValue) => {
            const checkboxElement = inclusionCheckboxGroup.querySelector(`input[value="${selectedValue}"]`);
            if (checkboxElement) {
                checkboxElement.checked = true;
            }
        });

        // 超级模式快捷方式
        const turboModeSwitch = ele(`<div><label><input type="checkbox" value="超级模式"/>超级模式</label></div>`);
        turboModeSwitch.querySelector(`input`).checked = settings.PreviewImage.Enable;
        turboModeSwitch.querySelector(`input`).addEventListener('change', (event) => {
            settings.PreviewImage.Enable = !settings.PreviewImage.Enable;
            GM_setValue("_98settings", settings);
            setTimeout(function(){
                window.location.reload();
            }, 100);
        });

        // 添加页面元素
        const searchForm = document.querySelector('.searchform');
        const settingsContainer = document.createElement('div');
        settingsContainer.className = 'settings-container';
        searchForm.appendChild(settingsContainer);

        // 将多选框组添加到页面中
        const keywordSettings = document.createElement('div');
        keywordSettings.appendChild(exclusionCheckboxGroup);
        keywordSettings.appendChild(inclusionCheckboxGroup);
        settingsContainer.appendChild(keywordSettings);

        // 将超级模式快捷方式添加到页面中
        settingsContainer.appendChild(turboModeSwitch)

        // 初始时移除搜索结果
        RemoveSearchResutls();
    }

    // 高亮关键词
    function HighlightKeywords() {
        const oRegex = new RegExp(`${settings.Highlight.Keywords}`, "gi");
        findAndReplaceDOMText(document.body, {
            find: oRegex,
            wrap: 'avem',
            wrapClass: 'highlight'
        });
        // 创建观察者对象
        var observerConfig = {childList: true, characterData: true ,subtree:true,};
        var observer = new window.MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                if(mutation.target.innerText.length < 5) {
                    return;
                }

                if(IsContentDom(mutation.target)) {
                    return;
                }

                if (mutation.target.innerText?.search(oRegex) <= -1) {
                    return;
                }

                observer.disconnect();  // 关闭对 dom 的监听

                console.log(mutation.target);
                // findAndReplaceDOMText(document.body, {
                //     find: new RegExp(`${settings.Highlight.Keywords}`, "gi"),
                //     wrap: 'avem',
                //     wrapClass: 'highlight'
                // });

                observer.observe(document.body, observerConfig);   // 开启对 dom 的监听
            })
        });

        // observer.observe(document.body, observerConfig)
    }

    // 检查是否为包含正文的Dom
    function IsContentDom(startDom){
        const RE_Exclude_className = /(?<!file)name|auth|user|(?<!home)code|^pstatus$|(?<!_tl_|ql-)editor|time|sav-id|sidebar|menu|TbwUpd/gi;

        if(startDom.classList && startDom.classList.length && startDom.className.match(RE_Exclude_className)){
            return true;
        }
        if(startDom.parentElement && "body" !== startDom.parentElement.nodeName){
            return IsContentDom(startDom.parentElement)
        } else {
            return false
        }
    }

    // 图片预览弹窗
    function DisplayPreviewBox(url) {
        GM_xmlhttpRequest({
            method: "GET",
            url: url,
            onload: function(response) {
                var parser = new DOMParser();
                var htmlDoc = parser.parseFromString(response.responseText, "text/html");
                var images = Array.from(htmlDoc.querySelectorAll(".zoom")).filter(image => !(image.getAttribute("zoomfile") || image.src || image.getAttribute('file')).includes('static'));
                if (!images || images.length == 0) return;
                if (images.length > 2) images.length = 2;

                var box = document.createElement("div");
                document.body.appendChild(box);
                box.classList.add("tk_preview");
                box.style.cssText = `
                    width: 99%;
                    border: 1px solid;
                    background-color: #ddd;
                    position: fixed;
                    top: 50%;
                    transform: translate(0%, -50%);
                    zIndex = 99999;
                `;

                box.addEventListener("click", function() {
                    document.body.removeChild(box);
                });

                for (let image of images) {
                    var preview = document.createElement("img");
                    box.appendChild(preview);

                    var src = image.getAttribute("zoomfile") || image.src || image.getAttribute('file');
                    preview.src = src;
                    preview.className = "preview-img"

                    if (settings.ScaleImageByWheel.Enable) {
                        preview.addEventListener("wheel",ImgScaleWheel);
                    }
                }
            }
        });
    }

    // 在搜索结果中直接加载图片
    function LoadPreviewsAndLinks(item) {
        if (!settings.PreviewImage.Enable) return;

        let linkTag = item.querySelector('a');

        GM_xmlhttpRequest({
            method: "GET",
            url: linkTag.href,
            onload: function(response) {
                const debug = false;
                var parser = new DOMParser();
                var htmlDoc = parser.parseFromString(response.responseText, "text/html");
                var images = Array.from(htmlDoc.querySelectorAll(".zoom")).filter(image => !(image.getAttribute("zoomfile") || image.src || image.getAttribute('file')).includes('static'));
                if  (!images || images.length == 0) return;
                if (images.length > 2) images.length = 2;

                let div = document.createElement('div');
                item.appendChild(div);
                div.style.width = settings.PreviewImage.Width;
                div.style.height = settings.PreviewImage.Height;
                div.classList.add('tk_preview_div');

                for (let image of images) {
                    var preview = document.createElement("img");
                    div.appendChild(preview);

                    const src = image.getAttribute("zoomfile") || image.src || image.getAttribute('file');
                    preview.src = src;
                    preview.className = "preview-img"

                    if (settings.ScaleImageByWheel.Enable) {
                        preview.addEventListener("wheel",ImgScaleWheel);
                        preview.addEventListener("click",ImgScaleClick);
                    }
                }

                if (debug) {
                    const code = htmlDoc.querySelector('.blockcode');
                    if (code != null) {
                        const magnet = code.querySelector('li').innerText
                        const magnetA = document.createElement('a');
                        magnetA.setAttribute('href', magnet);
                        magnetA.innerText = '🧲';
                        linkTag.parentNode.appendChild(magnetA);
                    }

                    const bt = htmlDoc.querySelector('.attnm');
                    if (bt != null) {
                        const btlink = bt.querySelector('a').href;
                        const btA = document.createElement('a');
                        btA.setAttribute('target', '_blank');
                        btA.setAttribute('href', btlink);
                        btA.innerText = '💾';
                        linkTag.parentNode.appendChild(btA);
                    }
                }
            }
        });
    }

    // 图片缩放
    const likePopupLifetimePages = 100;
    function ImgScaleWheel(e){
        if (!e.target.ImageScale) e.target.ImageScale = 1.0;
        if (!window.ImgIndex) window.ImgIndex = 100;

        var imageScale = e.target.ImageScale;
        if(e.target.tagName == "IMG"){
            e.target.style.zIndex = window.ImgIndex++;
            if(e.wheelDelta > 0){
                imageScale += settings.ScaleImageByWheel.Unit;

            // 推广弹窗
            const lifetimePages = (GM_getValue("lifetime_pages") ?? 0) + 1;
            const likePopupDisplayTimes = GM_getValue("likePopupDisplayTimes") ?? 0;
            if (likePopupDisplayTimes == 0 && lifetimePages >= likePopupLifetimePages) {
                document.querySelector('.like-popup').style.setProperty('display', 'block');
                GM_setValue("likePopupDisplayTimes", likePopupDisplayTimes + 1);
            }

            GM_setValue("lifetime_pages", lifetimePages);
            console.log("lifetime_pages:", lifetimePages);
            } else if(e.wheelDelta < 0){
                if(imageScale > 1){
                    imageScale -= settings.ScaleImageByWheel.Unit;
                }

                // 最小减至1
                if(imageScale <= 1){
                    imageScale = 1.0;
                    e.target.style = "";
                };
            }

            e.target.style.transform = "scale(" + imageScale +")";
        }

        e.target.ImageScale = imageScale;

        e.preventDefault();

        return false;
    }

    // 取消图片缩放
    function ImgScaleClick(e) {
        if(e.target.tagName == "IMG"){
            if (e.target.ImageScale) {
                e.target.ImageScale = 1.0;
            }

            e.target.style = "";
        }
    }

    // 尝试显示图片预览
    function DecorateResultItem(item) {
        if (item.styleDone) return;

        item.styleDone = true;

        // 删除搜索结果文本
        let p = item.querySelectorAll('p')[1]
        p.style.display = 'none';

        // 超级模式直接显示预览
        if (settings.PreviewImage.Enable) {
            LoadPreviewsAndLinks(item);
            return;
        }

        // 添加预览图片按钮
        var button = document.createElement("button");
        button.classList.add('tk-preview-button');
        button.style.marginRight = "10px";
        button.innerHTML = "预览图片";

        // 添加按钮点击事件
        let linkTag = item.querySelector('a');
        button.onclick = function() {
            // 获取链接的href属性
            DisplayPreviewBox(linkTag.href);
        };

        linkTag.parentNode.insertBefore(button, linkTag);
    }

    // 筛选搜索结果
    function RemoveSearchResutls() {
        // 读取所选值
        const excludedValues = settings2.ExcludedValues.concat(settings.Banned.Keywords);
        const includedValues = settings2.IncludedValues;

        // 获取搜索结果
        var items = document.querySelectorAll('.slst li');

        // 处理每一个搜索结果
        for (let item of items){
            const titleText = item.querySelector('a').innerText.toUpperCase();
            const forum = item.querySelector('.xi1').innerText;
            const preview = item.querySelector('.xg1').nextElementSibling.innerText;
            const resultText = titleText + forum + preview;

            // 只显示关键词
            let shouldShow = includedValues.length == 0 || includedValues.some(word => resultText.includes(word));

            if (shouldShow) {
                item.style.removeProperty('display');
            } else {
                item.style.display = "none";
                continue;
            }

            // 排除关键词
            let shouldHide = excludedValues.some(word => resultText.includes(word));
            if (shouldHide) {
                item.style.display = "none";
            } else {
                item.style.removeProperty('display');
                DecorateResultItem(item)
            }
        }
    };

    // 监听滚动条事件
    function WindowScroll(fn) {
        var beforeScrollTop = document.documentElement.scrollTop || document.body.scrollTop
        fn = fn || function () {};
        // 延时 1 秒执行,避免刚载入到页面就触发翻页事件
        setTimeout(function () {
            // 避免网页内容太少,高度撑不起来,不显示滚动条而无法触发翻页事件
            let scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop,
                scrollHeight = window.innerHeight || document.documentElement.clientHeight
            if (scrollTop === 0 && document.documentElement.scrollHeight === scrollHeight) {
                const style = `html, body {min-height: ${document.documentElement.scrollHeight+10}px;}`;

                console.log('网页内容太少,高度撑不起来!!', style);
                document.documentElement.appendChild(document.createElement('style')).textContent = style;
            }

            window.addEventListener('scroll', function (e) {
                var afterScrollTop = document.documentElement.scrollTop || document.body.scrollTop,
                    delta = afterScrollTop - beforeScrollTop;
                if (delta == 0) return false;
                fn(delta > 0 ? 'down' : 'up', e);
                beforeScrollTop = afterScrollTop;
            }, false);
        }, 1000)
    }

    // 将无缝翻页注册进监听器
    function RegisterAutoScroll() {
        if (curSite.pager.scrollD === undefined) curSite.pager.scrollD = 2000; // 默认翻页触发线 2000
        if (curSite.pager.interval === undefined) curSite.pager.interval = 500; // 默认间隔时间 500ms
        curSite.pageUrl = ''; // 下一页URL
        WindowScroll(function (direction, e) {
            // 下滑 且 未暂停翻页 且 SiteTypeID > 0 时,才准备翻页
            if (direction != 'down' || curSite.pausePage || curSite.SiteTypeID == 0) return

            let scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
            let scrollHeight = window.innerHeight || document.documentElement.clientHeight
            let scrollD = curSite.pager.scrollD;

            if (document.documentElement.scrollHeight <= scrollHeight + scrollTop + scrollD) {
                intervalPause();
                GoToNextPage(LoadNextPage);
            }
        });

        function intervalPause() {
            if (curSite.pager.interval) {
                curSite.pausePage = true
                setTimeout(function(){curSite.pausePage = false;}, curSite.pager.interval)
            }
        }
    }

    // 检查 URL
    function GoToNextPage(func) {
        if (GetNextPageLink()) {
            func(curSite.pageUrl);
        }
    }

    // 通用型获取下一页地址(从 元素 中获取页码)
    function GetNextPageLink(css) {
        if (!css) css = curSite.pager.nextL;
        let next = QueryElement(css);
        if (next && next.nodeType === 1 && next.href && next.href.slice(0,4) === 'http' && next.getAttribute('href').slice(0,1) !== '#') {
            if (next.href != curSite.pageUrl) {
                if (curSite.pager.forceHTTPS && location.protocol === 'https:') {
                    if (next.href.replace(/^http:/,'https:') === curSite.pageUrl) {
                        return false
                    }
                    curSite.pageUrl = next.href.replace(/^http:/,'https:');
                } else {
                    curSite.pageUrl = next.href;
                }
            } else {
                return false
            }

            return true
        }
        return false
    }

    // 查询单个元素
    function QueryElement(selector, contextNode = undefined, doc = document) {
        if (!selector) return;

        contextNode = contextNode || doc;
        return contextNode.querySelector(selector);
    }

    function CreateDocumentByString(e) {
        if (e) {
            if ('HTML' !== document.documentElement.nodeName) return (new DOMParser).parseFromString(e, 'application/xhtml+xml');
            var t;
            try { t = (new DOMParser).parseFromString(e, 'text/html');} catch (e) {}
            if (t) return t;
            if (document.implementation.createHTMLDocument) {
                t = document.implementation.createHTMLDocument('ADocument');
            } else {
                try {((t = document.cloneNode(!1)).appendChild(t.importNode(document.documentElement, !1)), t.documentElement.appendChild(t.createElement('head')), t.documentElement.appendChild(t.createElement('body')));} catch (e) {}
            }
            if (t) {
                var r = document.createRange(),
                    n = r.createContextualFragment(e);
                r.selectNodeContents(document.body);
                t.body.appendChild(n);
                for (var a, o = { TITLE: !0, META: !0, LINK: !0, STYLE: !0, BASE: !0}, i = t.body, s = i.childNodes, c = s.length - 1; c >= 0; c--) o[(a = s[c]).nodeName] && i.removeChild(a);
                return t;
            }
        } else console.error('没有找到要转成 DOM 的字符串', e);
    }

    // 读取下一页内容
    function LoadNextPage(url) {
        // 读取下一页
        GM_xmlhttpRequest({
            url: url,
            method: 'GET',
            overrideMimeType: 'text/html; charset=' + (document.characterSet||document.charset||document.inputEncoding),
            headers: {
                'Referer': (curSite.noReferer === true) ? '':location.href,
                'User-Agent': navigator.userAgent,
                'Accept': 'text/html,application/xhtml+xml,application/xml'
            },
            timeout: 10000,
            onload: function (response) {
                try {
                    // console.log('URL:' + url, '最终 URL:' + response.finalUrl, '返回内容:' + response.responseText)
                    console.log('成功载入下一页:', url, 'Response URL:', response.finalUrl)

                    ProcessNewPageElems(CreateDocumentByString(response.responseText));

                    // 筛选搜索结果
                    RemoveSearchResutls();

                    // 微移一个像素,这样可以直接用滚轮载入下一页
                    document.documentElement.scrollTop -= 0.1;
                } catch (e) {
                    console.error('[自动无缝翻页] - 处理获取到的下一页内容时出现问题,请检查!', e, response.responseText);
                }
            },
            onerror: function (response) {
                console.log('❌ 获取下一页失败...URL:' + url, response)
            },
            ontimeout: function (response) {
                setTimeout(function(){curSite.pageUrl = '';}, 3000)
                console.log('❌ 获取下一页超时,可 3 秒后再次滚动网页重试(或尝试刷新网页)...URL:' + url, response)
            }
        });
    }

    // 获取最后一个元素,排除 <script> <style> <link> 标签
    function GetLastElement(a) {
        if (a.length === 0) return
        let b = a.pop();
        if (b.tagName === 'SCRIPT' || b.tagName === 'STYLE' || b.tagName === 'LINK') {
            return GetLastElement(a);
        }
        return b
    }

    // 查询多个元素
    function QueryElementsAll(selector, contextNode = undefined, doc = document) {
        if (!selector) return [];
        contextNode = contextNode || doc;
        return [].slice.call(contextNode.querySelectorAll(selector));
    }

    // 替换元素
    function ReplaceNewPageElems(pageE, o = curSite.pager.replaceE, r = curSite.pager.replaceE) {
        let oE = QueryElementsAll(o),
            rE = QueryElementsAll(r, pageE, pageE);
        if (oE.length != 0 && rE.length != 0 && oE.length === rE.length) {
            for (let i = 0; i < oE.length; i++) {
                oE[i].outerHTML = rE[i].outerHTML;
            }
            return true
        } else {console.log(pageE,oE,rE)}
        return false
    }

    // XHR 后处理结果,插入、替换元素等(适用于翻页类型 1/3/6)
    function ProcessNewPageElems(response) {
        if (!curSite.pager.insertP) {curSite.pager.insertP = [curSite.pager.pageE, 5]}
        let pageE = QueryElementsAll(curSite.pager.pageE, response, response), toE;
        if (curSite.pager.insertP[1] === 5) { // 插入 pageE 列表最后一个元素的后面
            toE = GetLastElement(QueryElementsAll(curSite.pager.insertP[0]));
        }

        if (pageE.length > 0 && toE) {

            // 插入位置
            let addTo = 'afterend';

            // 插入新页面元素
            if (curSite.pager.insertP[1] === 2 || curSite.pager.insertP[1] === 4 || curSite.pager.insertP[1] === 5) pageE.reverse(); // 插入到 [元素内头部]、[目标本身后面] 时,需要反转顺序
            pageE.forEach(function (one) {toE.insertAdjacentElement(addTo, one);});

            // 当前页码 + 1
            curSite.pageNum.now = curSite.pageNum._now + 1

            // 替换待替换元素
            if (curSite.pager.replaceE) ReplaceNewPageElems(response);

        } else { // 获取主体元素失败
            console.error('[自动无缝翻页] 获取主体元素失败...')
        }
    }

    // 划词搜索
    function SelectSearch(e) {
        if(e.button != 0) return;    // 排除非左键点击
        if(document.activeElement.tagName.toUpperCase() == "INPUT" || document.activeElement.tagName.toUpperCase() == "TEXTAREA") return;   // 排除inpu和textarea内的文本
        var selectText = window.getSelection().toString().trim();
        if (selectText.length < 2) {
            var odiv = document.querySelector(".s2-sav-menu");
            if (odiv != null) {
                odiv.parentNode.removeChild(odiv);
            }

            return;
        }

        if(document.querySelector(".s2-sav-menu")) return; //如果已经存在菜单, 退出

        var odiv = CreateSearchPopup(selectText);
        var divClientRect = odiv.getBoundingClientRect()
        var divWidth = divClientRect.right - divClientRect.left;
        odiv.style.left = e.pageX - divWidth/2 - 5 + "px";
        odiv.style.top = e.pageY - 5 + "px";
        odiv.style.position = "absolute";

        document.body.appendChild(odiv);
    }

    // 创建搜索菜单
    function CreateSearchPopup(selectText){
        console.log(selectText)
        let aPattern = `
            <avdivbutton>
                <avdiv class='savlink savsehuatang' data-avid=${selectText}> 搜索 </avdiv>
            </avdivbutton>
            `;

        Sehuatang_GetFormHash();

        var odiv = document.createElement("avdiv")
        odiv.classList.add("s2-sav-menu","idExistent");
        odiv.innerHTML = aPattern;

        odiv.addEventListener("click",SearchPopupClick)
        return odiv;
    }

    // 点击划词搜索
    function SearchPopupClick(e) {
        if(e.target.classList.contains("savsehuatang")){
            // 防止多次点击导致重复发送请求
            e.target.classList.remove("savsehuatang");
            SearchSehuatang(e.target.dataset.avid);
        }

        var odiv = document.querySelector(".s2-sav-menu");
        odiv.parentNode.removeChild(odiv);
    }

    // 获取色花堂的formhash
    function Sehuatang_GetFormHash(){
        let sehuatang_getTime = settings2.sehuatang_getTime;
        let nowTime = new Date().getTime();
        let sehuatangURL = `https://${window.location.host}`;

        const debug = false;
        // 不确定这个值会不会变动, 12小时获取一次
        if(!sehuatang_getTime || nowTime-sehuatang_getTime > 43200000 || settings2.sehuatang_url != sehuatangURL){
            GM_xmlhttpRequest({
                method:"get",
                url:sehuatangURL,
                onload:function(data){
                    // console.log(data);
                    var parser=new DOMParser();
                    let htmlDoc=parser.parseFromString(data.responseText, "text/html");
                    let odom = htmlDoc.querySelector('input[name="formhash"]');
                    // console.log(odom);
                    let formhash_value = odom.value;
                    // console.log(formhash_value);

                    settings2.sehuatang_formhash = formhash_value;
                    settings2.sehuatang_getTime = nowTime;
                    settings2.sehuatang_url = sehuatangURL;
                    SaveSettings2();
                }
            })
            if(debug){console.log(`重新获取色花堂的formhash`)};
        }else{
            if(debug){console.log(`没有重新获取色花堂的formhash`)}
        }
    }

    // 色花堂搜索
    function SearchSehuatang(avID){
        let formhash = settings2.sehuatang_formhash;
        let sehuatangURL = `https://${window.location.host}`;

        if (formhash) {
            GM_xmlhttpRequest({
                method: "post",
                url: sehuatangURL+"/search.php?mod=forum",
                data: `formhash=${formhash}&srchtxt=${avID}&searchsubmit=yes`,
                headers:  {
                    "Content-Type": "application/x-www-form-urlencoded",
                    "Origin":sehuatangURL,
                    "Referer":sehuatangURL
                },
                onload: function(data){
                    if(data.finalUrl){
                        window.open(data.finalUrl);
                    } else{
                        GM_setClipboard(avID)
                        window.open(`${sehuatangURL}/search.php`);
                    }
                },
                onerror : function(err){
                    console.log('SearchSehuatang error')
                    console.log(err)
                }
            });
        } else {
            GM_setClipboard(avID)
            window.open(`${sehuatangURL}/search.php`);
        }
    }

    // 保存设置
    function SaveSettings2() {
        GM_setValue("_98settings2", settings2);
    }

    // 创建元素
    function ele(html) {
        let temp = document.createElement('template');
        html = html.trim();
        temp.innerHTML = html;
        return temp.content.firstChild;
    }
})();